pragma Singleton pragma ComponentBehavior: Bound import qs.custom import Quickshell import Quickshell.Io import QtQuick Singleton { id: root reloadableId: "brightness" property list ddcMonitors: [] readonly property list monitors: variants.instances property bool appleDisplayPresent: false function getMonitorForScreen(screen: ShellScreen): var { return monitors.find(m => m.modelData === screen); } function increaseBrightness(): void { const focusedName = Hypr.focusedMonitor.name; const monitor = monitors.find(m => focusedName === m.modelData.name); if (monitor) monitor.setBrightness(monitor.brightness + Config.osd.brightnessIncrement); } function decreaseBrightness(): void { const focusedName = Hypr.focusedMonitor.name; const monitor = monitors.find(m => focusedName === m.modelData.name); if (monitor) monitor.setBrightness(monitor.brightness - Config.osd.brightnessIncrement); } onMonitorsChanged: { ddcMonitors = []; ddcProc.running = true; } Variants { id: variants model: Quickshell.screens Monitor {} } Process { running: true command: ["sh", "-c", "asdbctl get"] // To avoid warnings if asdbctl is not installed stdout: StdioCollector { onStreamFinished: root.appleDisplayPresent = text.trim().length > 0 } } Process { id: ddcProc command: ["ddcutil", "detect", "--brief"] stdout: StdioCollector { onStreamFinished: root.ddcMonitors = text.trim().split("\n\n").filter(d => d.startsWith("Display ")).map(d => ({ model: d.match(/Monitor:.*:(.*):.*/)[1], busNum: d.match(/I2C bus:[ ]*\/dev\/i2c-([0-9]+)/)[1] })) } } CustomShortcut { name: "brightnessUp" description: "Increase brightness" onPressed: root.increaseBrightness() } CustomShortcut { name: "brightnessDown" description: "Decrease brightness" onPressed: root.decreaseBrightness() } component Monitor: QtObject { id: monitor required property ShellScreen modelData readonly property bool isDdc: root.ddcMonitors.some(m => m.model === modelData.model) readonly property string busNum: root.ddcMonitors.find(m => m.model === modelData.model)?.busNum ?? "" readonly property bool isAppleDisplay: root.appleDisplayPresent && modelData.model.startsWith("StudioDisplay") property real brightness property real queuedBrightness: NaN readonly property Process initProc: Process { stdout: StdioCollector { onStreamFinished: { if (monitor.isAppleDisplay) { const val = parseInt(text.trim()); monitor.brightness = val / 101; } else { const [, , , cur, max] = text.split(" "); monitor.brightness = parseInt(cur) / parseInt(max); } } } } readonly property Timer timer: Timer { interval: 500 onTriggered: { if (!isNaN(monitor.queuedBrightness)) { monitor.setBrightness(monitor.queuedBrightness); monitor.queuedBrightness = NaN; } } } function setBrightness(value: real): void { value = Math.max(0, Math.min(1, value)); const rounded = Math.round(value * 100); if (Math.round(brightness * 100) === rounded) return; if (isDdc && timer.running) { queuedBrightness = value; return; } brightness = value; if (isAppleDisplay) Quickshell.execDetached(["asdbctl", "set", rounded]); else if (isDdc) Quickshell.execDetached(["ddcutil", "-b", busNum, "setvcp", "10", rounded]); else Quickshell.execDetached(["brightnessctl", "s", `${rounded}%`]); if (isDdc) timer.restart(); } function initBrightness(): void { if (isAppleDisplay) initProc.command = ["asdbctl", "get"]; else if (isDdc) initProc.command = ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"]; else initProc.command = ["sh", "-c", "echo a b c $(brightnessctl g) $(brightnessctl m)"]; initProc.running = true; } onBusNumChanged: initBrightness() Component.onCompleted: initBrightness() } }