import { execAsync, Gio, GLib, GObject, monitorFile, property, register } 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; @property(Boolean) public get splash() { return this.#splash; } public set splash(showSplash: boolean) { this.#splash = showSplash; this.notify("splash"); this.writeChanges(); } @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) => this.#wallpaper = wall).catch(r => { throw new Error(`Wallpaper: Couldn't get wallpaper. Stderr: ${r}`) }); this.#monitor = monitorFile(this.#hyprpaperFile.get_path()!, async (_, event) => { if(event == Gio.FileMonitorEvent.CHANGED) { const [loaded, , content ] = this.#hyprpaperFile.load_contents(null); if(loaded) for(const line of content.split('\n')) { const [ key, value ] = line.split('='); if(key.trim().startsWith('#')) continue; switch(key) { case "splash": this.#splash = /(yes|true|on)/.test(value.trim()) ? true : false; break; case "wallpaper": this.#wallpaper = value; break; } }; } }); } vfunc_dispose(): void { this.#monitor.cancel(); } public static getDefault(): Wallpaper { if(!this.instance) this.instance = new Wallpaper(); return this.instance; } private async writeChanges(): Promise { if(!this.#wallpaper) return; const hyprpaperFile = Gio.File.new_for_path(`${GLib.get_user_config_dir()}/hypr/hyprpaper.conf`); hyprpaperFile.create(Gio.FileCreateFlags.REPLACE_DESTINATION, null); return hyprpaperFile.replace_contents_async(`# This file is automatically generated when choosing a new wallpaper in colorshell preload = ${this.#wallpaper} splash = ${this.#splash} wallpaper = , ${this.#wallpaper}`.split('\n').map(str => str.trimStart()).join('\n'), null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null ).then(() => {}).catch(r => { throw new Error(`Wallpaper: Couldn't write hyprpaper file. Stderr: ${r}`); }); } public async getWallpaper(): Promise { return await execAsync("hyprctl hyprpaper listactive | tail -n 1").then(stdout => { const loaded: (string|undefined) = stdout.split('=')[1]?.trim(); if(!loaded) throw new Error(`Wallpaper: Couldn't get wallpaper. There are no loaded wallpaper(s)`); return loaded; }).catch(stderr => { throw new Error(`Wallpaper: Couldn't get wallpaper. Stderr: ${stderr}`); }); } public async reloadColors(): Promise { return await execAsync(`wal -t --cols16 darken -i "${this.#wallpaper}"`).then(() => { }).catch(r => { throw new Error(`Wallpaper: Couldn't update shell colors. Stderr: ${r}`); }); } public async setWallpaper(path: string|Gio.File): Promise { return await execAsync("hyprctl hyprpaper unload all").then(async () => await execAsync(`hyprctl hyprpaper preload ${path}`).then(async () => await execAsync(`hyprctl hyprpaper wallpaper ${path}`).then(async () => { this.#wallpaper = (typeof path === "string") ? path : path.get_path()!; this.reloadColors(); this.writeChanges().catch(r => { throw new Error(`Wallpaper: Couldn't write changes to hyprpaper config file. Stderr: ${r}`); }); }).catch(r => { throw new Error(`Wallpaper: Couldn't set wallpaper. Stderr: ${r}`); }) ).catch(r => { throw new Error(`Wallpaper: Couldn't preload image. Stderr: ${r}`); }) ).catch(r => { throw new Error(`Wallpaper: Couldn't unload images from memory. Stderr: ${r}`); }); } public async pickWallpaper(): Promise { return execAsync(`zenity --file-selection`).then(wall => { if(!wall.trim()) return undefined; this.setWallpaper(wall).catch(r => { throw new Error(`Wallpaper: An error occurred in \`setWallpaper()\`. Stderr: ${r}`); }); return wall; }).catch(r => { throw new Error(`Wallpaper: Couldn't pick wallpaper, is zenity installed? Stderr: ${r}`); }); } }