From 7d45e9dc1bbe48b80667d2ca4256429bf551d0ab Mon Sep 17 00:00:00 2001 From: MoetaYuko Date: Wed, 1 May 2024 15:23:30 +0800 Subject: [PATCH] ags: implement per-monitor brightness control --- .../modules/.configuration/user_options.js | 5 +- .config/ags/modules/bar/main.js | 2 +- .config/ags/modules/bar/normal/spaceleft.js | 8 +- .../ags/modules/indicators/indicatorvalues.js | 12 +-- .config/ags/modules/indicators/main.js | 3 +- .config/ags/services/brightness.js | 77 ++++++++++++++++--- 6 files changed, 81 insertions(+), 26 deletions(-) diff --git a/.config/ags/modules/.configuration/user_options.js b/.config/ags/modules/.configuration/user_options.js index fcc7ee6c5..1aa791def 100644 --- a/.config/ags/modules/.configuration/user_options.js +++ b/.config/ags/modules/.configuration/user_options.js @@ -38,6 +38,9 @@ let configOptions = { 'warnTitles': ["Low battery", "Very low battery", 'Critical Battery'], 'warnMessages': ["Plug in the charger", "You there?", 'PLUG THE CHARGER ALREADY'], }, + 'brightness': { + 'controller': "brightnessctl", + }, 'music': { 'preferredPlayer': "plasma-browser-integration", }, @@ -159,4 +162,4 @@ function overrideConfigRecursive(userOverrides, configOptions = {}) { overrideConfigRecursive(userOverrides, configOptions); globalThis['userOptions'] = configOptions; -export default configOptions; \ No newline at end of file +export default configOptions; diff --git a/.config/ags/modules/bar/main.js b/.config/ags/modules/bar/main.js index 7f74c541a..0a0d8f42e 100644 --- a/.config/ags/modules/bar/main.js +++ b/.config/ags/modules/bar/main.js @@ -46,7 +46,7 @@ export const Bar = async (monitor = 0) => { const minHeight = styleContext.get_property('min-height', Gtk.StateFlags.NORMAL); // execAsync(['bash', '-c', `hyprctl keyword monitor ,addreserved,${minHeight},0,0,0`]).catch(print); }, - startWidget: (await WindowTitle()), + startWidget: (await WindowTitle(monitor)), centerWidget: Widget.Box({ className: 'spacing-h-4', children: [ diff --git a/.config/ags/modules/bar/normal/spaceleft.js b/.config/ags/modules/bar/normal/spaceleft.js index 6fbe5d394..b3d796b63 100644 --- a/.config/ags/modules/bar/normal/spaceleft.js +++ b/.config/ags/modules/bar/normal/spaceleft.js @@ -39,16 +39,16 @@ const WindowTitle = async () => { } -export default async () => { +export default async (monitor = 0) => { const optionalWindowTitleInstance = await WindowTitle(); return Widget.EventBox({ onScrollUp: () => { Indicator.popup(1); // Since the brightness and speaker are both on the same window - Brightness.screen_value += 0.05; + Brightness[monitor].screen_value += 0.05; }, onScrollDown: () => { Indicator.popup(1); // Since the brightness and speaker are both on the same window - Brightness.screen_value -= 0.05; + Brightness[monitor].screen_value -= 0.05; }, onPrimaryClick: () => { App.toggleWindow('sideleft'); @@ -75,4 +75,4 @@ export default async () => { ] }) }); -} \ No newline at end of file +} diff --git a/.config/ags/modules/indicators/indicatorvalues.js b/.config/ags/modules/indicators/indicatorvalues.js index 18f228626..755d63e8b 100644 --- a/.config/ags/modules/indicators/indicatorvalues.js +++ b/.config/ags/modules/indicators/indicatorvalues.js @@ -49,16 +49,16 @@ const OsdValue = ({ }); } -export default () => { +export default (monitor = 0) => { const brightnessIndicator = OsdValue({ name: 'Brightness', extraClassName: 'osd-brightness', extraProgressClassName: 'osd-brightness-progress', - labelSetup: (self) => self.hook(Brightness, self => { - self.label = `${Math.round(Brightness.screen_value * 100)}`; + labelSetup: (self) => self.hook(Brightness[monitor], self => { + self.label = `${Math.round(Brightness[monitor].screen_value * 100)}`; }, 'notify::screen-value'), - progressSetup: (self) => self.hook(Brightness, (progress) => { - const updateValue = Brightness.screen_value; + progressSetup: (self) => self.hook(Brightness[monitor], (progress) => { + const updateValue = Brightness[monitor].screen_value; progress.value = updateValue; }, 'notify::screen-value'), }); @@ -109,4 +109,4 @@ export default () => { ] }) }); -} \ No newline at end of file +} diff --git a/.config/ags/modules/indicators/main.js b/.config/ags/modules/indicators/main.js index db3fc4c8c..16741937f 100644 --- a/.config/ags/modules/indicators/main.js +++ b/.config/ags/modules/indicators/main.js @@ -22,7 +22,7 @@ export default (monitor = 0) => Widget.Window({ className: 'osd-window', css: 'min-height: 2px;', children: [ - IndicatorValues(), + IndicatorValues(monitor), MusicControls(), NotificationPopups(), ColorScheme(), @@ -30,4 +30,3 @@ export default (monitor = 0) => Widget.Window({ }) }), }); - diff --git a/.config/ags/services/brightness.js b/.config/ags/services/brightness.js index 89bfbac10..778f44ef2 100644 --- a/.config/ags/services/brightness.js +++ b/.config/ags/services/brightness.js @@ -1,10 +1,11 @@ +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 BrightnessService extends Service { +class BrightnessServiceBase extends Service { static { Service.register( this, @@ -23,7 +24,7 @@ class BrightnessService extends Service { percent = clamp(percent, 0, 1); this._screenValue = percent; - Utils.execAsync(`brightnessctl s ${percent * 100}% -q`) + Utils.execAsync(this.setBrightnessCmd(percent)) .then(() => { // signals has to be explicity emitted this.emit('screen-changed', percent); @@ -35,13 +36,6 @@ class BrightnessService extends Service { .catch(print); } - constructor() { - super(); - const current = Number(exec('brightnessctl g')); - const max = Number(exec('brightnessctl m')); - this._screenValue = current / max; - } - // overwriting connectWidget method, lets you // change the default event that widgets connect to connectWidget(widget, callback, event = 'screen-changed') { @@ -49,11 +43,70 @@ class BrightnessService extends Service { } } +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 service = new BrightnessService(); +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; +globalThis.brightness = service[0]; // export to use in other modules -export default service; \ No newline at end of file +export default service;