From e45f412930edeedfc39f9df0da93ce54d64454f2 Mon Sep 17 00:00:00 2001 From: Kiana Sheibani Date: Sun, 25 Jan 2026 04:45:00 -0500 Subject: [PATCH] feat!: support exponential brightness curve I don't need most of the fluff for handling other types of displays, and getting rid of it lets me do something a lot nicer: add an exponential-gamma brightness display. --- config/Config.qml | 2 + modules/bar/popouts/Wrapper.qml | 1 - modules/osd/Content.qml | 12 +-- modules/osd/Interactions.qml | 4 +- modules/osd/Wrapper.qml | 2 - modules/ui/Interactions.qml | 1 - modules/ui/Panels.qml | 3 - modules/ui/UI.qml | 1 - services/Brightness.qml | 135 +++++--------------------------- 9 files changed, 28 insertions(+), 133 deletions(-) diff --git a/config/Config.qml b/config/Config.qml index e2269aa..16481c8 100644 --- a/config/Config.qml +++ b/config/Config.qml @@ -171,6 +171,8 @@ Singleton { readonly property string sunsetFrom: "21:00" readonly property string sunsetTo: "9:00" readonly property int sunsetTemperature: 4500 + + readonly property real brightnessExp: 4.0 } readonly property QtObject session: QtObject { diff --git a/modules/bar/popouts/Wrapper.qml b/modules/bar/popouts/Wrapper.qml index 305f17f..df5184f 100644 --- a/modules/bar/popouts/Wrapper.qml +++ b/modules/bar/popouts/Wrapper.qml @@ -13,7 +13,6 @@ Item { id: root required property PersistentProperties uiState - required property ShellScreen screen readonly property real nonAnimWidth: content.implicitWidth readonly property real nonAnimHeight: y > 0 || hasCurrent ? content.implicitHeight : 0 diff --git a/modules/osd/Content.qml b/modules/osd/Content.qml index 3495b96..1449135 100644 --- a/modules/osd/Content.qml +++ b/modules/osd/Content.qml @@ -11,7 +11,6 @@ Item { id: root required property var uiState - required property Brightness.Monitor monitor anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left @@ -95,13 +94,10 @@ Item { implicitHeight: Config.osd.sliderLength function onWheel(event: WheelEvent) { - const monitor = root.monitor; - if (!monitor) - return; if (event.angleDelta.y > 0) - monitor.setBrightness(monitor.brightness + 0.1); + Brightness.setBrightness(Brightness.brightness + 0.1); else if (event.angleDelta.y < 0) - monitor.setBrightness(monitor.brightness - 0.1); + Brightness.setBrightness(Brightness.brightness - 0.1); } CustomFilledSlider { @@ -109,8 +105,8 @@ Item { color: Config.colors.brightness icon: Icons.getBrightnessIcon(value) - value: root.monitor?.brightness ?? 0 - onMoved: root.monitor?.setBrightness(value) + value: Brightness.brightness + onMoved: Brightness.setBrightness(value) } } } diff --git a/modules/osd/Interactions.qml b/modules/osd/Interactions.qml index 3036a35..4c2f05f 100644 --- a/modules/osd/Interactions.qml +++ b/modules/osd/Interactions.qml @@ -7,10 +7,8 @@ Scope { id: root required property PersistentProperties uiState - required property ShellScreen screen required property bool hovered required property bool suppressed - readonly property Brightness.Monitor monitor: Brightness.getMonitorForScreen(screen) function show(): void { if (!root.suppressed) { @@ -34,7 +32,7 @@ Scope { } Connections { - target: root.monitor + target: Brightness function onBrightnessChanged(): void { if (root.uiState.osdBrightnessReact) diff --git a/modules/osd/Wrapper.qml b/modules/osd/Wrapper.qml index 109909c..a764f19 100644 --- a/modules/osd/Wrapper.qml +++ b/modules/osd/Wrapper.qml @@ -8,7 +8,6 @@ Item { id: root required property var uiState - required property ShellScreen screen visible: width > 0 implicitWidth: 0 @@ -61,6 +60,5 @@ Item { id: content uiState: root.uiState - monitor: Brightness.getMonitorForScreen(root.screen) } } diff --git a/modules/ui/Interactions.qml b/modules/ui/Interactions.qml index 8c0a24d..3e08345 100644 --- a/modules/ui/Interactions.qml +++ b/modules/ui/Interactions.qml @@ -120,7 +120,6 @@ CustomMouseArea { Osd.Interactions { uiState: root.uiState - screen: root.screen hovered: root.osdHovered suppressed: root.osdSuppressed } diff --git a/modules/ui/Panels.qml b/modules/ui/Panels.qml index f808fa2..b1b39c1 100644 --- a/modules/ui/Panels.qml +++ b/modules/ui/Panels.qml @@ -13,7 +13,6 @@ Item { id: root required property PersistentProperties uiState - required property ShellScreen screen required property Item bar readonly property alias popouts: popouts @@ -31,14 +30,12 @@ Item { id: popouts uiState: root.uiState - screen: root.screen } Osd.Wrapper { id: osd uiState: root.uiState - screen: root.screen anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right diff --git a/modules/ui/UI.qml b/modules/ui/UI.qml index 896cdc4..e988ab0 100644 --- a/modules/ui/UI.qml +++ b/modules/ui/UI.qml @@ -110,7 +110,6 @@ Variants { visible: !uiState.uiState.hidden uiState: uiState.uiState - screen: scope.modelData bar: bar } } diff --git a/services/Brightness.qml b/services/Brightness.qml index 856b0c7..b7f8bc9 100644 --- a/services/Brightness.qml +++ b/services/Brightness.qml @@ -12,58 +12,38 @@ Singleton { 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); - } + property real brightness: 1.0 + property real maxBrightness: 0.0 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); + setBrightness(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); + setBrightness(brightness - Config.osd.brightnessIncrement); } - onMonitorsChanged: { - ddcMonitors = []; - ddcProc.running = true; - } - - Variants { - id: variants - - model: Quickshell.screens - - Monitor {} + function setBrightness(value: real): void { + value = Math.max(0, Math.min(1, value)); + if (Math.abs(brightness - value) < 0.01) return; + brightness = value; + + const exp = Config.services.brightnessExp; + const raw = Math.round((value ** exp) * maxBrightness); + Quickshell.execDetached(["brightnessctl", "s", `${raw}`]); } + Component.onCompleted: initProc.running = true Process { - running: true - command: ["sh", "-c", "asdbctl get"] // To avoid warnings if asdbctl is not installed + id: initProc + command: ["sh", "-c", "echo $(brightnessctl g) $(brightnessctl m)"] 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] - })) + onStreamFinished: { + const exp = Config.services.brightnessExp; + const [cur, max] = text.split(" "); + root.maxBrightness = parseInt(max); + root.brightness = (parseInt(cur) / root.maxBrightness) ** (1 / exp); + } } } @@ -110,77 +90,4 @@ Singleton { 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() - } }