import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; import Service from 'resource:///com/github/Aylur/ags/service.js'; import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; const { exec, execAsync } = Utils; import { clamp } from '../modules/.miscutils/mathfuncs.js'; class BrightnessServiceBase extends Service { static { Service.register( this, { 'screen-changed': ['float'], }, { 'screen-value': ['float', 'rw'], }, ); } _screenValue = 0; // the getter has to be in snake_case get screen_value() { return this._screenValue; } // the setter has to be in snake_case too set screen_value(percent) { percent = clamp(percent, 0, 1); this._screenValue = percent; Utils.execAsync(this.setBrightnessCmd(percent)) .then(() => { // signals has to be explicity emitted this.emit('screen-changed', percent); this.notify('screen-value'); // or use Service.changed(propName: string) which does the above two // this.changed('screen'); }) .catch(print); } // overwriting connectWidget method, lets you // change the default event that widgets connect to connectWidget(widget, callback, event = 'screen-changed') { super.connectWidget(widget, callback, event); } } class BrightnessCtlService extends BrightnessServiceBase { static { Service.register(this); } constructor() { super(); const current = Number(exec('brightnessctl g')); const max = Number(exec('brightnessctl m')); this._screenValue = current / max; } setBrightnessCmd(percent) { return `brightnessctl s ${percent * 100}% -q`; } } class BrightnessDdcService extends BrightnessServiceBase { static { Service.register(this); } constructor(monitor = 0) { super(); // don't use Hyprland.getMonitor(id), Hyprland monitor id isn't consistent // with Gdk, but the Array ordering is (magically) this._sn = Hyprland.monitors[monitor].serial; Utils.execAsync(`ddcutil --sn ${this._sn} getvcp 10 --brief`) .then((out) => { // only the last line is useful out = out.split('\n'); out = out[out.length - 1]; out = out.split(' '); const current = Number(out[3]); const max = Number(out[4]); this._screenValue = current / max; }) .catch(print); } setBrightnessCmd(percent) { return `ddcutil --sn ${this._sn} setvcp 10 ${Math.round(percent * 100)}`; } } // the singleton instance const numMonitors = Hyprland.monitors.length; const service = Array(numMonitors); switch (userOptions.brightness.controller) { case "brightnessctl": service.fill(new BrightnessCtlService()); break; case "ddcutil": for (let i = 0; i < numMonitors; i++) { service[i] = new BrightnessDdcService(i); } break; default: throw new Error(`Unknown brightness controller ${userOptions.brightness.controller}`); } // make it global for easy use with cli globalThis.brightness = service[0]; // export to use in other modules export default service;