feat: add brightness widget

This commit is contained in:
tux
2025-09-26 12:41:08 +05:30
parent 67e74a741a
commit 9f6ebfc46f
4 changed files with 173 additions and 0 deletions

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path fill-rule="evenodd" clip-rule="evenodd" d="M4.25 19C4.25 18.5858 4.58579 18.25 5 18.25H19C19.4142 18.25 19.75 18.5858 19.75 19C19.75 19.4142 19.4142 19.75 19 19.75H5C4.58579 19.75 4.25 19.4142 4.25 19ZM7.25 22C7.25 21.5858 7.58579 21.25 8 21.25H16C16.4142 21.25 16.75 21.5858 16.75 22C16.75 22.4142 16.4142 22.75 16 22.75H8C7.58579 22.75 7.25 22.4142 7.25 22Z" fill="#000"></path> <path d="M6.08267 15.25C5.5521 14.2858 5.25 13.1778 5.25 12C5.25 8.27208 8.27208 5.25 12 5.25C15.7279 5.25 18.75 8.27208 18.75 12C18.75 13.1778 18.4479 14.2858 17.9173 15.25H22C22.4142 15.25 22.75 15.5858 22.75 16C22.75 16.4142 22.4142 16.75 22 16.75H2C1.58579 16.75 1.25 16.4142 1.25 16C1.25 15.5858 1.58579 15.25 2 15.25H6.08267Z" fill="#000"></path> <path fill-rule="evenodd" clip-rule="evenodd" d="M12 1.25C12.4142 1.25 12.75 1.58579 12.75 2V3C12.75 3.41421 12.4142 3.75 12 3.75C11.5858 3.75 11.25 3.41421 11.25 3V2C11.25 1.58579 11.5858 1.25 12 1.25ZM4.39861 4.39861C4.6915 4.10572 5.16638 4.10572 5.45927 4.39861L5.85211 4.79145C6.145 5.08434 6.145 5.55921 5.85211 5.85211C5.55921 6.145 5.08434 6.145 4.79145 5.85211L4.39861 5.45927C4.10572 5.16638 4.10572 4.6915 4.39861 4.39861ZM19.6011 4.39887C19.894 4.69176 19.894 5.16664 19.6011 5.45953L19.2083 5.85237C18.9154 6.14526 18.4405 6.14526 18.1476 5.85237C17.8547 5.55947 17.8547 5.0846 18.1476 4.79171L18.5405 4.39887C18.8334 4.10598 19.3082 4.10598 19.6011 4.39887ZM1.25 12C1.25 11.5858 1.58579 11.25 2 11.25H3C3.41421 11.25 3.75 11.5858 3.75 12C3.75 12.4142 3.41421 12.75 3 12.75H2C1.58579 12.75 1.25 12.4142 1.25 12ZM20.25 12C20.25 11.5858 20.5858 11.25 21 11.25H22C22.4142 11.25 22.75 11.5858 22.75 12C22.75 12.4142 22.4142 12.75 22 12.75H21C20.5858 12.75 20.25 12.4142 20.25 12Z" fill="#000"></path> </g></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path fill-rule="evenodd" clip-rule="evenodd" d="M8 5H16C18.8284 5 20.2426 5 21.1213 5.87868C22 6.75736 22 8.17157 22 11V13C22 15.8284 22 17.2426 21.1213 18.1213C20.2426 19 18.8284 19 16 19H8C5.17157 19 3.75736 19 2.87868 18.1213C2 17.2426 2 15.8284 2 13V11C2 8.17157 2 6.75736 2.87868 5.87868C3.75736 5 5.17157 5 8 5ZM6 10C6.55228 10 7 9.55228 7 9C7 8.44772 6.55228 8 6 8C5.44772 8 5 8.44772 5 9C5 9.55228 5.44772 10 6 10ZM6 13C6.55228 13 7 12.5523 7 12C7 11.4477 6.55228 11 6 11C5.44772 11 5 11.4477 5 12C5 12.5523 5.44772 13 6 13ZM9 13C9.55228 13 10 12.5523 10 12C10 11.4477 9.55228 11 9 11C8.44772 11 8 11.4477 8 12C8 12.5523 8.44772 13 9 13ZM9 10C9.55228 10 10 9.55228 10 9C10 8.44772 9.55228 8 9 8C8.44772 8 8 8.44772 8 9C8 9.55228 8.44772 10 9 10ZM12 10C12.5523 10 13 9.55228 13 9C13 8.44772 12.5523 8 12 8C11.4477 8 11 8.44772 11 9C11 9.55228 11.4477 10 12 10ZM12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13ZM15 10C15.5523 10 16 9.55228 16 9C16 8.44772 15.5523 8 15 8C14.4477 8 14 8.44772 14 9C14 9.55228 14.4477 10 15 10ZM15 13C15.5523 13 16 12.5523 16 12C16 11.4477 15.5523 11 15 11C14.4477 11 14 11.4477 14 12C14 12.5523 14.4477 13 15 13ZM18 10C18.5523 10 19 9.55228 19 9C19 8.44772 18.5523 8 18 8C17.4477 8 17 8.44772 17 9C17 9.55228 17.4477 10 18 10ZM18 13C18.5523 13 19 12.5523 19 12C19 11.4477 18.5523 11 18 11C17.4477 11 17 11.4477 17 12C17 12.5523 17.4477 13 18 13ZM17.75 16C17.75 16.4142 17.4142 16.75 17 16.75H7C6.58579 16.75 6.25 16.4142 6.25 16C6.25 15.5858 6.58579 15.25 7 15.25H17C17.4142 15.25 17.75 15.5858 17.75 16Z" fill="#000"></path> </g></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

