diff --git a/.config/quickshell/modules/bar/Bar.qml b/.config/quickshell/modules/bar/Bar.qml index fb5535e3a..79f3476db 100644 --- a/.config/quickshell/modules/bar/Bar.qml +++ b/.config/quickshell/modules/bar/Bar.qml @@ -26,7 +26,8 @@ Scope { PanelWindow { // Bar window id: barRoot - property var modelData + property ShellScreen modelData + property var brightnessMonitor: Brightness.getMonitorForScreen(modelData) screen: modelData WlrLayershell.namespace: "quickshell:bar" @@ -79,9 +80,9 @@ Scope { WheelHandler { onWheel: (event) => { if (event.angleDelta.y < 0) - Brightness.increment = -1; + barRoot.brightnessMonitor.setBrightness(barRoot.brightnessMonitor.brightness - 0.05); else if (event.angleDelta.y > 0) - Brightness.increment = 1; + barRoot.brightnessMonitor.setBrightness(barRoot.brightnessMonitor.brightness + 0.05); // Store the mouse position and start tracking barLeftSideMouseArea.lastScrollX = event.x; barLeftSideMouseArea.lastScrollY = event.y; diff --git a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml index f154fbd56..2662985d3 100644 --- a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml +++ b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml @@ -27,15 +27,7 @@ Scope { showOsdValues = false } } - - Connections { - target: Brightness ?? null - function onValueChanged() { - if (!Brightness.ready) return - root.triggerOsd() - } - } - + Connections { target: Audio.sink?.audio ?? null function onVolumeChanged() { @@ -51,6 +43,15 @@ Scope { id: osdLoader property var modelData active: showOsdValues + property var brightnessMonitor: Brightness.getMonitorForScreen(modelData) + + Connections { + target: brightnessMonitor + function onBrightnessChanged() { + if (!brightnessMonitor.ready) return + root.triggerOsd() + } + } PanelWindow { property var modelData @@ -101,7 +102,7 @@ Scope { OsdValueIndicator { id: osdValues anchors.centerIn: parent - value: Brightness.value + value: brightnessMonitor.brightness icon: "light_mode" name: qsTr("Brightness") } diff --git a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml index 6225f4a95..b1c812322 100644 --- a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml +++ b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml @@ -28,6 +28,13 @@ Scope { } } + Connections { + target: Brightness + function onBrightnessChanged() { + showOsdValues = false + } + } + Connections { target: Audio.sink?.audio ?? null function onVolumeChanged() { @@ -40,14 +47,6 @@ Scope { } } - Connections { - target: Brightness ?? null - function onValueChanged() { - if (!Brightness.ready) return - root.showOsdValues = false - } - } - Variants { model: Quickshell.screens diff --git a/.config/quickshell/services/Brightness.qml b/.config/quickshell/services/Brightness.qml index 8d8f88489..7e8be7cf4 100644 --- a/.config/quickshell/services/Brightness.qml +++ b/.config/quickshell/services/Brightness.qml @@ -1,92 +1,135 @@ pragma Singleton pragma ComponentBehavior: Bound + import Quickshell import Quickshell.Io import Quickshell.Hyprland +import QtQuick Singleton { id: root - property bool ready: false - property real value - property int increment: 0 + signal brightnessChanged() - function refresh() { - getBrightness.running = true; + property var ddcMonitors: [] + readonly property list monitors: Quickshell.screens.map(screen => monitorComp.createObject(root, { + screen + })) + + function getMonitorForScreen(screen: ShellScreen): var { + return monitors.find(m => m.screen === screen); } - onIncrementChanged: () => { - if (increment > 0) { - increaseBrightness.running = true; - root.increment = 0; - } else if (increment < 0) { - decreaseBrightness.running = true; - root.increment = 0; - } + function increaseBrightness(): void { + console.log("increaseBrightness"); + const focusedName = Hyprland.focusedMonitor.name; + const monitor = monitors.find(m => focusedName === m.screen.name); + if (monitor) + monitor.setBrightness(monitor.brightness + 0.1); + } + + function decreaseBrightness(): void { + const focusedName = Hyprland.focusedMonitor.name; + const monitor = monitors.find(m => focusedName === m.screen.name); + if (monitor) + monitor.setBrightness(monitor.brightness - 0.1); + } + + reloadableId: "brightness" + + onMonitorsChanged: { + ddcMonitors = []; + ddcProc.running = true; } Process { - id: getBrightness - - command: ["sh", "-c", "brightnessctl -m i | cut -d, -f4"] - running: true - onExited: { - if (!ready) ready = true - } + id: ddcProc + command: ["ddcutil", "detect", "--brief"] stdout: SplitParser { - onRead: (data) => { - root.value = parseFloat(data.replace("%", "")) / 100; - if (root.value < 0.01) { - preventPitchBlack.running = true; + splitMarker: "\n\n" + onRead: data => { + if (data.startsWith("Display ")) { + const lines = data.split("\n").map(l => l.trim()); + root.ddcMonitors.push({ + model: lines.find(l => l.startsWith("Monitor:")).split(":")[2], + busNum: lines.find(l => l.startsWith("I2C bus:")).split("/dev/i2c-")[1] + }); } } } - + onExited: root.ddcMonitorsChanged() } Process { - id: decreaseBrightness - - command: ["brightnessctl", "set", "5%-", "-e"] - running: false - onExited: { - running = false; - getBrightness.running = true; - } - } - - Process { - id: increaseBrightness - - command: ["brightnessctl", "set", "5%+", "-e"] - running: false - onExited: { - running = false; - getBrightness.running = true; - } - } - - Process { - id: preventPitchBlack - - command: ["brightnessctl", "set", "1%+", "-e"] - running: false - onExited: { - running = false; - getBrightness.running = true; - } + id: setProc } IpcHandler { target: "brightness" function increment() { - root.increment = 1 + onPressed: root.increaseBrightness() } function decrement() { - root.increment = -1 + onPressed: root.decreaseBrightness() } } + + component BrightnessMonitor: QtObject { + id: monitor + + required property ShellScreen screen + readonly property bool isDdc: root.ddcMonitors.some(m => m.model === screen.model) + readonly property string busNum: root.ddcMonitors.find(m => m.model === screen.model)?.busNum ?? "" + property real brightness + property bool ready: false + + onBrightnessChanged: { + if (monitor.ready) { + root.brightnessChanged(); + } + } + + function initialize() { + monitor.ready = false; + initProc.command = isDdc ? ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"] : ["sh", "-c", `echo "a b c $(brightnessctl g) $(brightnessctl m)"`]; + initProc.running = true; + } + + readonly property Process initProc: Process { + stdout: SplitParser { + onRead: data => { + const [, , , current, max] = data.split(" "); + monitor.brightness = parseInt(current) / parseInt(max); + monitor.ready = true; + } + } + } + + function setBrightness(value: real): void { + value = Math.max(0.01, Math.min(1, value)); + const rounded = Math.round(value * 100); + if (Math.round(brightness * 100) === rounded) + return; + brightness = value; + setProc.command = isDdc ? ["ddcutil", "-b", busNum, "setvcp", "10", rounded] : ["brightnessctl", "s", `${rounded}%`]; + setProc.startDetached(); + } + + Component.onCompleted: { + initialize(); + } + + onBusNumChanged: { + initialize(); + } + } + + Component { + id: monitorComp + + BrightnessMonitor {} + } }