mirror of
https://github.com/zephrynis/nix-flake.git
synced 2026-02-18 12:11:54 +00:00
- Add complete colorshell v2.0.3 configuration to home/ags-config/ - Disable runner plugin and NightLight tile (incompatible with NixOS) - Customize SCSS with full opacity (no transparency) - Add dark pale blue color scheme in home/pywal-colors/ - Configure Papirus-Dark icon theme via home-manager - Make ~/.config/ags/ immutable and managed by Nix store - Auto-deploy pywal colors to ~/.cache/wal/colors.json All AGS configuration is now reproducible and version controlled.
345 lines
12 KiB
TypeScript
345 lines
12 KiB
TypeScript
// thanks Aylur!!
|
|
// import "/usr/share/ags/js/lib/overrides"; // Not needed in AGS v3 with NixOS
|
|
|
|
// Define build-time constants (normally set by colorshell's build script)
|
|
globalThis.DEVEL = false;
|
|
globalThis.SRC = "$HOME/.config/ags";
|
|
globalThis.GRESOURCES_FILE = "$HOME/.config/ags/resources.gresource";
|
|
globalThis.COLORSHELL_VERSION = "2.0.3-nixos";
|
|
|
|
import "./config";
|
|
// Runner disabled - using Vicinae instead
|
|
// import {
|
|
// PluginApps,
|
|
// PluginClipboard,
|
|
// PluginMedia,
|
|
// PluginShell,
|
|
// PluginWallpapers,
|
|
// PluginWebSearch,
|
|
// PluginKill
|
|
// } from "./runner/plugins";
|
|
import { handleArguments } from "./modules/arg-handler";
|
|
// import { Runner } from "./runner/Runner";
|
|
import { Windows } from "./windows";
|
|
import { Notifications } from "./modules/notifications";
|
|
import { Wallpaper } from "./modules/wallpaper";
|
|
import { Stylesheet } from "./modules/stylesheet";
|
|
import { Clipboard } from "./modules/clipboard";
|
|
import { Gdk, Gtk } from "ags/gtk4";
|
|
import { createBinding, createComputed, createRoot, getScope, Scope } from "ags";
|
|
import { OSDModes, triggerOSD } from "./window/osd";
|
|
import { programArgs, programInvocationName } from "system";
|
|
import { setConsoleLogDomain } from "console";
|
|
import { createScopedConnection, createSubscription, encoder, secureBaseBinding } from "./modules/utils";
|
|
import { exec } from "ags/process";
|
|
import { NightLight } from "./modules/nightlight";
|
|
import { Backlights } from "./modules/backlight";
|
|
import GObject, { register } from "ags/gobject";
|
|
|
|
import Media from "./modules/media";
|
|
import GLib from "gi://GLib?version=2.0";
|
|
import Gio from "gi://Gio?version=2.0";
|
|
import Adw from "gi://Adw?version=1";
|
|
import AstalWp from "gi://AstalWp";
|
|
|
|
|
|
// const runnerPlugins: Array<Runner.Plugin> = [
|
|
// PluginApps,
|
|
// PluginShell,
|
|
// PluginWebSearch,
|
|
// PluginKill,
|
|
// PluginMedia,
|
|
// PluginWallpapers,
|
|
// PluginClipboard
|
|
// ];
|
|
|
|
const defaultWindows: Array<string> = [ "bar" ];
|
|
|
|
GLib.unsetenv("LD_PRELOAD");
|
|
|
|
|
|
@register({ GTypeName: "Shell" })
|
|
export class Shell extends Adw.Application {
|
|
private static instance: Shell;
|
|
|
|
#scope!: Scope;
|
|
#connections = new Map<GObject.Object, Array<number> | number>();
|
|
#providers: Array<Gtk.CssProvider> = [];
|
|
#gresource: Gio.Resource|null = null;
|
|
#socketService!: Gio.SocketService;
|
|
#socketFile!: Gio.File;
|
|
|
|
get scope() { return this.#scope; }
|
|
|
|
constructor() {
|
|
super({
|
|
applicationId: "io.github.retrozinndev.colorshell",
|
|
flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
|
|
version: COLORSHELL_VERSION ?? "0.0.0-unknown",
|
|
});
|
|
|
|
setConsoleLogDomain("Colorshell");
|
|
GLib.set_application_name("colorshell");
|
|
}
|
|
|
|
public static getDefault(): Shell {
|
|
if(!this.instance)
|
|
this.instance = new Shell();
|
|
|
|
return this.instance;
|
|
}
|
|
|
|
public resetStyle(): void {
|
|
this.#providers.forEach(provider =>
|
|
Gtk.StyleContext.remove_provider_for_display(
|
|
Gdk.Display.get_default()!,
|
|
provider
|
|
)
|
|
);
|
|
}
|
|
|
|
public removeProvider(provider: Gtk.CssProvider): void {
|
|
if(!this.#providers.includes(provider)) {
|
|
console.warn("Colorshell: Couldn't find the provided GtkCssProvider to remove. Was it added before?");
|
|
return;
|
|
}
|
|
|
|
for(let i = 0; i < this.#providers.length; i++) {
|
|
const prov = this.#providers[i];
|
|
if(prov === provider) {
|
|
this.#providers.splice(i, 1);
|
|
Gtk.StyleContext.remove_provider_for_display(
|
|
Gdk.Display.get_default()!,
|
|
provider
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public applyStyle(stylesheet: string): void {
|
|
try {
|
|
const provider = Gtk.CssProvider.new();
|
|
provider.load_from_string(stylesheet)
|
|
this.#providers.push(provider);
|
|
|
|
Gtk.StyleContext.add_provider_for_display(
|
|
Gdk.Display.get_default()!,
|
|
provider,
|
|
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
|
|
);
|
|
} catch(e) {
|
|
console.error(`Colorshell: Couldn't apply style. Stderr: ${e}`);
|
|
return;
|
|
}
|
|
}
|
|
|
|
vfunc_command_line(cmd: Gio.ApplicationCommandLine): number {
|
|
const args = cmd.get_arguments().toSpliced(0, 1); // remove executable
|
|
|
|
if(cmd.isRemote) {
|
|
try {
|
|
// warn user that this method is pretty slow
|
|
cmd.print_literal("\nColorshell: !! Using a remote instance to communicate is pretty slow, \
|
|
you should use the socket in the XDG_RUNTIME_DIR/colorshell.sock for a faster response.\n\n");
|
|
|
|
const res = handleArguments(cmd, args);
|
|
|
|
cmd.done();
|
|
cmd.set_exit_status(res);
|
|
return res;
|
|
} catch(_e) {
|
|
const e = _e as Error;
|
|
cmd.printerr_literal(`Error: something went wrong! Stderr: ${e.message}\n${e.stack}`);
|
|
cmd.done();
|
|
return 1;
|
|
}
|
|
} else {
|
|
if(args.length > 0) {
|
|
cmd.printerr_literal("Error: colorshell not running. Try to clean-run before using arguments");
|
|
cmd.done();
|
|
return 1;
|
|
}
|
|
|
|
this.activate();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
vfunc_activate(): void {
|
|
super.vfunc_activate();
|
|
this.hold();
|
|
this.main();
|
|
}
|
|
|
|
private init(): void {
|
|
// load gresource from build-defined path
|
|
try {
|
|
this.#gresource = Gio.Resource.load(GRESOURCES_FILE.split('/').filter(s =>
|
|
s !== ""
|
|
).map(path => {
|
|
// support environment variables at runtime
|
|
if(/^\$/.test(path)) {
|
|
const env = GLib.getenv(path.replace(/^\$/, ""));
|
|
if(env === null)
|
|
throw new Error(`Couldn't get environment variable: ${path}`);
|
|
|
|
return env;
|
|
}
|
|
|
|
return path;
|
|
}).join('/'));
|
|
Gio.resources_register(this.#gresource);
|
|
|
|
// add icons
|
|
Gtk.IconTheme.get_for_display(Gdk.Display.get_default()!)
|
|
.add_resource_path("/io/github/retrozinndev/colorshell/icons")
|
|
} catch(_e) {
|
|
const e = _e as Error;
|
|
console.error(`Error: couldn't load gresource! Stderr: ${e.message}\n${e.stack}`);
|
|
}
|
|
|
|
this.#socketFile = Gio.File.new_for_path(`${GLib.get_user_runtime_dir() ??
|
|
`/run/user/${exec("id -u").trim()}`}/colorshell.sock`);
|
|
|
|
if(this.#socketFile.query_exists(null)) {
|
|
console.log(`Colorshell: Deleting previous instance's socket`);
|
|
this.#socketFile.delete(null);
|
|
}
|
|
|
|
this.#socketService = Gio.SocketService.new();
|
|
this.#socketService.add_address(
|
|
Gio.UnixSocketAddress.new(this.#socketFile.get_path()!),
|
|
Gio.SocketType.STREAM,
|
|
Gio.SocketProtocol.DEFAULT,
|
|
null
|
|
);
|
|
|
|
// handle communication via socket
|
|
createScopedConnection(this.#socketService, "incoming", (conn) => {
|
|
const inputStream = Gio.DataInputStream.new(conn.inputStream);
|
|
inputStream.read_upto_async('\x00', -1, GLib.PRIORITY_DEFAULT, null, (_, res) => {
|
|
const [args, len] = inputStream.read_upto_finish(res);
|
|
inputStream.close(null);
|
|
conn.inputStream.close(null);
|
|
|
|
if(len < 1) {
|
|
console.error(`Colorshell: No args provided via socket call`);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const [success, parsedArgs] = GLib.shell_parse_argv(`colorshell ${args}`);
|
|
parsedArgs?.splice(0, 1); // remove the unnecessary `colorshell` part
|
|
|
|
if(success) {
|
|
handleArguments({
|
|
print_literal: (msg) => conn.outputStream.write_bytes(
|
|
encoder.encode(`${msg}\n`),
|
|
null
|
|
),
|
|
// TODO: support writing to stderr(i don't know how to do that :sob:)
|
|
printerr_literal: (msg) => conn.outputStream.write_bytes(
|
|
encoder.encode(`${msg}\n`),
|
|
null
|
|
)
|
|
}, parsedArgs!);
|
|
|
|
conn.outputStream.flush(null);
|
|
conn.close(null);
|
|
return;
|
|
}
|
|
|
|
conn.outputStream.write_bytes(
|
|
encoder.encode("Error: Unexpected error occurred on argument parsing!"),
|
|
null
|
|
);
|
|
|
|
conn.outputStream.flush(null);
|
|
conn.close(null);
|
|
} catch(_e) {
|
|
const e = _e as Error;
|
|
console.error(`Colorshell: An error occurred while writing to socket output. Stderr:\n${
|
|
e.message}\n${e.stack}`);
|
|
}
|
|
});
|
|
|
|
return false;
|
|
});
|
|
}
|
|
|
|
private main(): void {
|
|
Gtk.init();
|
|
Adw.init();
|
|
|
|
createRoot((dispose) => {
|
|
console.log(`Colorshell: Initializing things`);
|
|
this.#connections.set(this, this.connect("shutdown", () => dispose()));
|
|
|
|
this.init();
|
|
this.#scope = getScope();
|
|
|
|
// NightLight.getDefault(); // Disabled - requires gamma control tools
|
|
|
|
Media.getDefault();
|
|
Clipboard.getDefault();
|
|
|
|
console.log("Colorshell: Initializing Wallpaper and Stylesheet modules");
|
|
Wallpaper.getDefault();
|
|
Stylesheet.getDefault();
|
|
|
|
// Runner disabled - using Vicinae instead
|
|
// console.log("Adding runner plugins");
|
|
// runnerPlugins.forEach(plugin => Runner.addPlugin(plugin));
|
|
|
|
createSubscription(
|
|
createComputed([
|
|
secureBaseBinding<AstalWp.Endpoint>(createBinding(
|
|
AstalWp.get_default(), "defaultSpeaker"
|
|
), "volume", null),
|
|
secureBaseBinding<AstalWp.Endpoint>(createBinding(
|
|
AstalWp.get_default(), "defaultSpeaker"
|
|
), "mute", null)
|
|
]),
|
|
() => !Windows.getDefault().isOpen("control-center") &&
|
|
triggerOSD(OSDModes.sink)
|
|
);
|
|
|
|
createSubscription(
|
|
secureBaseBinding<Backlights.Backlight>(
|
|
createBinding(Backlights.getDefault(), "default"),
|
|
"brightness",
|
|
100
|
|
),
|
|
() => !Windows.getDefault().isOpen("control-center") &&
|
|
triggerOSD(OSDModes.brightness)
|
|
);
|
|
|
|
this.#connections.set(Notifications.getDefault(), [
|
|
Notifications.getDefault().connect("notification-added", () => {
|
|
Windows.getDefault().open("floating-notifications");
|
|
}),
|
|
Notifications.getDefault().connect("notification-removed", (self) => {
|
|
self.notifications.length === 0 && Windows.getDefault().close("floating-notifications");
|
|
})
|
|
]);
|
|
|
|
defaultWindows.forEach(w => Windows.getDefault().open(w));
|
|
});
|
|
|
|
this.#scope.onCleanup(() => {
|
|
console.log("Colorshell: disposing connections and quitting because of ::shutdown");
|
|
this.#connections.forEach((ids, obj) => Array.isArray(ids) ?
|
|
ids.forEach(id => obj.disconnect(id))
|
|
: obj.disconnect(ids));
|
|
});
|
|
}
|
|
|
|
quit(): void {
|
|
this.release();
|
|
}
|
|
}
|
|
|
|
Shell.getDefault().runAsync([ programInvocationName, ...programArgs ]);
|