Give a laptop its first week: the keys, the lid, the battery's endgame

The audit's fourth tier -- what a Framework owner reaches for in the first
week and found missing.

The power button stops being an instant, unconfirmed poweroff: a shipped
logind drop-in tells the daemon to stand down and the compositor binds the
key to the power menu, the way GNOME turns it into a question. Holding it
still hard-cuts through firmware. change-settings restarts logind so the
change applies without waiting for a boot, and the Power page says what the
button does now.

The function row fills in: F10 (XF86RFKill) toggles airplane mode through a
new panama-osd verb that blocks or unblocks every radio and says which way it
went; F9 (XF86Display) opens the Displays page, the honest action until
mirroring exists. And the lid becomes a switch bind: closing a docked lid
turns the internal panel off so nothing renders inside a closed shell and no
workspace strands on an invisible output, and opening it restores the panel
with the mode and scale chosen in Settings. panama-lid owns both decisions;
undocked machines suspend via logind before any of it matters.

The battery gets an endgame. On battery the screen dims to 30% two-thirds of
the way to blanking -- GNOME's single largest idle battery saver -- and
restores exactly the level it saved. At the urgent threshold the machine
suspends after a fifteen-second grace, cancelled by plugging in, because a
suspend preserves the session for days and a hard cut at 0% preserves
nothing; "Only warn" remains a choice on the Power page. Hibernate joins the
power menu, but only where logind answers CanHibernate with yes -- an entry
that fails silently is worse than none.

And brightness stops being two code paths: the Displays page now embeds the
same control the quick-settings panel uses, so the built-in backlight and
DDC/CI monitors share one surface that withdraws itself where neither
exists. The lid contract narrows to what its principle protects -- a
HandleLidSwitch drop-in -- so deliberate policy for other keys can ship.

Claude-Session: https://claude.ai/code/session_01Epx9ZC1gwm81K3jm9x9CKh
This commit is contained in:
Gabriel Brown
2026-08-23 12:21:08 -04:00
parent 153554b5df
commit 9530e32326
15 changed files with 244 additions and 35 deletions
@@ -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",