From 7732841d474725858363efeeb79e775126bd4f62 Mon Sep 17 00:00:00 2001 From: Aylur Date: Thu, 24 Aug 2023 17:50:20 +0200 Subject: [PATCH 1/4] print run-js to stdout if the argument of run-js is a * single line expression, print it to stdout * multiline function, then the function has to call resolve or reject --- src/ags.src.gresource.xml | 1 + src/app.ts | 58 ++++++++++++++++------ src/client.ts | 67 +++++++++++++++++++++++++ src/main.ts | 100 +++++++++++++++++++------------------- 4 files changed, 161 insertions(+), 65 deletions(-) create mode 100644 src/client.ts diff --git a/src/ags.src.gresource.xml b/src/ags.src.gresource.xml index 7305f47..884e0af 100644 --- a/src/ags.src.gresource.xml +++ b/src/ags.src.gresource.xml @@ -3,6 +3,7 @@ main.js app.js + client.js utils.js widget.js diff --git a/src/app.ts b/src/app.ts index 40d838b..8ae8392 100644 --- a/src/app.ts +++ b/src/app.ts @@ -27,6 +27,7 @@ export default class App extends Gtk.Application { private _windows: Map; private _closeDelay!: { [key: string]: number }; private _cssProviders: Gtk.CssProvider[] = []; + private _clientActionGroup: Gio.DBusActionGroup; static configPath: string; static configDir: string; @@ -39,7 +40,7 @@ export default class App extends Gtk.Application { static get windows() { return App.instance._windows; } static getWindow(name: string) { return App.instance.getWindow(name); } static closeWindow(name: string) { App.instance.closeWindow(name); } - static openWindow(name: string) { App.getWindow(name)?.show(); } + static openWindow(name: string) { App.instance.openWindow(name); } static toggleWindow(name: string) { App.instance.toggleWindow(name); } static quit() { App.instance.quit(); } @@ -76,21 +77,20 @@ export default class App extends Gtk.Application { App.instance._cssProviders.push(cssProvider); } - constructor({ bus, config }: { - bus: string - config: string - }) { + constructor(bus: string, path: string, configPath: string) { super({ application_id: bus, flags: Gio.ApplicationFlags.DEFAULT_FLAGS, }); this._windows = new Map(); + this._clientActionGroup = Gio.DBusActionGroup + .get(Gio.DBus.session, bus + '.client', path + '/client'); - const dir = config.split('/'); + const dir = configPath.split('/'); dir.pop(); App.configDir = dir.join('/'); - App.configPath = config; + App.configPath = configPath; App.instance = this; } @@ -111,7 +111,13 @@ export default class App extends Gtk.Application { toggleWindow(name: string) { const w = this.getWindow(name); if (w) - w.visible ? App.closeWindow(name) : App.openWindow(name); + w.visible ? this.closeWindow(name) : this.openWindow(name); + else + return 'There is no window named ' + name; + } + + openWindow(name: string) { + this.getWindow(name)?.show(); } closeWindow(name: string) { @@ -201,6 +207,14 @@ export default class App extends Gtk.Application { } } + _runJs(js: string) { + return new Promise((resolve, reject) => { + js.includes('\n') || js.includes(';') + ? Function('resolve', 'reject', js)(resolve, reject) + : resolve(`${Function('return ' + js)()}` || ''); + }); + } + _addAction( name: string, callback: (_source: Gio.SimpleAction, _param: GLib.Variant) => void, @@ -214,16 +228,30 @@ export default class App extends Gtk.Application { } _exportActions() { - this._addAction('inspector', - () => Gtk.Window.set_interactive_debugging(true)); - this._addAction('quit', App.quit); + this._addAction('inspector', () => { + Gtk.Window.set_interactive_debugging(true); + }); - this._addAction('toggle-window', (_, arg) => App.toggleWindow( - arg.unpack() as string), new GLib.VariantType('s')); + this._addAction('quit', () => { + this.quit(); + }); + + this._addAction('toggle-window', (_, arg) => { + const err = this.toggleWindow(arg.unpack() as string); + this._clientActionGroup.activate_action('print', + new GLib.Variant('s', err || '')); + }, new GLib.VariantType('s')); this._addAction('run-js', (_, arg) => { - const fn = new Function(arg.unpack() as string); - fn(); + this._runJs(arg.unpack() as string) + .then(res => { + this._clientActionGroup.activate_action('print', + new GLib.Variant('s', `${res}` || '')); + }) + .catch(rej => { + this._clientActionGroup.activate_action('print', + new GLib.Variant('s', `${rej}` || '')); + }); }, new GLib.VariantType('s')); } } diff --git a/src/client.ts b/src/client.ts new file mode 100644 index 0000000..e772724 --- /dev/null +++ b/src/client.ts @@ -0,0 +1,67 @@ +import Gtk from 'gi://Gtk?version=3.0'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; + +interface Flags { + busName: string + inspector: boolean + runJs: string + toggleWindow: string + quit: boolean +} + +export default class Client extends Gtk.Application { + static { GObject.registerClass(this); } + + private _flags: Flags; + private _actionGroup: Gio.DBusActionGroup; + + constructor(bus: string, path: string, flags: Flags) { + super({ + application_id: bus + '.client', + flags: Gio.ApplicationFlags.DEFAULT_FLAGS, + }); + + this._flags = flags; + this._actionGroup = Gio.DBusActionGroup + .get(Gio.DBus.session, bus, path); + + const action = new Gio.SimpleAction({ + name: 'print', + parameter_type: new GLib.VariantType('s'), + }); + action.connect('activate', (_, returnValue) => { + print(returnValue?.unpack() as string || ''); + this.quit(); + }); + this.add_action(action); + } + + vfunc_activate() { + const { toggleWindow, runJs, inspector, quit } = this._flags; + if (toggleWindow) { + this.hold(); + this._actionGroup.activate_action('toggle-window', + new GLib.Variant('s', toggleWindow)); + } + + if (runJs) { + this.hold(); + this._actionGroup.activate_action('run-js', + new GLib.Variant('s', runJs)); + } + + if (inspector) + this._actionGroup.activate_action('inspector', null); + + if (quit) + this._actionGroup.activate_action('quit', null); + + if (!toggleWindow && !runJs && !inspector && !quit) { + print('Ags with busname "' + + this._flags.busName + + '" is already running'); + } + } +} diff --git a/src/main.ts b/src/main.ts index c33df03..4a5989b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,6 +2,7 @@ import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import * as Utils from './utils.js'; import App from './app.js'; +import Client from './client.js'; import Service from './service/service.js'; import Widget from './widget.js'; import './service/apps.js'; @@ -30,42 +31,23 @@ OPTIONS: -i, --inspector Open up the Gtk debug tool, useful for fetching css selectors -t, --toggle-window Show or hide a window - -r, --run-js Evaluate given string as a function and execute it. - NOTE: It won't print anything, - but if the function logs something, - it can be seen on AGS's stdout. + -r, --run-js Evaluate given string as a function and execute it + NOTE: logging inside this function won't print + anything to the terminal, but it logs to the stdout + of the running AGS proccess + To print to the current stdout the string has to be + a single expression on a single line, + or call resolve() or reject() in the function body --clear-cache Removes ${Utils.CACHE_DIR} EXAMPLES ags --config $HOME/.config/ags/main.js --bus-name second-instance - ags --run-js "ags.Service.Mpris.getPlayer()?.playPause()" + ags --run-js "ags.Service.Mpris.getPlayer()?.name" + ags --run-js "ags.Utils.timeout(1000, () => { + resolve('a second later'); + })" ags --toggle-window "window-name"`; -function client( - busName: string, - inspector: boolean, - runJs: string, - toggleWindow: string, - quit: boolean, -) { - const actions = Gio.DBusActionGroup.get( - Gio.DBus.session, APP_BUS(busName), APP_PATH(busName)); - - if (toggleWindow) { - actions.activate_action('toggle-window', - new GLib.Variant('s', toggleWindow)); - } - - if (runJs) - actions.activate_action('run-js', new GLib.Variant('s', runJs)); - - if (inspector) - actions.activate_action('inspector', null); - - if (quit) - actions.activate_action('quit', null); -} - function isRunning(dbusName: string) { return Gio.DBus.session.call_sync( 'org.freedesktop.DBus', @@ -82,12 +64,14 @@ function isRunning(dbusName: string) { } export function main(args: string[]) { - let appBus = pkg.name; - let config = DEFAULT_CONF; - let inspector = false; - let runJs = ''; - let toggleWindow = ''; - let quit = false; + const flags = { + busName: pkg.name, + config: DEFAULT_CONF, + inspector: false, + runJs: '', + toggleWindow: '', + quit: false, + }; for (let i = 1; i < args.length; ++i) { switch (args[i]) { @@ -110,36 +94,36 @@ export function main(args: string[]) { case '-b': case '--bus-name': - appBus = args[++i]; + flags.busName = args[++i]; break; case '-c': case '--config': - config = args[++i]; + flags.config = args[++i]; break; case 'inspector': case '-i': case '--inspector': - inspector = true; + flags.inspector = true; break; case 'run-js': case '-r': case '--run-js': - runJs = args[++i]; + flags.runJs = args[++i]; break; case 'toggle-window': case '-t': case '--toggle-window': - toggleWindow = args[++i]; + flags.toggleWindow = args[++i]; break; case 'quit': case '-q': case '--quit': - quit = true; + flags.quit = true; break; default: @@ -156,19 +140,35 @@ export function main(args: string[]) { Service, }; - const bus = APP_BUS(appBus); + const bus = APP_BUS(flags.busName); + const path = APP_PATH(flags.busName); + if (!isRunning(bus)) { - const app = new App({ bus, config }); + const app = new App(bus, path, flags.config); app.connect('config-parsed', () => { - client(appBus, inspector, runJs, toggleWindow, quit); + const { toggleWindow, runJs, inspector } = flags; + const actions = Gio.DBusActionGroup.get( + Gio.DBus.session, bus, path); + + if (toggleWindow) { + actions.activate_action('toggle-window', + new GLib.Variant('s', toggleWindow)); + } + + if (runJs) { + actions.activate_action('run-js', + new GLib.Variant('s', runJs)); + } + + if (inspector) + actions.activate_action('inspector', null); }); // @ts-ignore return app.runAsync(null); } - - client(appBus, inspector, runJs, toggleWindow, quit); - - if (!inspector && !runJs && !toggleWindow && !quit) - print(`Ags with busname "${appBus}" is already running`); + else { + // @ts-ignore + return new Client(bus, path, flags).runAsync(null); + } } From 226b0515c4d35f41ac4c77e29efb9eaa7ae64d5d Mon Sep 17 00:00:00 2001 From: Aylur Date: Wed, 30 Aug 2023 14:02:41 +0200 Subject: [PATCH 2/4] update help message --- src/main.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.ts b/src/main.ts index 9745f6e..c9d8664 100644 --- a/src/main.ts +++ b/src/main.ts @@ -45,10 +45,10 @@ OPTIONS: EXAMPLES ags --config $HOME/.config/ags/main.js --bus-name second-instance ags --run-js "ags.Service.Mpris.getPlayer()?.name" - ags --run-js "ags.Utils.timeout(1000, () => { + ags --run-promise "ags.Utils.timeout(1000, () => { resolve('a second later'); })" - ags --toggle-window "window-name"`; + ags --toggle-window window-name`; function isRunning(dbusName: string) { return Gio.DBus.session.call_sync( From 8463d2cc0c6e471b64664fde0f69d7485ddada92 Mon Sep 17 00:00:00 2001 From: Aylur Date: Wed, 30 Aug 2023 14:16:46 +0200 Subject: [PATCH 3/4] use current time as client bus name --- src/client.ts | 19 +++++++++++++------ src/dbus/types.ts | 13 ++++++++----- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/client.ts b/src/client.ts index 38a250f..f655863 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,6 +1,7 @@ import Gtk from 'gi://Gtk?version=3.0'; import GObject from 'gi://GObject'; import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; import { loadInterfaceXML } from './utils.js'; import { type AgsProxy } from './dbus/types.js'; @@ -10,6 +11,8 @@ const AgsIFace = (bus: string) => const ClientIFace = (bus: string) => loadInterfaceXML('com.github.Aylur.ags.client')?.replace('@BUS@', bus); +const TIME = `${GLib.DateTime.new_now_local().to_unix()}`; + interface Flags { busName: string inspector: boolean @@ -22,18 +25,18 @@ interface Flags { class Client extends Gtk.Application { static { GObject.registerClass(this); } - private _path: string; + private _objectPath: string; private _dbus!: Gio.DBusExportedObject; private _proxy: AgsProxy; private _promiseJs: string; constructor(bus: string, path: string, proxy: AgsProxy, js: string) { super({ - application_id: bus + '.client', + application_id: bus + '.client' + TIME, flags: Gio.ApplicationFlags.DEFAULT_FLAGS, }); - this._path = path + '/client'; + this._objectPath = path + '/client' + TIME; this._proxy = proxy; this._promiseJs = js; } @@ -47,7 +50,7 @@ class Client extends Gtk.Application { this._dbus = Gio.DBusExportedObject .wrapJSObject(ClientIFace(this.applicationId) as string, this); - this._dbus.export(connection, this._path); + this._dbus.export(connection, this._objectPath); }, null, null, @@ -63,7 +66,11 @@ class Client extends Gtk.Application { vfunc_activate(): void { this.hold(); this._register(); - this._proxy.RunPromiseRemote(this._promiseJs, this.applicationId, this._path); + this._proxy.RunPromiseRemote( + this._promiseJs, + this.applicationId, + this._objectPath, + ); } } @@ -81,7 +88,7 @@ export default function(bus: string, path: string, flags: Flags) { proxy.InspectorRemote(); else if (flags.quit) - proxy.Quit(); + proxy.QuitRemote(); else if (flags.runPromise) return new Client(bus, path, proxy, flags.runPromise).run(null); diff --git a/src/dbus/types.ts b/src/dbus/types.ts index db20dd9..c1adad0 100644 --- a/src/dbus/types.ts +++ b/src/dbus/types.ts @@ -47,9 +47,12 @@ export interface BatteryProxy extends Gio.DBusProxy { export interface AgsProxy extends Gio.DBusProxy { new(...args: unknown[]): AgsProxy - Inspector: () => void; - ToggleWindow: (name: string) => boolean; - Quit: () => void; - RunJs: (js: string) => string; - RunJsAsync: (js: string) => string; + InspectorRemote: () => void; + QuitRemote: () => void; + ToggleWindowSync: (name: string) => boolean; + RunJsSync: (js: string) => string; + RunPromiseRemote: ( + js: string, + busName?: string, + objPath?: string) => void } From e669c4fb75d402f72c5a92bb9b70f87ac7274923 Mon Sep 17 00:00:00 2001 From: Aylur Date: Wed, 30 Aug 2023 14:27:05 +0200 Subject: [PATCH 4/4] fix run-js semicolon check --- src/app.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app.ts b/src/app.ts index ddb9a5b..7ba1062 100644 --- a/src/app.ts +++ b/src/app.ts @@ -231,8 +231,8 @@ export default class App extends Gtk.Application { RunJs(js: string): string { return js.includes(';') - ? `${Function('return ' + js)()}` - : `${Function(js)()}`; + ? `${Function(js)()}` + : `${Function('return ' + js)()}`; } RunPromise(js: string, busName?: string, objPath?: string) {