✨ feat(wallpaper, config): add config properties to configure wallpaper position and color generation modes
This commit is contained in:
+34
-11
@@ -1,47 +1,70 @@
|
|||||||
import { Config } from "./modules/config";
|
import { Config } from "./modules/config";
|
||||||
|
import { WallpaperPositioning, WalMode } from "./modules/wallpaper";
|
||||||
|
|
||||||
import GLib from "gi://GLib?version=2.0";
|
import GLib from "gi://GLib?version=2.0";
|
||||||
|
|
||||||
|
|
||||||
const generalConfigDefaults = {
|
const generalConfigDefaults = {
|
||||||
notifications: {
|
notifications: {
|
||||||
|
/** low-priority notification timeout
|
||||||
|
* @default 4000 */
|
||||||
timeout_low: 4000,
|
timeout_low: 4000,
|
||||||
|
/** regular notification timeout
|
||||||
|
* @default 6000 */
|
||||||
timeout_normal: 6000,
|
timeout_normal: 6000,
|
||||||
|
/** critical/very important notification timeout
|
||||||
|
* @default 0 */
|
||||||
timeout_critical: 0,
|
timeout_critical: 0,
|
||||||
/** notification popup horizontal position. can be "left" or "right"
|
/** notification popup horizontal position. can be "left" or "right"
|
||||||
* @default "right" */
|
* @default "right" */
|
||||||
position_h: "right",
|
position_h: "right",
|
||||||
/** vertical notification popup position. can be "top" or "bottom"
|
/** vertical notification popup position. can be "top" or "bottom"
|
||||||
* @default "top" */
|
* @default "top" */
|
||||||
position_v: "top"
|
position_v: "top",
|
||||||
|
/** dismisses notification popup when unhovered after hovering
|
||||||
|
* @default false */
|
||||||
|
dismiss_after_unhover: false
|
||||||
},
|
},
|
||||||
|
|
||||||
night_light: {
|
night_light: {
|
||||||
/** whether to save night light values to disk */
|
/** whether to save night light/gamma filter values to disk when clicking
|
||||||
|
* on power/session actions(suspend, log out, power off, reboot)
|
||||||
|
* @default true */
|
||||||
save_on_shutdown: true
|
save_on_shutdown: true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
wallpaper: {
|
||||||
|
/** wallpaper positioning mode (hyprpaper) */
|
||||||
|
positioning: "cover" satisfies WallpaperPositioning,
|
||||||
|
/** color generation mode.
|
||||||
|
* darken: picks darker colors; lighten: picks brighter colors */
|
||||||
|
color_mode: "darken" satisfies WalMode
|
||||||
|
},
|
||||||
|
|
||||||
workspaces: {
|
workspaces: {
|
||||||
/** breaks `enable_helper`, makes all workspaces show their respective ID
|
/** breaks `enable_helper`, makes all workspaces show their respective ID
|
||||||
* by default */
|
* by default */
|
||||||
always_show_id: false,
|
always_show_id: false,
|
||||||
/** this is the function that shows the Workspace's IDs
|
/** this is the function that shows the Workspace's IDs
|
||||||
* around the current workspace if one breaks the crescent order.
|
* around the current workspace if one breaks the crescent order.
|
||||||
* It basically helps keyboard navigation between workspaces.
|
* It basically helps keyboard navigation between workspaces.
|
||||||
* ---
|
* ---
|
||||||
* Example: 1(empty, current, shows ID), 2(empty, does not appear(makes
|
* Example: 1(empty, current, shows ID), 2(empty, does not appear(makes
|
||||||
* the previous not to be in a crescent order)), 3(not empty, shows ID) */
|
* the previous not to be in a crescent order)), 3(not empty, shows ID) */
|
||||||
enable_helper: true,
|
enable_helper: true,
|
||||||
/** hide workspace indicator if there's only one active workspace */
|
/** hide workspace indicator if there's only one active workspace */
|
||||||
hide_if_single: false
|
hide_if_single: false
|
||||||
},
|
},
|
||||||
|
|
||||||
clock: {
|
clock: {
|
||||||
/** use the same format as gnu's `date` command */
|
/** use the same format as gnu's `date` command
|
||||||
|
* @default "%A %d, %H:%M" // -> "tuesday, 11, 15:44" */
|
||||||
date_format: "%A %d, %H:%M"
|
date_format: "%A %d, %H:%M"
|
||||||
},
|
},
|
||||||
|
|
||||||
misc: {
|
misc: {
|
||||||
|
/** plays a system-bell sound effect using canberra-gtk-play on volume change
|
||||||
|
* @default true */
|
||||||
play_bell_on_volume_change: true
|
play_bell_on_volume_change: true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+107
-89
@@ -1,12 +1,12 @@
|
|||||||
import { execAsync } from "ags/process";
|
import { execAsync } from "ags/process";
|
||||||
import { timeout } from "ags/time";
|
import { readFile } from "ags/file";
|
||||||
import { monitorFile, readFile } from "ags/file";
|
import GObject, { register, getter, gtype, property, setter } from "ags/gobject";
|
||||||
import GObject, { register, getter } from "ags/gobject";
|
|
||||||
|
|
||||||
import AstalIO from "gi://AstalIO";
|
|
||||||
import Gio from "gi://Gio?version=2.0";
|
import Gio from "gi://Gio?version=2.0";
|
||||||
import GLib from "gi://GLib?version=2.0";
|
import GLib from "gi://GLib?version=2.0";
|
||||||
import { decoder, encoder } from "./utils";
|
import { createSubscription, encoder } from "./utils";
|
||||||
|
import { Notifications } from "./notifications";
|
||||||
|
import { generalConfig } from "../config";
|
||||||
|
|
||||||
|
|
||||||
export { Wallpaper };
|
export { Wallpaper };
|
||||||
@@ -40,15 +40,19 @@ type WalData = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type WalMode = "darken"|"lighten";
|
||||||
|
|
||||||
|
/** wallpaper tiling mode */
|
||||||
|
export type WallpaperPositioning = "contain"|"tile"|"cover";
|
||||||
|
|
||||||
@register({ GTypeName: "Wallpaper" })
|
@register({ GTypeName: "Wallpaper" })
|
||||||
class Wallpaper extends GObject.Object {
|
class Wallpaper extends GObject.Object {
|
||||||
private static instance: Wallpaper;
|
private static instance: Wallpaper;
|
||||||
#wallpaper: (string|undefined);
|
#wallpaper: (string|undefined);
|
||||||
#splash: boolean = true;
|
#splash: boolean = true;
|
||||||
#monitor: Gio.FileMonitor;
|
|
||||||
#hyprpaperFile: Gio.File;
|
#hyprpaperFile: Gio.File;
|
||||||
#wallpapersPath: string;
|
#wallpapersPath: string;
|
||||||
#ignoreWatch: boolean = false;
|
|
||||||
|
|
||||||
@getter(Boolean)
|
@getter(Boolean)
|
||||||
public get splash() { return this.#splash; }
|
public get splash() { return this.#splash; }
|
||||||
@@ -57,14 +61,21 @@ class Wallpaper extends GObject.Object {
|
|||||||
this.notify("splash");
|
this.notify("splash");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** current wallpaper's complete path
|
/** current wallpaper's complete path. can be an empty string if undefined */
|
||||||
* can be an empty string if undefined */
|
|
||||||
@getter(String)
|
@getter(String)
|
||||||
public get wallpaper() { return this.#wallpaper ?? ""; }
|
get wallpaper() { return this.#wallpaper ?? ""; }
|
||||||
public set wallpaper(newValue: string) { this.setWallpaper(newValue); }
|
|
||||||
|
@setter(String)
|
||||||
|
set wallpaper(newValue: string) { this.setWallpaper(newValue); }
|
||||||
|
|
||||||
public get wallpapersPath() { return this.#wallpapersPath; }
|
public get wallpapersPath() { return this.#wallpapersPath; }
|
||||||
|
|
||||||
|
@property(gtype<WallpaperMode>(String))
|
||||||
|
positioning: WallpaperMode = "cover";
|
||||||
|
|
||||||
|
@property(gtype<WalMode>(String))
|
||||||
|
colorMode: WalMode = "darken";
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -78,58 +89,52 @@ class Wallpaper extends GObject.Object {
|
|||||||
if(wall?.trim()) this.#wallpaper = wall.trim();
|
if(wall?.trim()) this.#wallpaper = wall.trim();
|
||||||
});
|
});
|
||||||
|
|
||||||
let tmeout: (AstalIO.Time|undefined) = undefined;
|
createSubscription(
|
||||||
|
generalConfig.bindProperty("wallpaper.color_mode", "string"),
|
||||||
|
() => {
|
||||||
|
const mode = generalConfig.getProperty("wallpaper.color_mode", "string");
|
||||||
|
if(!mode || (mode !== "darken" && mode !== "lighten")) {
|
||||||
|
Notifications.getDefault().sendNotification({
|
||||||
|
appName: "colorshell",
|
||||||
|
summary: "Couldn't update color mode",
|
||||||
|
body: "Invalid mode. Possible values are: \"darken\" or \"lighten\""
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
this.#monitor = monitorFile(this.#hyprpaperFile.get_path()!, (_, event) => {
|
this.colorMode = mode as WalMode;
|
||||||
if(event !== Gio.FileMonitorEvent.CHANGED && event !== Gio.FileMonitorEvent.CREATED &&
|
this.reloadColors();
|
||||||
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);
|
createSubscription(
|
||||||
if(!loaded)
|
generalConfig.bindProperty("wallpaper.positioning", "string"),
|
||||||
console.error("Wallpaper: Couldn't read changes inside the hyprpaper file!");
|
() => {
|
||||||
|
const positioning = generalConfig
|
||||||
|
.getProperty("wallpaper.positioning", "string") as WallpaperPositioning;
|
||||||
|
|
||||||
const content = decoder.decode(text);
|
if(!positioning || (positioning !== "contain" &&
|
||||||
|
positioning !== "cover" &&
|
||||||
|
positioning !== "tile")) {
|
||||||
|
|
||||||
if(content) {
|
Notifications.getDefault().sendNotification({
|
||||||
let setWall: boolean = true;
|
appName: "colorshell",
|
||||||
|
summary: "Couldn't update wallpaper position",
|
||||||
for(const line of content.split('\n')) {
|
body: "Invalid position value. Possible values are: \"cover\", \"contain\" or \"tile\""
|
||||||
if(line.trim().startsWith('#'))
|
});
|
||||||
continue;
|
return;
|
||||||
|
|
||||||
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.positioning = positioning;
|
||||||
this.#monitor.cancel();
|
this.reloadWallpaper().catch((e: Error) =>
|
||||||
|
Notifications.getDefault().sendNotification({
|
||||||
|
appName: "colorshell",
|
||||||
|
summary: "Couldn't update wallpaper position",
|
||||||
|
body: `An error occurred while updating wallpaper's position: ${e.message}`
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getDefault(): Wallpaper {
|
public static getDefault(): Wallpaper {
|
||||||
@@ -140,29 +145,28 @@ class Wallpaper extends GObject.Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private writeChanges(): void {
|
private writeChanges(): void {
|
||||||
this.#ignoreWatch = true; // tell monitor to ignore file replace
|
|
||||||
this.#hyprpaperFile.replace_async(null, false,
|
this.#hyprpaperFile.replace_async(null, false,
|
||||||
Gio.FileCreateFlags.REPLACE_DESTINATION,
|
Gio.FileCreateFlags.REPLACE_DESTINATION,
|
||||||
GLib.PRIORITY_DEFAULT, null, (_, result) => {
|
GLib.PRIORITY_DEFAULT, null, (_, result) => {
|
||||||
const res = this.#hyprpaperFile.replace_finish(result);
|
const res = this.#hyprpaperFile.replace_finish(result);
|
||||||
if(res) {
|
if(!res) {
|
||||||
// success
|
console.error(`Wallpaper: an error occurred when trying to replace the hyprpaper file`);
|
||||||
this.#ignoreWatch = true; // tell monitor to ignore this change
|
|
||||||
res.write_bytes_async(encoder.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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(`Wallpaper: an error occurred when trying to replace the hyprpaper file`);
|
// success
|
||||||
|
res.write_bytes_async(encoder.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;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -173,42 +177,56 @@ class Wallpaper extends GObject.Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getWallpaper(): Promise<string|undefined> {
|
public async getWallpaper(): Promise<string|undefined> {
|
||||||
return await execAsync("sh -c \"hyprctl hyprpaper listactive | tail -n 1\"").then(stdout => {
|
return await execAsync("hyprctl hyprpaper listactive").then(stdout => {
|
||||||
const loaded: (string|undefined) = stdout.split('=')[1]?.trim();
|
const lineSplit = stdout.split('\n');
|
||||||
|
stdout = lineSplit[lineSplit.length - 1];
|
||||||
|
|
||||||
|
const loaded = stdout.split('=')[1]?.trim();
|
||||||
|
|
||||||
if(!loaded)
|
if(!loaded)
|
||||||
console.warn(`Wallpaper: Couldn't get wallpaper. There is(are) no loaded wallpaper(s)`);
|
console.warn(`Wallpaper: Couldn't get wallpaper. There is(are) no loaded wallpaper(s)`);
|
||||||
|
|
||||||
return loaded;
|
return loaded;
|
||||||
}).catch((err: Gio.IOErrorEnum) => {
|
}).catch((err: Error) => {
|
||||||
console.error(`Wallpaper: Couldn't get wallpaper. Stderr: \n${err.message ? `${err.message} /` : ""} Stack: \n ${err.stack}`);
|
console.error(`Wallpaper: Couldn't get wallpaper. Stderr: \n${err.message}`);
|
||||||
return undefined;
|
return undefined;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public reloadColors(): void {
|
public reloadColors(): void {
|
||||||
execAsync(`wal -t --cols16 darken -i "${this.#wallpaper}"`).then(() => {
|
execAsync(`wal -t --cols16 ${this.colorMode} -i "${this.#wallpaper}"`).then(() => {
|
||||||
console.log("Wallpaper: reloaded shell colors");
|
console.log("Wallpaper: reloaded shell colors");
|
||||||
}).catch(r => {
|
}).catch((e: Error) => {
|
||||||
console.error(`Wallpaper: Couldn't update shell colors. Stderr: ${r}`);
|
console.error(`Wallpaper: Couldn't update shell colors. Stderr: ${e.message}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async reloadWallpaper(write: boolean = true): Promise<void> {
|
||||||
|
if(this.wallpaper.trim() === "")
|
||||||
|
return;
|
||||||
|
|
||||||
|
await execAsync(`hyprctl hyprpaper wallpaper \", ${this.positioning}:${this.wallpaper}\"`);
|
||||||
|
this.reloadColors();
|
||||||
|
write && this.writeChanges();
|
||||||
|
}
|
||||||
|
|
||||||
public setWallpaper(path: string|Gio.File, write: boolean = true): void {
|
public setWallpaper(path: string|Gio.File, write: boolean = true): void {
|
||||||
|
path = typeof path === "string" ? path : path.peek_path()!;
|
||||||
|
|
||||||
execAsync("hyprctl hyprpaper unload all").then(() =>
|
execAsync("hyprctl hyprpaper unload all").then(() =>
|
||||||
execAsync(`hyprctl hyprpaper preload ${path}`).then(() =>
|
execAsync(`hyprctl hyprpaper preload ${path}`).then(() =>
|
||||||
execAsync(`hyprctl hyprpaper wallpaper ${path}`).then(() => {
|
execAsync(`hyprctl hyprpaper wallpaper \", ${this.positioning}:${path}\"`).then(() => {
|
||||||
this.#wallpaper = (typeof path === "string") ? path : path.get_path()!;
|
this.#wallpaper = path;
|
||||||
this.reloadColors();
|
this.reloadColors();
|
||||||
write && this.writeChanges();
|
write && this.writeChanges();
|
||||||
}).catch(r => {
|
}).catch((e: Error) => {
|
||||||
console.error(`Wallpaper: Couldn't set wallpaper. Stderr: ${r}`);
|
console.error(`Wallpaper: Couldn't set wallpaper. Stderr: ${e.message}`);
|
||||||
})
|
})
|
||||||
).catch(r => {
|
).catch((e: Error) => {
|
||||||
console.error(`Wallpaper: Couldn't preload image. Stderr: ${r}`);
|
console.error(`Wallpaper: Couldn't preload image. Stderr: ${e.message}`);
|
||||||
})
|
})
|
||||||
).catch(r => {
|
).catch((e: Error) => {
|
||||||
console.error(`Wallpaper: Couldn't unload images from memory. Stderr: ${r}`);
|
console.error(`Wallpaper: Couldn't unload images from memory. Stderr: ${e.message}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,8 +236,8 @@ class Wallpaper extends GObject.Object {
|
|||||||
|
|
||||||
this.setWallpaper(wall);
|
this.setWallpaper(wall);
|
||||||
return wall;
|
return wall;
|
||||||
}).catch(r => {
|
}).catch((e: Error) => {
|
||||||
console.error(`Wallpaper: Couldn't pick wallpaper, is \`zenity\` installed? Stderr: ${r}`);
|
console.error(`Wallpaper: Couldn't pick wallpaper, is \`zenity\` installed? Stderr: ${e.message}`);
|
||||||
return undefined;
|
return undefined;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user