Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

quickshell/nuguland/Brightness.qml

Raw
import QtQuick
import Quickshell
import Quickshell.Io
import "brightness.js" as Brightness

Item {
    id: root

    property Osd osdService
    property Niri niriService
    property string devicePath: "/sys/class/backlight/intel_backlight"
    property var ddcDisplays: ({})
    property var lastAction: ({})
    property var lastOsd: ({})
    // Optimistic session level. The sysfs write is async (execDetached) and the
    // FileView (blockAllReads) loads once and never reloads, so a fresh read after
    // a write returns the stale startup value. Track the level we just set so
    // stateJson / the bar reflect it immediately. -1 = not yet set this session.
    property real liveLevel: -1

    function intText(text, fallback) {
        const n = parseInt(String(text || "").trim(), 10);
        return isFinite(n) ? n : fallback;
    }

    function backlightPresent() {
        return String(maxFile.text() || "").trim().length > 0 && String(brightnessFile.text() || "").trim().length > 0;
    }

    function nativeMax() {
        return Math.max(1, intText(maxFile.text(), 1));
    }

    function nativeBrightness() {
        return Math.max(1, intText(brightnessFile.text(), nativeMax()));
    }

    function levelFraction() {
        if (!backlightPresent())
            return 0;

        if (liveLevel >= 0)
            return Brightness.clampLevel(liveLevel);

        return Brightness.clampLevel(nativeBrightness() / nativeMax());
    }

    function outputRows() {
        return niriService ? niriService.outputList() : [];
    }

    function osd(value) {
        const text = String(Brightness.levelPercent(value));
        if (!osdService)
            return {
                "event": "brightness",
                "value": text
            };

        try {
            return JSON.parse(osdService.trigger("brightness", text));
        } catch (error) {
            return {
                "event": "brightness",
                "value": text
            };
        }
    }

    function stateObject() {
        const level = Math.round(levelFraction() * 100) / 100;
        const displays = Brightness.displayRows({
            "present": backlightPresent(),
            "level": level
        }, outputRows(), ddcDisplays);
        const global = Brightness.globalState(displays);
        // Internal panels use backlight; only external displays need DDC setup.
        const setupNeeded = displays.some(row => row.kind === "ddc" && !row.controllable);
        return {
            "level": global.level,
            "displays": displays,
            "global": global,
            "setup": {
                "needed": setupNeeded,
                "available": true
            },
            "compact": {
                "label": "brightness",
                "role": Brightness.levelRole(global.level)
            },
            "lastAction": lastAction,
            "lastOsd": lastOsd
        };
    }

    function stateJson() {
        return JSON.stringify(stateObject());
    }

    function selfTest() {
        const cases = [];
        const check = (name, got, want) => cases.push({
                "case": name,
                "ok": JSON.stringify(got) === JSON.stringify(want),
                "got": got,
                "want": want
            });
        check("clampLevel(-1)", Brightness.clampLevel(-1), 0);
        check("clampLevel(2)", Brightness.clampLevel(2), 1);
        check("levelPercent(0.5)", Brightness.levelPercent(0.5), 50);
        check("levelRole(0.15)", Brightness.levelRole(0.15), "ui.minor");
        check("levelRole(0.5)", Brightness.levelRole(0.5), "ui.accent");
        check("levelRole(0.85)", Brightness.levelRole(0.85), "warning");
        const fail = cases.filter(c => !c.ok).length;
        return JSON.stringify({
            "pass": cases.length - fail,
            "fail": fail,
            "cases": cases
        });
    }

    function applyNative(level) {
        if (!backlightPresent())
            return;

        const value = Brightness.clampLevel(level);
        Quickshell.execDetached(["sh", "-c", `d='${devicePath}'; m=$(cat "$d/max_brightness"); n=$(awk -v v=${value} -v m=$m 'BEGIN { x=int(v*m); if (x<1) x=1; if (x>m) x=m; print x }'); printf '%s\n' "$n" > "$d/brightness"`]);
    }

    function setupDdc() {
        lastAction = {
            "kind": "setup-ddc"
        };
        Quickshell.execDetached(["pkexec", "sh", "-c", "pacman -S --needed --noconfirm ddcutil && modprobe i2c-dev && udevadm trigger --subsystem-match=i2c-dev"]);
        return stateJson();
    }

    function setDisplay(id, level) {
        const value = Brightness.clampLevel(level);
        if (String(id) === "backlight") {
            applyNative(value);
            liveLevel = value;
        }
        lastAction = {
            "kind": "set-display",
            "id": String(id),
            "level": Math.round(value * 100) / 100
        };
        lastOsd = osd(value);
        return stateJson();
    }

    function setAll(level) {
        const value = Brightness.clampLevel(level);
        const rows = stateObject().displays || [];
        for (let i = 0; i < rows.length; i++) {
            if (rows[i].controllable)
                setDisplay(rows[i].id, value);
        }
        lastAction = {
            "kind": "set-all",
            "level": Math.round(value * 100) / 100
        };
        lastOsd = osd(value);
        return stateJson();
    }

    function setLevel(level) {
        const value = Brightness.clampLevel(level);
        return setAll(value);
    }

    function increase() {
        return setLevel(levelFraction() + 0.05);
    }

    function decrease() {
        return setLevel(levelFraction() - 0.05);
    }

    FileView {
        id: brightnessFile

        path: `${root.devicePath}/brightness`
        blockLoading: true
        blockAllReads: true
        printErrors: false
    }

    FileView {
        id: maxFile

        path: `${root.devicePath}/max_brightness`
        blockLoading: true
        blockAllReads: true
        printErrors: false
    }
}