Files
dots-hyprland/.config/quickshell/ii/services/Brightness.qml
T

166 lines
4.7 KiB
QML

pragma Singleton
pragma ComponentBehavior: Bound
// From https://github.com/caelestia-dots/shell with modifications.
// License: GPLv3
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import QtQuick
/**
* For managing brightness of monitors. Supports both brightnessctl and ddcutil.
*/
Singleton {
id: root
signal brightnessChanged()
property var ddcMonitors: []
readonly property list<BrightnessMonitor> monitors: Quickshell.screens.map(screen => monitorComp.createObject(root, {
screen
}))
function getMonitorForScreen(screen: ShellScreen): var {
return monitors.find(m => m.screen === screen);
}
function increaseBrightness(): void {
const focusedName = Hyprland.focusedMonitor.name;
const monitor = monitors.find(m => focusedName === m.screen.name);
if (monitor)
monitor.setBrightness(monitor.brightness + 0.05);
}
function decreaseBrightness(): void {
const focusedName = Hyprland.focusedMonitor.name;
const monitor = monitors.find(m => focusedName === m.screen.name);
if (monitor)
monitor.setBrightness(monitor.brightness - 0.05);
}
reloadableId: "brightness"
onMonitorsChanged: {
ddcMonitors = [];
ddcProc.running = true;
}
Process {
id: ddcProc
command: ["ddcutil", "detect", "--brief"]
stdout: SplitParser {
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: setProc
}
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 int rawMaxBrightness: 100
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.rawMaxBrightness = parseInt(max);
monitor.brightness = parseInt(current) / monitor.rawMaxBrightness;
monitor.ready = true;
}
}
}
// We need a delay for DDC monitors because they can be quite slow and might act weird with rapid changes
property var setTimer: Timer {
id: setTimer
interval: monitor.isDdc ? 300 : 0
onTriggered: {
syncBrightness();
}
}
function syncBrightness() {
const rounded = Math.round(monitor.brightness * monitor.rawMaxBrightness);
setProc.command = isDdc ? ["ddcutil", "-b", busNum, "setvcp", "10", rounded] : ["brightnessctl", "s", rounded, "--quiet"];
setProc.startDetached();
}
function setBrightness(value: real): void {
value = Math.max(0.01, Math.min(1, value));
monitor.brightness = value;
setTimer.restart();
}
Component.onCompleted: {
initialize();
}
onBusNumChanged: {
initialize();
}
}
Component {
id: monitorComp
BrightnessMonitor {}
}
IpcHandler {
target: "brightness"
function increment() {
onPressed: root.increaseBrightness()
}
function decrement() {
onPressed: root.decreaseBrightness()
}
}
GlobalShortcut {
name: "brightnessIncrease"
description: "Increase brightness"
onPressed: root.increaseBrightness()
}
GlobalShortcut {
name: "brightnessDecrease"
description: "Decrease brightness"
onPressed: root.decreaseBrightness()
}
}