🔧 chore(modules/backlight): better implementation
This commit is contained in:
+108
-102
@@ -1,92 +1,46 @@
|
|||||||
import { monitorFile, readFile, writeFile } from "ags/file";
|
import { monitorFile, readFile } from "ags/file";
|
||||||
import GObject, { getter, ParamSpec, register, setter, signal } from "ags/gobject";
|
import { exec } from "ags/process";
|
||||||
|
import GObject, { getter, ParamSpec, setter, signal } from "ags/gobject";
|
||||||
|
|
||||||
import Gio from "gi://Gio?version=2.0";
|
import Gio from "gi://Gio?version=2.0";
|
||||||
|
|
||||||
|
|
||||||
export { Backlight };
|
export namespace Backlights {
|
||||||
@register({ GTypeName: "Backlight" })
|
|
||||||
class Backlight extends GObject.Object {
|
|
||||||
|
|
||||||
private static _backlights: Array<Backlight> = [];
|
let instance: Backlights;
|
||||||
public static get backlights() {
|
|
||||||
return this._backlights;
|
|
||||||
};
|
|
||||||
|
|
||||||
private static default: Backlight;
|
export function getDefault(): Backlights {
|
||||||
|
if(!instance)
|
||||||
|
instance = new Backlights;
|
||||||
|
|
||||||
readonly #name: string;
|
return instance;
|
||||||
#path: string;
|
|
||||||
#maxBrightness: number;
|
|
||||||
#brightness: number;
|
|
||||||
#available: boolean = true;
|
|
||||||
#monitor: Gio.FileMonitor;
|
|
||||||
|
|
||||||
@signal(Number) brightnessChanged(_: number): void {};
|
|
||||||
|
|
||||||
@getter(String)
|
|
||||||
get name() { return this.#name; }
|
|
||||||
|
|
||||||
@getter(String)
|
|
||||||
get path() { return this.#path; }
|
|
||||||
|
|
||||||
@getter(GObject.Object as unknown as ParamSpec<Backlight>)
|
|
||||||
get default() { return Backlight.default; }
|
|
||||||
|
|
||||||
@getter(Object as unknown as ParamSpec<Array<Backlight>>)
|
|
||||||
get backlights() { return Backlight.backlights; }
|
|
||||||
|
|
||||||
@getter(Boolean)
|
|
||||||
get isDefault() { return this.path === this.default?.path; }
|
|
||||||
|
|
||||||
@getter(Number)
|
|
||||||
get brightness() { return this.#brightness; };
|
|
||||||
@setter(Number)
|
|
||||||
set brightness(level: number) {
|
|
||||||
if(!this.writeBrightness(level)) return;
|
|
||||||
|
|
||||||
this.#brightness = level;
|
|
||||||
this.notify("brightness");
|
|
||||||
this.emit("brightness-changed", level);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@getter(Number)
|
export class Backlights extends GObject.Object {
|
||||||
get maxBrightness() { return this.#maxBrightness;};
|
static {
|
||||||
|
GObject.registerClass({
|
||||||
|
GTypeName: "Backlights"
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
public static $gtype: GObject.GType<Backlights>;
|
||||||
|
|
||||||
|
|
||||||
|
#backlights: Array<Backlight> = [];
|
||||||
|
#default: Backlight|null = null;
|
||||||
|
#available: boolean = false;
|
||||||
|
|
||||||
|
|
||||||
|
@getter(Array as unknown as ParamSpec<Array<Backlight>>)
|
||||||
|
get backlights() { return this.#backlights; }
|
||||||
|
|
||||||
|
@getter(Backlight as unknown as ParamSpec<Backlight|null>)
|
||||||
|
get default() { return this.#default; }
|
||||||
|
|
||||||
|
/** true if there are any backlights available */
|
||||||
@getter(Boolean)
|
@getter(Boolean)
|
||||||
get available() { return this.#available; }
|
get available() { return this.#available; }
|
||||||
|
|
||||||
|
public scan(): Array<Backlight> {
|
||||||
declare $signals: GObject.Object.SignalSignatures & {
|
|
||||||
"brightness-changed": (value: number) => void
|
|
||||||
};
|
|
||||||
|
|
||||||
public static setDefault(backlight: Backlight): void {
|
|
||||||
const prev = this.default;
|
|
||||||
this.default = backlight;
|
|
||||||
|
|
||||||
prev && prev.notify("is-default");
|
|
||||||
backlight.notify("is-default");
|
|
||||||
this.backlights.forEach(bk => bk.notify("default"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static amount(): number {
|
|
||||||
const dir = Gio.File.new_for_path(`/sys/class/backlight`);
|
|
||||||
let num: number = 0,
|
|
||||||
fileEnum: Gio.FileEnumerator;
|
|
||||||
|
|
||||||
try {
|
|
||||||
fileEnum = dir.enumerate_children("standard::*", Gio.FileQueryInfoFlags.NONE, null);
|
|
||||||
|
|
||||||
for(const _ of fileEnum)
|
|
||||||
num++;
|
|
||||||
} catch(_) {
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static scan(): Array<Backlight> {
|
|
||||||
const dir = Gio.File.new_for_path(`/sys/class/backlight`),
|
const dir = Gio.File.new_for_path(`/sys/class/backlight`),
|
||||||
backlights: Array<Backlight> = [];
|
backlights: Array<Backlight> = [];
|
||||||
|
|
||||||
@@ -103,21 +57,89 @@ class Backlight extends GObject.Object {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
Backlight._backlights = backlights;
|
if(backlights.length < 1 && this.#available === true) {
|
||||||
backlights.forEach(bk => bk.notify("backlights"));
|
this.#available = false;
|
||||||
|
this.notify("available");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.#backlights.length < 1 && backlights.length > 0) {
|
||||||
|
this.#available = true;
|
||||||
|
this.notify("available");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#backlights = backlights;
|
||||||
|
this.notify("backlights");
|
||||||
|
|
||||||
return backlights;
|
return backlights;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setDefault(bk: Backlight): void {
|
||||||
|
this.#default = bk;
|
||||||
|
this.notify("default");
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(scan: boolean = true) {
|
||||||
|
super();
|
||||||
|
scan && this.scan();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Backlight extends GObject.Object {
|
||||||
|
static {
|
||||||
|
GObject.registerClass({
|
||||||
|
GTypeName: "Backlight"
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
public static $gtype: GObject.GType<Backlight>;
|
||||||
|
|
||||||
|
readonly #name: string;
|
||||||
|
#path: string;
|
||||||
|
#maxBrightness: number;
|
||||||
|
#brightness: number;
|
||||||
|
#monitor: Gio.FileMonitor;
|
||||||
|
#conn: number;
|
||||||
|
|
||||||
|
@signal(Number) brightnessChanged(_: number): void {};
|
||||||
|
|
||||||
|
@getter(String)
|
||||||
|
get name() { return this.#name; }
|
||||||
|
|
||||||
|
@getter(String)
|
||||||
|
get path() { return this.#path; }
|
||||||
|
|
||||||
|
@getter(Boolean)
|
||||||
|
get isDefault() { return this.path === getDefault().default?.path; }
|
||||||
|
|
||||||
|
@getter(Number)
|
||||||
|
get brightness() { return this.#brightness; };
|
||||||
|
@setter(Number)
|
||||||
|
set brightness(level: number) {
|
||||||
|
if(!this.writeBrightness(level)) return;
|
||||||
|
|
||||||
|
this.#brightness = level;
|
||||||
|
this.notify("brightness");
|
||||||
|
this.emit("brightness-changed", level);
|
||||||
|
}
|
||||||
|
|
||||||
|
@getter(Number)
|
||||||
|
get maxBrightness() { return this.#maxBrightness;};
|
||||||
|
|
||||||
|
|
||||||
|
declare $signals: GObject.Object.SignalSignatures & {
|
||||||
|
"brightness-changed": (value: number) => void
|
||||||
|
};
|
||||||
|
|
||||||
// intel_backlight is mostly the default on laptops
|
// intel_backlight is mostly the default on laptops
|
||||||
constructor(name: string = "intel_backlight") {
|
constructor(name: string = "intel_backlight") {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// check if backlight exists
|
// check if backlight exists
|
||||||
if(!Gio.File.new_for_path(`/sys/class/backlight/${name}/brightness`).query_exists(null)) {
|
if(!Gio.File.new_for_path(`/sys/class/backlight/${name}/brightness`).query_exists(null))
|
||||||
this.#available = false;
|
|
||||||
this.notify("available");
|
|
||||||
throw new Error(`Brightness: Couldn't find brightness for "${name}"`);
|
throw new Error(`Brightness: Couldn't find brightness for "${name}"`);
|
||||||
}
|
|
||||||
|
// notify :is-default on default backlight change
|
||||||
|
this.#conn = getDefault().connect("notify::default", () =>
|
||||||
|
this.notify("is-default"));
|
||||||
|
|
||||||
this.#name = name;
|
this.#name = name;
|
||||||
this.#path = `/sys/class/backlight/${name}`;
|
this.#path = `/sys/class/backlight/${name}`;
|
||||||
@@ -147,7 +169,7 @@ class Backlight extends GObject.Object {
|
|||||||
|
|
||||||
private writeBrightness(level: number): boolean {
|
private writeBrightness(level: number): boolean {
|
||||||
try {
|
try {
|
||||||
writeFile(`${this.#path}/brightness`, level.toString());
|
exec(`brightnessctl -d ${this.#name} s ${level}`);
|
||||||
return true;
|
return true;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error(`Backlight: Couldn't set brightness for "${this.#name}". Stderr: ${e}`);
|
console.error(`Backlight: Couldn't set brightness for "${this.#name}". Stderr: ${e}`);
|
||||||
@@ -156,26 +178,9 @@ class Backlight extends GObject.Object {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getDefault(): Backlight|null {
|
|
||||||
if(this.default)
|
|
||||||
return this.default;
|
|
||||||
|
|
||||||
if(this.backlights.length < 1)
|
|
||||||
this.scan();
|
|
||||||
|
|
||||||
const first = this.backlights[0];
|
|
||||||
if(first) {
|
|
||||||
try {
|
|
||||||
this.default = first;
|
|
||||||
return this.default;
|
|
||||||
} catch(_) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
vfunc_dispose(): void {
|
vfunc_dispose(): void {
|
||||||
this.#monitor.cancel();
|
this.#monitor.cancel();
|
||||||
|
getDefault().disconnect(this.#conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public emit<Signal extends keyof typeof this.$signals>(
|
public emit<Signal extends keyof typeof this.$signals>(
|
||||||
@@ -185,3 +190,4 @@ class Backlight extends GObject.Object {
|
|||||||
super.emit(signal, ...args);
|
super.emit(signal, ...args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Pages } from "./Pages";
|
|||||||
import { PageSound } from "./pages/Sound";
|
import { PageSound } from "./pages/Sound";
|
||||||
import { PageMicrophone } from "./pages/Microphone";
|
import { PageMicrophone } from "./pages/Microphone";
|
||||||
import { createBinding, With } from "ags";
|
import { createBinding, With } from "ags";
|
||||||
import { Backlight } from "../../modules/backlight";
|
import { Backlights } from "../../modules/backlight";
|
||||||
|
|
||||||
import AstalWp from "gi://AstalWp";
|
import AstalWp from "gi://AstalWp";
|
||||||
import { PageBacklight } from "./pages/Backlight";
|
import { PageBacklight } from "./pages/Backlight";
|
||||||
@@ -50,10 +50,9 @@ export function Sliders() {
|
|||||||
slidersPages?.toggle(PageMicrophone)} />
|
slidersPages?.toggle(PageMicrophone)} />
|
||||||
</Gtk.Box>}
|
</Gtk.Box>}
|
||||||
</With>
|
</With>
|
||||||
<Gtk.Box visible={Boolean(Backlight.getDefault())}>
|
<Gtk.Box visible={createBinding(Backlights.getDefault(), "available")}>
|
||||||
{Backlight.getDefault() &&
|
<With value={createBinding(Backlights.getDefault(), "default")}>
|
||||||
<With value={createBinding(Backlight.getDefault()!, "default")}>
|
{(bklight: Backlights.Backlight|null) => bklight &&
|
||||||
{(bklight: Backlight) => bklight &&
|
|
||||||
<Gtk.Box class={"backlight"} spacing={3}>
|
<Gtk.Box class={"backlight"} spacing={3}>
|
||||||
<Gtk.Button onClicked={() => {
|
<Gtk.Button onClicked={() => {
|
||||||
bklight.brightness = bklight.maxBrightness
|
bklight.brightness = bklight.maxBrightness
|
||||||
@@ -63,7 +62,7 @@ export function Sliders() {
|
|||||||
<Astal.Slider drawValue={false} hexpand value={createBinding(bklight, "brightness")}
|
<Astal.Slider drawValue={false} hexpand value={createBinding(bklight, "brightness")}
|
||||||
max={bklight.maxBrightness}
|
max={bklight.maxBrightness}
|
||||||
onChangeValue={(_, __, value) => {
|
onChangeValue={(_, __, value) => {
|
||||||
Backlight.getDefault()!.brightness = value
|
bklight.brightness = value
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Gtk.Button class={"more"} iconName={"go-next-symbolic"} onClicked={() =>
|
<Gtk.Button class={"more"} iconName={"go-next-symbolic"} onClicked={() =>
|
||||||
@@ -71,7 +70,6 @@ export function Sliders() {
|
|||||||
</Gtk.Box>
|
</Gtk.Box>
|
||||||
}
|
}
|
||||||
</With>
|
</With>
|
||||||
}
|
|
||||||
</Gtk.Box>
|
</Gtk.Box>
|
||||||
<Pages $={(self) => slidersPages = self} />
|
<Pages $={(self) => slidersPages = self} />
|
||||||
</Gtk.Box>
|
</Gtk.Box>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Astal, Gtk } from "ags/gtk4";
|
import { Astal, Gtk } from "ags/gtk4";
|
||||||
import { tr } from "../../../i18n/intl";
|
import { tr } from "../../../i18n/intl";
|
||||||
import { Backlight } from "../../../modules/backlight";
|
import { Backlights } from "../../../modules/backlight";
|
||||||
import { Page } from "./Page";
|
import { Page } from "./Page";
|
||||||
import { createBinding, With } from "ags";
|
import { createBinding, With } from "ags";
|
||||||
import { addSliderMarksFromMinMax } from "../../../modules/utils";
|
import { addSliderMarksFromMinMax } from "../../../modules/utils";
|
||||||
@@ -11,8 +11,8 @@ export const PageBacklight = new Page({
|
|||||||
title: tr("control_center.pages.backlight.title"),
|
title: tr("control_center.pages.backlight.title"),
|
||||||
description: tr("control_center.pages.backlight.description"),
|
description: tr("control_center.pages.backlight.description"),
|
||||||
content: () => (
|
content: () => (
|
||||||
<With value={createBinding(Backlight.getDefault()!, "backlights")}>
|
<With value={createBinding(Backlights.getDefault(), "backlights")}>
|
||||||
{(bklights: Array<Backlight>) => bklights.length > 0 &&
|
{(bklights: Array<Backlights.Backlight>) => bklights.length > 0 &&
|
||||||
<Gtk.Box orientation={Gtk.Orientation.VERTICAL} spacing={6}>
|
<Gtk.Box orientation={Gtk.Orientation.VERTICAL} spacing={6}>
|
||||||
{bklights.map((bklight, i) =>
|
{bklights.map((bklight, i) =>
|
||||||
<Gtk.Box class={"bklight"} orientation={Gtk.Orientation.VERTICAL}
|
<Gtk.Box class={"bklight"} orientation={Gtk.Orientation.VERTICAL}
|
||||||
@@ -36,6 +36,6 @@ export const PageBacklight = new Page({
|
|||||||
headerButtons: [{
|
headerButtons: [{
|
||||||
icon: "arrow-circular-top-right",
|
icon: "arrow-circular-top-right",
|
||||||
tooltipText: tr("control_center.pages.backlight.refresh"),
|
tooltipText: tr("control_center.pages.backlight.refresh"),
|
||||||
actionClicked: () => Backlight.scan()
|
actionClicked: () => Backlights.getDefault().scan()
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user