diff --git a/config/copy/etc/systemd/logind.conf.d/panama-power-key.conf b/config/copy/etc/systemd/logind.conf.d/panama-power-key.conf
new file mode 100644
index 0000000..6acc0c3
--- /dev/null
+++ b/config/copy/etc/systemd/logind.conf.d/panama-power-key.conf
@@ -0,0 +1,8 @@
+# A stray press of the power button should ask, not act. Fedora's logind
+# default is HandlePowerKey=poweroff -- an instant, unconfirmed shutdown.
+# Panama binds the key in the compositor to open its power menu instead
+# (keybinds.lua), the way GNOME turns the key into a dialog, so logind is
+# told to stand down. Holding the button still hard-cuts through firmware,
+# which remains the escape hatch this file cannot and should not remove.
+[Login]
+HandlePowerKey=ignore
diff --git a/config/dot/hypr/keybinds.lua b/config/dot/hypr/keybinds.lua
index 6e61cf9..c784cbb 100644
--- a/config/dot/hypr/keybinds.lua
+++ b/config/dot/hypr/keybinds.lua
@@ -34,6 +34,10 @@ local osd = function(action)
return "$HOME/.config/quickshell/scripts/panama-osd " .. action
end
+local lid = function(action)
+ return "$HOME/.config/quickshell/scripts/panama-lid " .. action
+end
+
local dictate = function(action)
return "$HOME/.config/quickshell/scripts/panama-dictate " .. action
end
@@ -458,6 +462,30 @@ bind("XF86AudioStop", hl.dsp.exec_cmd(osd("media stop")), { locked = true , desc
bind("XF86MonBrightnessUp", hl.dsp.exec_cmd(osd("brightness up 5")), { locked = true, repeating = true , description = "Brightness up" })
bind("XF86MonBrightnessDown", hl.dsp.exec_cmd(osd("brightness down 5")), { locked = true, repeating = true , description = "Brightness down" })
+-- Airplane mode. The Framework's F10 emits exactly this keysym, and for a
+-- while it emitted it into silence. panama-osd owns the toggle so the OSD
+-- can say which way it went.
+bind("XF86RFKill", hl.dsp.exec_cmd(osd("airplane toggle")), { locked = true, description = "Airplane mode" })
+
+-- F9 on the same row. GNOME shows a display-switching OSD here; until
+-- mirroring exists (DESKTOP-PARITY gap), the honest action is the page where
+-- displays are actually arranged.
+bind("XF86Display", hl.dsp.exec_cmd("qs ipc call settings page displays"), { description = "Display settings" })
+
+-- The power button. logind is told to ignore it (config/copy ships the
+-- drop-in) so a stray press is a question, not an instant poweroff -- the
+-- menu this opens is the question. Until the next boot after that drop-in
+-- lands, logind still acts on the key; this bind costs nothing extra then.
+bind("XF86PowerOff", hl.dsp.exec_cmd("qs ipc call powermenu toggle"), { locked = true, description = "Power menu" })
+
+-- The lid, as a switch rather than a key. Closing a docked lid turns the
+-- internal panel off so nothing renders inside a closed shell and no
+-- workspace strands on an invisible output; opening it turns the panel back
+-- on. panama-lid owns the decision -- undocked machines suspend via logind
+-- before this matters, and the guard's inhibitor handles staying awake.
+bind("switch:on:Lid Switch", hl.dsp.exec_cmd(lid("close")), { locked = true, description = "Lid closed" })
+bind("switch:off:Lid Switch", hl.dsp.exec_cmd(lid("open")), { locked = true, description = "Lid opened" })
+
category("Applications")
-- Hardware keys GNOME mapped that have obvious equivalents.
bind("XF86Tools", hl.dsp.exec_cmd(settings), { description = "Settings" })
diff --git a/config/dot/quickshell/config/PreferenceSchema.qml b/config/dot/quickshell/config/PreferenceSchema.qml
index 336cc36..aa12d50 100644
--- a/config/dot/quickshell/config/PreferenceSchema.qml
+++ b/config/dot/quickshell/config/PreferenceSchema.qml
@@ -125,6 +125,20 @@ Singleton {
label: "Urgent at",
detail: "Interrupt at this level, even during Do Not Disturb"
},
+ // What the desktop DOES at the critical threshold, beyond interrupting.
+ // Suspend by default, which is GNOME's behavior: sleep preserves the
+ // session at a level the firmware can hold for days, and the
+ // alternative -- a hard cut at 0% -- preserves nothing.
+ {
+ key: "batteryCriticalAction", type: "enum", def: "suspend", group: "battery",
+ label: "At the urgent level",
+ detail: "What happens when the battery reaches the urgent threshold while discharging",
+ options: [
+ { value: "suspend", label: "Suspend" },
+ { value: "nothing", label: "Only warn" }
+ ]
+ },
+
// Only offered where the firmware exposes a ceiling; the Power page
// hides the control entirely otherwise. 100 means charge to full.
{
diff --git a/config/dot/quickshell/modules/powermenu/PowerMenu.qml b/config/dot/quickshell/modules/powermenu/PowerMenu.qml
index fd3cac6..69945cb 100644
--- a/config/dot/quickshell/modules/powermenu/PowerMenu.qml
+++ b/config/dot/quickshell/modules/powermenu/PowerMenu.qml
@@ -5,6 +5,7 @@
// (see PowerButton); Lock and Suspend are one press because neither loses work.
import Quickshell
+import Quickshell.Io
import Quickshell.Wayland
import QtQuick
import qs.config
@@ -31,6 +32,23 @@ PanelWindow {
property int currentIndex: 0
+ // Hibernate appears only where it can deliver: logind answers "yes" only
+ // with a resume-capable swap configured, and a menu entry that fails
+ // silently is worse than none. Checked once per shell run -- swap does
+ // not come and go.
+ property bool canHibernate: false
+
+ Process {
+ id: hibernateProbe
+ command: ["busctl", "call", "org.freedesktop.login1",
+ "/org/freedesktop/login1", "org.freedesktop.login1.Manager",
+ "CanHibernate"]
+ running: true
+ stdout: StdioCollector {
+ onStreamFinished: win.canHibernate = this.text.includes('"yes"')
+ }
+ }
+
// A uwsm-managed session logs out by stopping the uwsm unit; a plain
// "Hyprland" session must ask the compositor to exit instead. The branch
// used to test merely that the uwsm BINARY existed -- but uwsm is always
@@ -41,7 +59,11 @@ PanelWindow {
// compositor unit, wayland-wm@*.service, so that is what decides.
readonly property string logoutScript: 'if systemctl --user list-units --no-legend --state=active "wayland-wm@*.service" 2>/dev/null | grep -q .; then exec uwsm stop; else exec hyprctl dispatch "hl.dsp.exit()"; fi'
- readonly property var entries: [
+ // The rows actually shown: an entry may declare `available: false` to
+ // withdraw itself (hibernate on a machine with no resume swap).
+ readonly property var entries: allEntries.filter(entry => entry.available !== false)
+
+ readonly property var allEntries: [
{
glyph: "",
label: "Lock",
@@ -60,6 +82,13 @@ PanelWindow {
destructive: false,
cmd: ["systemctl", "suspend"]
},
+ {
+ glyph: "",
+ label: "Hibernate",
+ destructive: false,
+ available: win.canHibernate,
+ cmd: ["systemctl", "hibernate"]
+ },
{
glyph: "",
label: "Restart",
diff --git a/config/dot/quickshell/modules/settings/DisplaysPage.qml b/config/dot/quickshell/modules/settings/DisplaysPage.qml
index f320710..5008202 100644
--- a/config/dot/quickshell/modules/settings/DisplaysPage.qml
+++ b/config/dot/quickshell/modules/settings/DisplaysPage.qml
@@ -12,6 +12,7 @@
import QtQuick
import qs.config
import qs.services
+import qs.modules.quicksettings
import qs.widgets
SettingsPage {
@@ -289,35 +290,21 @@ SettingsPage {
// writing it into settings.json would mean restoring a value the panel had
// already moved on from. So there is no schema key here and no SliderRow --
// the rows read and write the display directly.
+ // One brightness surface for both kinds this machine may have, shared
+ // with the quick-settings panel so the two can never disagree. This card
+ // was DDC/CI-only for a while, which on a laptop meant Settings showed
+ // an error about external-monitor brightness while the panel's backlight
+ // worked fine one panel over.
SettingsCard {
- visible: Brightness.available || Brightness.lastError !== ""
+ visible: pageBrightness.visible || Brightness.lastError !== ""
title: "Brightness"
- subtitle: Brightness.available
- ? "Sent to the monitor over DDC/CI, the same channel its buttons use."
+ subtitle: pageBrightness.visible
+ ? "The built-in panel through its backlight; external monitors over DDC/CI, the same channel their buttons use."
: Brightness.lastError
- Repeater {
- model: Brightness.displays
-
- SettingRow {
- id: brightnessRow
- required property var modelData
- required property int index
-
- label: Displays.monitorNamed(modelData.connector)?.description || modelData.connector
- detail: modelData.connector ? modelData.connector + " · " + modelData.value + "%"
- : modelData.value + "%"
- divider: brightnessRow.index < Brightness.displays.length - 1
- controlWidth: 190
-
- ValueSlider {
- anchors.verticalCenter: parent.verticalCenter
- anchors.right: parent.right
- width: parent.width
- value: brightnessRow.modelData.value / 100
- onMoved: v => Brightness.set(brightnessRow.modelData.bus, Math.round(v * 100))
- }
- }
+ BrightnessControl {
+ id: pageBrightness
+ width: parent.width
}
}
diff --git a/config/dot/quickshell/modules/settings/PowerPage.qml b/config/dot/quickshell/modules/settings/PowerPage.qml
index fb9e6e4..f174a26 100644
--- a/config/dot/quickshell/modules/settings/PowerPage.qml
+++ b/config/dot/quickshell/modules/settings/PowerPage.qml
@@ -53,8 +53,9 @@ SettingsPage {
// page rendered them -- search delivered people to this card and the
// controls were not here.
SliderRow { setting: "batteryLowPercent" }
- SliderRow {
- setting: "batteryCriticalPercent"
+ SliderRow { setting: "batteryCriticalPercent" }
+ ChoiceRow {
+ setting: "batteryCriticalAction"
divider: Battery.chargeLimitSupported
}
@@ -149,6 +150,15 @@ SettingsPage {
}
}
+ // Informational like the lid card above it: the behavior is a logind
+ // drop-in plus a compositor bind (see keybinds.lua), not a preference,
+ // and saying what a physical button does beats leaving it to be
+ // discovered by pressing it.
+ SettingsCard {
+ title: "The power button"
+ subtitle: "A press opens the power menu instead of powering off immediately. Holding it still forces power off through the firmware, as ever."
+ }
+
// Only shown when the numbers are actually contradictory, rather than as a
// permanent warning nobody reads.
SettingsCard {
diff --git a/config/dot/quickshell/scripts/panama-idle b/config/dot/quickshell/scripts/panama-idle
index 8aac71d..781c643 100755
--- a/config/dot/quickshell/scripts/panama-idle
+++ b/config/dot/quickshell/scripts/panama-idle
@@ -117,6 +117,25 @@ generate() {
printf ' inhibit_sleep = 2\n'
printf '}\n'
+ # On battery, dim before blanking -- GNOME's single largest idle
+ # battery saver, and the gentle warning that the screen is about to
+ # go. Two-thirds of the way to blank, floor of 30 seconds; -s saves
+ # the level and -r restores exactly it, so a person's brightness
+ # choice survives the round trip. Only where a real backlight exists:
+ # a desktop's DDC monitor is not worth waking over this.
+ if on_battery && (( blank_min > 0 )) \
+ && command -v brightnessctl >/dev/null 2>&1 \
+ && [[ -n "$(ls /sys/class/backlight 2>/dev/null)" ]]; then
+ dim_at=$(( blank_min * 60 * 2 / 3 ))
+ (( dim_at < 30 )) && dim_at=30
+ printf '\n# %s seconds -> dim, undone on activity.\n' "$dim_at"
+ printf 'listener {\n'
+ printf ' timeout = %s\n' "$dim_at"
+ printf ' on-timeout = brightnessctl -c backlight -s set 30%%\n'
+ printf ' on-resume = brightnessctl -c backlight -r\n'
+ printf '}\n'
+ fi
+
if (( blank_min > 0 )); then
printf '\n# %s minutes -> screen off.\n' "$blank_min"
printf 'listener {\n'
diff --git a/config/dot/quickshell/scripts/panama-lid b/config/dot/quickshell/scripts/panama-lid
index 8d8ca23..a1ef9b2 100755
--- a/config/dot/quickshell/scripts/panama-lid
+++ b/config/dot/quickshell/scripts/panama-lid
@@ -68,16 +68,64 @@ cmd_guard() {
sleep infinity
}
+# The internal panel's connector name, from the compositor: eDP, LVDS and
+# DSI are built in, everything else arrived through a cable -- the same rule
+# panama-hw and LidPolicy use.
+internal_connector() {
+ hyprctl -j monitors all 2>/dev/null \
+ | jq -r '[.[] | select(.name | test("^(eDP|LVDS|DSI)"; "i"))][0].name // empty'
+}
+
+# Bound to the lid switch by keybinds.lua. Closing a DOCKED lid turns the
+# internal panel off, so nothing renders inside a closed shell and no
+# workspace strands on an invisible output; an undocked lid does nothing here
+# because logind is already suspending the machine. Failure direction: if the
+# disable never runs, the panel stays lit inside the shell -- wasteful, not
+# harmful.
+cmd_close() {
+ "$HW" laptop || exit 0
+ "$HW" external-monitor || exit 0
+ local internal
+ internal="$(internal_connector)"
+ [[ -n "$internal" ]] || exit 0
+ hyprctl eval "hl.monitor({ output = \"$internal\", disable = true })" >/dev/null
+}
+
+# Re-enable on open, preferring what the person chose for this panel in
+# Settings (the same displays store monitors.lua reads at startup) and
+# falling back to the panel's preferred mode.
+cmd_open() {
+ "$HW" laptop || exit 0
+ local internal entry mode scale
+ internal="$(internal_connector)"
+ [[ -n "$internal" ]] || exit 0
+ entry="$(jq -c --arg name "$internal" '.displays[$name] // empty' \
+ "${XDG_CONFIG_HOME:-$HOME/.config}/panama/settings.json" 2>/dev/null)"
+ if [[ -n "$entry" ]]; then
+ mode="$(jq -r '.mode' <<<"$entry")"
+ scale="$(jq -r '.scale' <<<"$entry")"
+ hyprctl eval "hl.monitor({ output = \"$internal\", mode = \"$mode\", position = \"auto\", scale = $scale })" >/dev/null
+ else
+ hyprctl eval "hl.monitor({ output = \"$internal\", mode = \"preferred\", position = \"auto\", scale = \"auto\" })" >/dev/null
+ fi
+}
+
case "${1:-status}" in
status) cmd_status ;;
guard) cmd_guard ;;
+ close) cmd_close ;;
+ open) cmd_open ;;
-h|--help)
cat <<'USAGE'
-usage: panama-lid [status|guard]
+usage: panama-lid [status|guard|close|open]
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
+ close docked lid closed: turn the internal panel off (bound to the lid
+ switch by keybinds.lua); does nothing undocked
+ open lid opened: turn the internal panel back on, restoring the mode
+ and scale chosen in Settings
USAGE
;;
*) printf 'panama-lid: unknown command: %s\n' "$1" >&2; exit 2 ;;
diff --git a/config/dot/quickshell/scripts/panama-osd b/config/dot/quickshell/scripts/panama-osd
index d2750d0..245d9ff 100755
--- a/config/dot/quickshell/scripts/panama-osd
+++ b/config/dot/quickshell/scripts/panama-osd
@@ -240,11 +240,34 @@ media_action() {
show_message "$kind" "$label"
}
+# Airplane mode: one rfkill state for every radio, with an OSD saying which
+# way it went. "Any radio unblocked" reads as radios-on, so toggle means
+# "block everything" from a mixed state -- the direction a person reaching
+# for airplane mode wants.
+airplane_action() {
+ case "${1:-toggle}" in
+ toggle)
+ if rfkill list 2>/dev/null | grep -q 'Soft blocked: no'; then
+ rfkill block all 2>/dev/null \
+ && show_message "airplane-mode-symbolic" "Airplane mode on"
+ else
+ rfkill unblock all 2>/dev/null \
+ && show_message "network-wireless-symbolic" "Airplane mode off"
+ fi
+ ;;
+ *)
+ printf 'Usage: panama-osd airplane toggle\n' >&2
+ return 2
+ ;;
+ esac
+}
+
case "${1:-}" in
volume) shift; adjust_volume "$@" ;;
microphone) shift; adjust_microphone "$@" ;;
brightness) shift; adjust_brightness "$@" ;;
media) shift; media_action "$@" ;;
+ airplane) shift; airplane_action "$@" ;;
message)
shift
kind="${1:-}"
@@ -256,7 +279,7 @@ case "${1:-}" in
show_message "$kind" "$*"
;;
*)
- printf 'Usage: panama-osd volume|microphone|brightness|media|message ACTION [value]\n' >&2
+ printf 'Usage: panama-osd volume|microphone|brightness|media|airplane|message ACTION [value]\n' >&2
exit 2
;;
esac
diff --git a/config/dot/quickshell/services/DeviceEvents.qml b/config/dot/quickshell/services/DeviceEvents.qml
index 932b65f..d616a5d 100644
--- a/config/dot/quickshell/services/DeviceEvents.qml
+++ b/config/dot/quickshell/services/DeviceEvents.qml
@@ -7,6 +7,7 @@ import Quickshell.Bluetooth
import Quickshell.Io
import Quickshell.Services.Pipewire
import QtQuick
+import qs.config
Singleton {
id: root
@@ -146,11 +147,38 @@ Singleton {
// Running out is not ambient. Published at a priority Do Not Disturb does
// not silence, because the one notification you must not miss is the one
// saying the machine is about to stop.
+ //
+ // And then, unless told otherwise, the machine is put to sleep: a suspend
+ // at the urgent threshold preserves the session for days, a hard cut at
+ // 0% preserves nothing. The grace timer is what makes the warning above
+ // readable and the action cancelable -- plugging in during it stands
+ // everything down.
+ Timer {
+ id: criticalSuspend
+ interval: 15000
+ onTriggered: {
+ if (Battery.critical && !Battery.acOnline
+ && DesktopPreferences.get("batteryCriticalAction") === "suspend")
+ Quickshell.execDetached(["systemctl", "suspend"]);
+ }
+ }
+
+ Connections {
+ target: Battery
+ function onAcOnlineChanged(): void {
+ if (Battery.acOnline)
+ criticalSuspend.stop();
+ }
+ }
+
Connections {
target: Battery
function onCriticalChanged(): void {
if (!root.powerInitialized || !Battery.critical)
return;
+ if (!Battery.acOnline
+ && DesktopPreferences.get("batteryCriticalAction") === "suspend")
+ criticalSuspend.restart();
StatusEvents.publish({
key: "battery-critical",
glyph: "\u{F0083}",
diff --git a/config/local/share/vicinae/scripts/settings-power b/config/local/share/vicinae/scripts/settings-power
index 8391654..f8509f1 100755
--- a/config/local/share/vicinae/scripts/settings-power
+++ b/config/local/share/vicinae/scripts/settings-power
@@ -5,6 +5,6 @@
# @vicinae.mode silent
# @vicinae.icon ../../icons/hicolor/scalable/apps/panama-settings.svg
# @vicinae.description Open Power & Lock in Settings.
-# @vicinae.keywords ["settings", "warn at", "urgent at", "stop charging at", "turn the screen off after", "lock the screen after", "suspend after", "lock before sleeping", "lock after"]
+# @vicinae.keywords ["settings", "warn at", "urgent at", "at the urgent level", "stop charging at", "turn the screen off after", "lock the screen after", "suspend after", "lock before sleeping", "lock after"]
exec "$HOME/.config/quickshell/scripts/panama-action" settings-page power
diff --git a/docs/settings.md b/docs/settings.md
index f149caa..9d5aeca 100644
--- a/docs/settings.md
+++ b/docs/settings.md
@@ -4,7 +4,7 @@
Do not edit this file. Run `quickshell/scripts/panama-settings-docs`
after changing the schema; a contract fails when this copy is stale.
-149 settings across 30 groups. 70 of them are applied to the compositor and confirmed by reading the value back.
+150 settings across 30 groups. 70 of them are applied to the compositor and confirmed by reading the value back.
## accessibility
@@ -37,6 +37,7 @@ Found on **Power & Lock**.
|---|---|---|
| **Warn at**
`batteryLowPercent` | 20 % | Mention the battery once it drops this low. Range 5–50. |
| **Urgent at**
`batteryCriticalPercent` | 5 % | Interrupt at this level, even during Do Not Disturb. Range 1–25. |
+| **At the urgent level**
`batteryCriticalAction` | suspend | What happens when the battery reaches the urgent threshold while discharging Choices: Suspend, Only warn. |
| **Stop charging at**
`batteryChargeLimit` | 100 % | Charging to less than full is easier on the battery over years. Range 50–100. |
## capture
diff --git a/setup/scripts/change-settings b/setup/scripts/change-settings
index 094ac71..5a1e715 100755
--- a/setup/scripts/change-settings
+++ b/setup/scripts/change-settings
@@ -37,6 +37,16 @@ if [[ -f /etc/udev/rules.d/99-panama-gpu.rules ]]; then
sudo udevadm trigger --subsystem-match=drm
fi
+# The power-key drop-in lands via the same copy; logind reads its config only
+# at start, so without this the button keeps hard-powering-off until the next
+# boot. Restarting logind is what Fedora's own updates do and current systemd
+# preserves sessions across it.
+if [[ -f /etc/systemd/logind.conf.d/panama-power-key.conf ]]; then
+ log "Reloading logind so the power button opens the menu instead of powering off"
+ sudo systemctl try-restart systemd-logind 2>/dev/null \
+ || log "Could not restart logind; the power-button change applies at next boot"
+fi
+
# The document-portal drop-in lands via the same copy. A user unit's drop-ins
# are read at daemon-reload, so without this the guard applies from the next
# login rather than from now -- and the failure it guards against is one you
diff --git a/tests/quickshell/declared-dependencies-contract b/tests/quickshell/declared-dependencies-contract
index d2d1649..18d1467 100755
--- a/tests/quickshell/declared-dependencies-contract
+++ b/tests/quickshell/declared-dependencies-contract
@@ -36,7 +36,7 @@ SHELL_WORDS='^(if|then|else|elif|fi|for|while|until|do|done|case|esac|in|functio
# authselect is on the list for the same reason: it manages Fedora's PAM and
# nsswitch profiles and arrives with fprintd-pam, realmd and nss-mdns, so the
# fingerprint aliases in config/bash can rely on it without declaring it.
-BASELINE='^(sh|bash|cat|cut|sed|awk|gawk|grep|egrep|head|tail|sort|uniq|tr|wc|find|xargs|basename|dirname|mkdir|rm|cp|mv|ln|chmod|chown|stat|df|du|date|sleep|env|id|tee|touch|mktemp|readlink|realpath|seq|comm|join|paste|od|file|nl|fold|column|tput|timeout|flock|install|sha256sum|md5sum|base64|nproc|uptime|free|uname|hostname|whoami|ps|pgrep|pkill|kill|killall|lsblk|mount|umount|sudo|su|rpm|dnf|flatpak|git|python3|ss|ip|lsof|authselect|setsid|nohup|grub2-mkconfig)$'
+BASELINE='^(sh|bash|cat|cut|sed|awk|gawk|grep|egrep|head|tail|sort|uniq|tr|wc|find|xargs|basename|dirname|mkdir|rm|cp|mv|ln|chmod|chown|stat|df|du|date|sleep|env|id|tee|touch|mktemp|readlink|realpath|seq|comm|join|paste|od|file|nl|fold|column|tput|timeout|flock|install|sha256sum|md5sum|base64|nproc|uptime|free|uname|hostname|whoami|ps|pgrep|pkill|kill|killall|lsblk|mount|umount|sudo|su|rpm|dnf|flatpak|git|python3|ss|ip|ls|rfkill|lsof|authselect|setsid|nohup|grub2-mkconfig)$'
SESSION='^(systemctl|busctl|journalctl|loginctl|hostnamectl|localectl|systemd-inhibit|systemd-run|udevadm|gsettings|dconf|dbus-send|dbus-monitor|hyprctl|qs|quickshell|gnf|panama|wl-copy|wl-paste)$'
diff --git a/tests/setup/lid-contract b/tests/setup/lid-contract
index ef4199d..7aa2fe3 100755
--- a/tests/setup/lid-contract
+++ b/tests/setup/lid-contract
@@ -46,8 +46,12 @@ mkdir -p "$fake/bin"
# ── 1. Panama never takes the lid over ───────────────────────────────────────
-if compgen -G "$repo_dir/config/copy/etc/systemd/logind.conf.d/*" >/dev/null 2>&1; then
- note 'a logind drop-in is shipped; the inhibitor approach exists so the lid is never left doing nothing'
+# Specifically the LID. Other keys may be configured there -- the power-key
+# drop-in is deliberate policy with a compositor bind behind it -- but a
+# HandleLidSwitch line would replace the inhibitor design this file protects.
+if compgen -G "$repo_dir/config/copy/etc/systemd/logind.conf.d/*" >/dev/null 2>&1 \
+ && sed 's/#.*//' "$repo_dir"/config/copy/etc/systemd/logind.conf.d/* | grep -q 'HandleLidSwitch'; then
+ note 'a logind drop-in sets HandleLidSwitch; the inhibitor approach exists so the lid is never left doing nothing'
fi
# Comments are stripped first: these files explain at length what they
# deliberately do NOT do, and prose naming HandleLidSwitch or lock-session is