pragma Singleton // ───────────────────────────────────────────────────────────────────────────── // Closing the lid on a docked machine should not put it to sleep. // // logind handles the lid correctly except for one case it cannot see: an // external display means a closed lid is a docked machine rather than one // being put in a bag. Its own "docked" test looks for an ACPI docking station, // which 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 disconnects; logind does the rest, including the suspend, and // hypridle's before_sleep_cmd already locks the session on the way down. // // This service exists only to notice the topology changing. The decision // itself is in bin/panama-lid, so a machine with no lid and no external // display costs one process start that exits immediately. // // Failure is safe in the direction that matters. If the guard dies, or this // service never starts, the inhibitor is released and logind's default returns: // a docked laptop suspends when the lid closes, which is merely annoying. The // alternative design -- a logind drop-in setting HandleLidSwitch=ignore plus a // watcher of our own -- fails the other way, leaving a lid that does nothing at // all on a machine being carried out of the building. // ───────────────────────────────────────────────────────────────────────────── import Quickshell import Quickshell.Io import QtQuick Singleton { id: root readonly property string helperPath: Quickshell.shellDir + "/scripts/panama-lid" // Whether the inhibitor is currently held. Reported by the Power page and // by panama-doctor; nothing depends on it to make a decision. readonly property bool inhibited: guard.running // Same rule panama-hw uses: eDP, LVDS and DSI are the built-in panel and // everything else arrived through a cable. Computed here rather than asked // of the helper so a topology change is noticed without spawning anything. readonly property bool externalConnected: { const screens = Quickshell.screens ?? []; for (const screen of screens) { const name = String(screen?.name ?? ""); if (name === "") continue; if (!/eDP|LVDS|DSI/i.test(name)) return true; } return false; } onExternalConnectedChanged: root.reconcile() function reconcile(): void { // Restarting rather than toggling: the helper re-checks whether this // machine is a laptop at all, and a process that is already holding // the right inhibitor costs nothing to leave alone. if (root.externalConnected) { if (!guard.running) guard.running = true; } else if (guard.running) { guard.running = false; } } // Long-lived by design: it holds the inhibitor for as long as it runs, and // exits immediately on a machine that should not have one. Process { id: guard command: [root.helperPath, "guard"] } Component.onCompleted: root.reconcile() }