142
utils/brightness.ts Normal file
View File

@@ -0,0 +1,142 @@
import GObject, { register, getter, setter } from "ags/gobject";
import { monitorFile, readFileAsync } from "ags/file";
import { exec, execAsync } from "ags/process";
import { onCleanup } from "ags";
const get = (args: string) => Number(exec(`brightnessctl ${args}`));
const screen = exec(`bash -c "ls -w1 /sys/class/backlight | head -1"`);
const kbd = exec(`bash -c "ls -w1 /sys/class/leds | head -1"`);
@register({ GTypeName: "Brightness" })
export default class Brightness extends GObject.Object {
// @ts-ignore - notify method is provided by GObject at runtime
notify(property: string): void;
static instance: Brightness;
static get_default() {
if (!this.instance) this.instance = new Brightness();
return this.instance;
}
#hasBacklight = false;
#kbdMax = 0;
#kbd = 0;
#screenMax = 0;
#screen = 0;
#screenMonitor: any = null;
#kbdMonitor: any = null;
#settingScreen = false;
#settingKbd = false;
constructor() {
super();
this.initializeHardware();
}
private initializeHardware(): void {
this.#hasBacklight = exec(`bash -c "ls /sys/class/backlight"`).length > 0;
if (!this.#hasBacklight) return;
try {
this.#kbdMax = get(`--device ${kbd} max`);
this.#kbd = get(`--device ${kbd} get`);
this.#screenMax = get("max");
this.#screen = get("get") / this.#screenMax;
this.#screenMonitor = monitorFile(
`/sys/class/backlight/${screen}/brightness`,
async (f) => {
if (this.#settingScreen) return;
try {
const v = await readFileAsync(f);
const newValue = Number(v) / this.#screenMax;
if (Math.abs(this.#screen - newValue) > 0.001) {
this.#screen = newValue;
this.notify("screen");
}
} catch (error) {
console.error("Error reading screen brightness:", error);
}
},
);
this.#kbdMonitor = monitorFile(
`/sys/class/leds/${kbd}/brightness`,
async (f) => {
if (this.#settingKbd) return;
try {
const v = await readFileAsync(f);
const newValue = Number(v);
if (this.#kbd !== newValue) {
this.#kbd = newValue;
this.notify("kbd");
}
} catch (error) {
console.error("Error reading keyboard brightness:", error);
}
},
);
onCleanup(() => {
this.#screenMonitor?.cancel?.();
this.#kbdMonitor?.cancel?.();
});
} catch (error) {
console.error("Error initializing brightness controls:", error);
this.#hasBacklight = false;
}
}
@getter(Boolean)
get hasBacklight(): boolean {
return this.#hasBacklight;
}
@getter(Number)
get kbd(): number {
return this.#kbd;
}
@setter(Number)
set kbd(value: number) {
if (!this.#hasBacklight || value < 0 || value > this.#kbdMax) return;
this.#settingKbd = true;
execAsync(`brightnessctl -d ${kbd} s ${value} -q`)
.then(() => {
setTimeout(() => (this.#settingKbd = false), 100);
})
.catch((error) => {
console.error("Error setting keyboard brightness:", error);
this.#settingKbd = false;
});
}
@getter(Number)
get screen(): number {
return this.#screen;
}
@setter(Number)
set screen(percent: number) {
if (!this.#hasBacklight) return;
percent = Math.max(0.01, Math.min(1, percent)); // Minimum 1% to avoid complete darkness
this.#screen = percent;
this.notify("screen");
this.#settingScreen = true;
execAsync(`brightnessctl -d ${screen} set ${Math.floor(percent * 100)}% -q`)
.then(() => {
setTimeout(() => (this.#settingScreen = false), 200);
})
.catch((error) => {
console.error("Error setting screen brightness:", error);
this.#screen = get("get") / this.#screenMax;
this.notify("screen");
this.#settingScreen = false;
});
}
}

View File

@@ -1,12 +1,14 @@
import { createBinding } from "ags";
import { Gdk, Gtk } from "ags/gtk4";
import AstalWp from "gi://AstalWp";
import Brightness from "../../utils/brightness";
export const SlidingControls = () => {
const { VERTICAL } = Gtk.Orientation;
const { defaultSpeaker: speaker, defaultMicrophone: microphone } =
AstalWp.get_default()!;
const brightness = Brightness.get_default();
const speakerIsMuted = createBinding(speaker, "mute");
@@ -36,6 +38,33 @@ export const SlidingControls = () => {
cursor={Gdk.Cursor.new_from_name("pointer", null)}
/>
</box>
<box cssClasses={["volume"]} spacing={20}>
<image iconName="fa-brightness-symbolic" />
<slider
hexpand
onChangeValue={(self) => {
brightness.screen = self.value;
}}
value={createBinding(brightness, "screen")}
cursor={Gdk.Cursor.new_from_name("pointer", null)}
/>
</box>
<box cssClasses={["volume"]} spacing={20}>
<image iconName="fa-keyboard-symbolic" />
<slider
hexpand
min={0}
max={3}
step={1}
onChangeValue={(self) => {
brightness.kbd = self.value;
}}
value={createBinding(brightness, "kbd")}
cursor={Gdk.Cursor.new_from_name("pointer", null)}
/>
</box>
</box>
);
};