Files
colorshell/ags/scripts/wallpaper.ts
T

178 lines
6.6 KiB
TypeScript

import { AstalIO, execAsync, Gio, GLib, GObject, monitorFile, property, register, timeout } from "astal";
export { Wallpaper };
@register({ GTypeName: "Wallpaper" })
class Wallpaper extends GObject.Object {
private static instance: Wallpaper;
#wallpaper: (string|undefined);
#splash: boolean = true;
#monitor: Gio.FileMonitor;
#hyprpaperFile: Gio.File;
#wallpapersPath: string;
#ignoreWatch: boolean = false;
@property(Boolean)
public get splash() { return this.#splash; }
public set splash(showSplash: boolean) {
this.#splash = showSplash;
this.notify("splash");
}
@property(String)
public get wallpaper(): (string|undefined) { return this.#wallpaper; }
public set wallpaper(newValue: string) { this.setWallpaper(newValue); }
public get wallpapersPath() { return this.#wallpapersPath; }
constructor() {
super();
this.#wallpapersPath = GLib.getenv("WALLPAPERS") ?? `${GLib.get_home_dir()}/wallpapers`;
this.#hyprpaperFile = Gio.File.new_for_path(`${GLib.get_user_config_dir()}/hypr/hyprpaper.conf`);
this.getWallpaper().then((wall) => {
if(wall?.trim()) this.#wallpaper = wall.trim();
});
let tmeout: (AstalIO.Time|undefined) = undefined;
this.#monitor = monitorFile(this.#hyprpaperFile.get_path()!, (_, event) => {
if(event !== Gio.FileMonitorEvent.CHANGED && event !== Gio.FileMonitorEvent.CREATED &&
event !== Gio.FileMonitorEvent.MOVED_IN)
return;
if(tmeout) return;
else tmeout = timeout(1500, () => tmeout = undefined);
if(this.#ignoreWatch) {
this.#ignoreWatch = false;
return;
}
const [ loaded, text ] = this.#hyprpaperFile.load_contents(null);
if(!loaded)
console.error("Wallpaper: Couldn't read changes inside the hyprpaper file!");
const content = new TextDecoder().decode(text);
if(content) {
let setWall: boolean = true;
for(const line of content.split('\n')) {
if(line.trim().startsWith('#'))
continue;
const lineSplit = line.split('=');
const key = lineSplit[0].trim(),
value = lineSplit.filter((_, i) => i !== 0).join('=').trim();
switch(key) {
case "splash":
this.splash = (/(yes|true|on|enable|enabled|1).*/.test(value)) ? true : false;
break;
case "wallpaper":
if(this.#wallpaper !== value && setWall) {
this.setWallpaper(value, false);
setWall = false; // wallpaper already set
}
break;
}
}
}
});
}
vfunc_dispose(): void {
this.#monitor.cancel();
}
public static getDefault(): Wallpaper {
if(!this.instance)
this.instance = new Wallpaper();
return this.instance;
}
private writeChanges(): void {
this.#ignoreWatch = true; // tell monitor to ignore file replace
this.#hyprpaperFile.replace_async(null, false,
Gio.FileCreateFlags.REPLACE_DESTINATION,
GLib.PRIORITY_DEFAULT, null, (_, result) => {
const res = this.#hyprpaperFile.replace_finish(result);
if(res) {
// success
this.#ignoreWatch = true; // tell monitor to ignore this change
res.write_bytes_async(new TextEncoder().encode(`# This file was automatically generated by color-shell
preload = ${this.#wallpaper}
splash = ${this.#splash}
wallpaper = , ${this.#wallpaper}`.split('\n').map(str => str.trimStart()).join('\n')),
GLib.PRIORITY_DEFAULT, null, (_, asyncRes) => {
if(_!.write_finish(asyncRes)) res.flush(null);
res.close(null);
}
);
return;
}
console.error(`Wallpaper: an error occurred when trying to replace the hyprpaper file`);
}
);
}
public async getWallpaper(): Promise<string|undefined> {
return await execAsync("sh -c \"hyprctl hyprpaper listactive | tail -n 1\"").then(stdout => {
const loaded: (string|undefined) = stdout.split('=')[1]?.trim();
if(!loaded)
console.warn(`Wallpaper: Couldn't get wallpaper. There is(are) no loaded wallpaper(s)`);
return loaded;
}).catch((err: Gio.IOErrorEnum) => {
console.error(`Wallpaper: Couldn't get wallpaper. Stderr: \n${err.message ? `${err.message} /` : ""} Stack: \n ${err.stack}`);
return undefined;
});
}
public reloadColors(): void {
execAsync(`wal -t --cols16 darken -i "${this.#wallpaper}"`).then(() => {
console.log("Wallpaper: reloaded shell colors");
}).catch(r => {
console.error(`Wallpaper: Couldn't update shell colors. Stderr: ${r}`);
});
}
public setWallpaper(path: string|Gio.File, write: boolean = true): void {
execAsync("hyprctl hyprpaper unload all").then(() =>
execAsync(`hyprctl hyprpaper preload ${path}`).then(() =>
execAsync(`hyprctl hyprpaper wallpaper ${path}`).then(() => {
this.#wallpaper = (typeof path === "string") ? path : path.get_path()!;
this.reloadColors();
write && this.writeChanges();
}).catch(r => {
console.error(`Wallpaper: Couldn't set wallpaper. Stderr: ${r}`);
})
).catch(r => {
console.error(`Wallpaper: Couldn't preload image. Stderr: ${r}`);
})
).catch(r => {
console.error(`Wallpaper: Couldn't unload images from memory. Stderr: ${r}`);
});
}
public async pickWallpaper(): Promise<string|undefined> {
return (await execAsync(`zenity --file-selection`).then(wall => {
if(!wall.trim()) return undefined;
this.setWallpaper(wall);
return wall;
}).catch(r => {
console.error(`Wallpaper: Couldn't pick wallpaper, is \`zenity\` installed? Stderr: ${r}`);
return undefined;
}));
}
}