Files
dotfiles/quickshell/services/Brightness.qml
T
2026-10-09 04:17:36 +02:00

98 lines
2.8 KiB
QML

pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
// Backlight control. Reads the sysfs value directly so the OSD is exact, and
// writes through brightnessctl, which already owns the udev permissions here.
Singleton {
id: root
readonly property string device: "amdgpu_bl1"
property int raw: 0
property int maxRaw: 65535
readonly property real percent: maxRaw > 0 ? raw / maxRaw * 100 : 0
readonly property bool available: maxRaw > 0
signal bumped()
FileView {
id: maxFile
path: "/sys/class/backlight/" + root.device + "/max_brightness"
watchChanges: false
onLoaded: {
const v = parseInt(text().trim());
if (v > 0) root.maxRaw = v;
}
}
FileView {
id: currentFile
path: "/sys/class/backlight/" + root.device + "/brightness"
watchChanges: true
onFileChanged: reload()
onLoaded: {
const v = parseInt(text().trim());
if (!isNaN(v) && v !== root.raw) {
root.raw = v;
// Settled, or moved by something else: step from here again.
if (!setter.running && root.pending < 0) root.target = -1;
root.bumped();
}
}
}
// sysfs writes do not always fire inotify for backlight, so poll gently too.
Timer {
interval: 1000
running: !GameMode.active
repeat: true
onTriggered: currentFile.reload()
}
// The level last asked for, until the backlight reports it. Steps build on
// it rather than on `percent`, which only catches up on the next read, so
// a fast scroll adds up instead of repeating one step.
property int target: -1
// A level asked for while a write is still running. Restarting the write
// would kill it half done, so the newest request waits and the rest drop.
property int pending: -1
Process {
id: setter
onExited: {
currentFile.reload();
if (root.pending < 0) return;
const next = root.pending;
root.pending = -1;
root._write(next);
}
}
function _write(p: int) {
setter.command = ["brightnessctl", "-d", root.device, "-q", "set", p + "%"];
setter.running = true;
}
function setPercent(p: real) {
const clamped = Math.max(1, Math.min(100, Math.round(p)));
root.target = clamped;
if (setter.running) root.pending = clamped;
else root._write(clamped);
}
function change(delta: real) {
setPercent((root.target >= 0 ? root.target : percent) + delta);
}
readonly property string icon: {
const p = root.percent;
if (p >= 80) return "󰃠";
if (p >= 55) return "󰃝";
if (p >= 30) return "󰃟";
return "󰃞";
}
}