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()
}
