From 81c4e3a12d05a3550f1d8c942f0d919b4688c3bc Mon Sep 17 00:00:00 2001 From: Aylur Date: Tue, 21 May 2024 22:08:41 +0200 Subject: fix proxy, add remaining cli functionality --- src/cli.vala.in | 88 +++++++++++++++++++++++++++++++++++++++++++-------- src/daemon.vala | 34 ++++++++++++-------- src/notifd.vala | 55 +++----------------------------- src/notification.vala | 6 ++-- src/proxy.vala | 69 +++++++++++++++++++++++++++++----------- src/signals.md | 32 +++++++++++++++++++ 6 files changed, 185 insertions(+), 99 deletions(-) create mode 100644 src/signals.md (limited to 'src') diff --git a/src/cli.vala.in b/src/cli.vala.in index 056a69d..f49a056 100644 --- a/src/cli.vala.in +++ b/src/cli.vala.in @@ -1,9 +1,19 @@ -private static bool version; -private static bool help; +static bool help; +static bool version; +static bool daemonize; +static bool list; +static string invoke; +static int close_n; +static int get_n; private const OptionEntry[] options = { { "version", 'v', OptionFlags.NONE, OptionArg.NONE, ref version, null, null }, { "help", 'h', OptionFlags.NONE, OptionArg.NONE, ref help, null, null }, + { "daemon", 'd', OptionFlags.NONE, OptionArg.NONE, ref daemonize, null, null }, + { "list", 'l', OptionFlags.NONE, OptionArg.NONE, ref list, null, null }, + { "invoke", 'i', OptionFlags.NONE, OptionArg.STRING, ref invoke, null, null }, + { "close", 'c', OptionFlags.NONE, OptionArg.INT, ref close_n, null, null }, + { "get", 'g', OptionFlags.NONE, OptionArg.INT, ref get_n, null, null }, { null }, }; @@ -22,26 +32,76 @@ int main(string[] argv) { if (help) { print("Cli client for astal-notifd\n\n"); print("Usage:\n"); - print(" %s [flags] message\n\n", argv[0]); + print(" %s [flags]\n\n", argv[0]); print("Flags:\n"); - print(" -h, --help Print this help and exit\n"); - print(" -v, --version Print version number and exit\n"); + print(" -h, --help Print this help and exit\n"); + print(" -v, --version Print version number and exit\n"); + print(" -l, --list Print every notification and exit\n"); + print(" -d, --daemonize Watch for new notifications\n"); + print(" -i, --invoke Invoke a notification action\n"); + print(" -c, --close Close a notification by its id\n"); + print(" -g, --get Print a notification by its id\n"); return 0; } + var loop = new MainLoop(); + var notifd = new AstalNotifd.Notifd(); + if (version) { print("@VERSION@"); return 0; } - var notifd = new AstalNotifd.Notifd(); - notifd.notified.connect((id) => { - stdout.printf("%s\n", notifd.get_notification_json(id)); - }); - notifd.active.connect(() => { - foreach (var n in notifd.notifications) - stdout.printf("%s\n", n.to_json_string()); - }); - new MainLoop().run(); + if (list) { + var cache = Environment.get_user_cache_dir() + "/astal/notifd/notifications.json"; + if (FileUtils.test(cache, FileTest.EXISTS)) { + try { + uint8[] json; + File.new_for_path(cache).load_contents(null, out json, null); + stdout.printf("%s", (string)json); + } catch (Error err) { + stderr.printf("failed to load cache: %s", err.message); + } + } + return 0; + } + + if (daemonize) { + notifd.notified.connect((id) => { + stdout.printf("%s\n", notifd.get_notification_json(id)); + }); + } + + if (invoke != null) { + if (!invoke.contains(":")) { + stderr.printf("invoke format needs to be :"); + return 1; + } + + var split = invoke.split(":"); + var n_id = int.parse(split[0]); + var a_id = split[1]; + + notifd.active.connect(() => { + notifd.get_notification(n_id).invoke(a_id); + loop.quit(); + }); + } + + if (close_n > 0) { + notifd.active.connect(() => { + notifd.get_notification(close_n).dismiss(); + loop.quit(); + }); + } + + if (get_n > 0) { + notifd.active.connect(() => { + stdout.printf("%s", notifd.get_notification(get_n).to_json_string()); + loop.quit(); + }); + } + + loop.run(); return 0; } diff --git a/src/daemon.vala b/src/daemon.vala index fd344e7..69212ef 100644 --- a/src/daemon.vala +++ b/src/daemon.vala @@ -13,21 +13,26 @@ internal class Daemon : Object { public static string version = "0.1"; private string cache_file; + private string cache_directory; + private uint n_id = 1; private HashTable notifs = new HashTable((i) => i, (a, b) => a == b); - public string cache_directory { set; owned get; } public bool ignore_timeout { get; set; } public bool dont_disturb { get; set; } public signal void notified(uint id); public signal void resolved(uint id, ClosedReason reason); + public signal void action_invoked(uint id, string action); - construct { - if (cache_directory == null) - cache_directory = Environment.get_user_cache_dir() + "/astal/notifd"; + // emitting an event from proxy doesn't seem to work + public void emit_notified(uint id) { notified(id); } + public void emit_resolved(uint id, ClosedReason reason) { resolved(id, reason); } + public void emit_action_invoked(uint id, string action) { action_invoked(id, action); } + construct { + cache_directory = Environment.get_user_cache_dir() + "/astal/notifd"; cache_file = cache_directory + "/notifications.json"; if (FileUtils.test(cache_file, FileTest.EXISTS)) { @@ -38,8 +43,7 @@ internal class Daemon : Object { parser.load_from_data((string)json); var list = parser.get_root().get_array(); for (var i = 0; i < list.get_length(); ++i) { - var n = new Notification.from_json(list.get_object_element(i)); - notifs.set(n.id, n); + add_notification(new Notification.from_json(list.get_object_element(i))); } } catch (Error err) { warning("failed to load cache: %s", err.message); @@ -73,7 +77,7 @@ internal class Daemon : Object { } [DBus (visible = false)] - public Notification get_notification(uint id) throws DBusError, IOError { + public Notification get_notification(uint id) { return notifs.get(id); } @@ -105,12 +109,10 @@ internal class Daemon : Object { hints.remove("icon_data"); var id = replaces_id > 0 ? replaces_id : n_id++; - var n = new Notification( - app_name, id, app_icon, summary, body, actions, hints, expire_timeout - ); - n.dismissed.connect(() => resolved(id, ClosedReason.DISMISSED_BY_USER)); - n.invoked.connect((action) => action_invoked(id, action)); + add_notification(new Notification( + app_name, id, app_icon, summary, body, actions, hints, expire_timeout + )); if (!ignore_timeout && expire_timeout > 0) { Timeout.add(expire_timeout, () => { @@ -119,7 +121,6 @@ internal class Daemon : Object { }, Priority.DEFAULT); } - notifs.set(id, n); if (!dont_disturb) notified(id); @@ -127,6 +128,12 @@ internal class Daemon : Object { return id; } + private void add_notification(Notification n) { + n.dismissed.connect(() => resolved(n.id, ClosedReason.DISMISSED_BY_USER)); + n.invoked.connect((action) => action_invoked(n.id, action)); + notifs.set(n.id, n); + } + private void cache() { var list = new Json.Builder().begin_array(); foreach (var n in notifications) { @@ -148,7 +155,6 @@ internal class Daemon : Object { } public signal void notification_closed(uint id, uint reason); - public signal void action_invoked(uint id, string action); public signal void activation_token(uint id, string token); public void close_notification(uint id) throws DBusError, IOError { diff --git a/src/notifd.vala b/src/notifd.vala index 220278c..55d910b 100644 --- a/src/notifd.vala +++ b/src/notifd.vala @@ -8,23 +8,8 @@ public class Notifd : Object { private Daemon daemon; private DaemonProxy proxy; - private HashTable notifs = - new HashTable((i) => i, (a, b) => a == b); - public signal void active(ActiveType type); - public string cache_directory { - owned get { - return proxy != null ? proxy.cache_directory : daemon.cache_directory; - } - set { - if (proxy != null) - proxy.cache_directory = value; - else - daemon.cache_directory = value; - } - } - public bool ignore_timeout { get { return proxy != null ? proxy.ignore_timeout : daemon.ignore_timeout; @@ -50,7 +35,7 @@ public class Notifd : Object { } public List notifications { - owned get { return notifs.get_values(); } + owned get { return proxy != null ? proxy.notifications : daemon.notifications; } } public uint[] notification_ids() throws Error { @@ -58,33 +43,15 @@ public class Notifd : Object { } public Notification get_notification(uint id) { - return notifs.get(id); + return proxy != null ? proxy.get_notification(id) : daemon.get_notification(id); } public string get_notification_json(uint id) { - return notifs.get(id).to_json_string(); - } - - public signal void notified(uint id) { - add_notification(id); + return get_notification(id).to_json_string(); } - public signal void resolved(uint id, ClosedReason reason) { - notifs.remove(id); - } - - private void add_notification(uint id) { - try { - if (proxy != null) { - var json = proxy.get_notification_json(id); - notifs.set(id, Notification.from_json_string(json)); - } else { - notifs.set(id, daemon.get_notification(id)); - } - } catch (Error err) { - warning("could not add notification: %s", err.message); - } - } + public signal void notified(uint id); + public signal void resolved(uint id, ClosedReason reason); construct { Bus.own_name( @@ -112,23 +79,11 @@ public class Notifd : Object { notify_property(prop.name); } }); - foreach (var n in daemon.notifications) { - notifs.set(n.id, n); - } } private void try_proxy() { proxy = new DaemonProxy(); if (proxy.start()) { - try { - foreach (var id in proxy.notification_ids()) { - add_notification(id); - } - } - catch (Error err) { - warning("could not get notification ids: %s", err.message); - } - active(ActiveType.PROXY); } else { return; diff --git a/src/notification.vala b/src/notification.vala index bec014d..55d62d1 100644 --- a/src/notification.vala +++ b/src/notification.vala @@ -90,7 +90,7 @@ public class Notification : Object { public void dismiss() { dismissed(); } public void invoke(string action) { invoked(action); } - public Notification.from_json(Json.Object root) throws GLib.Error { + internal Notification.from_json(Json.Object root) throws GLib.Error { foreach (var key in root.get_members()) { var node = root.get_member(key); switch (key) { @@ -123,7 +123,7 @@ public class Notification : Object { } } - public static Notification from_json_string(string json) throws GLib.Error { + internal static Notification from_json_string(string json) throws GLib.Error { var parser = new Json.Parser(); parser.load_from_data(json); return new Notification.from_json(parser.get_root().get_object()); @@ -135,7 +135,7 @@ public class Notification : Object { return generator.to_data(null); } - public Json.Node to_json() { + internal Json.Node to_json() { var acts = new Json.Builder().begin_array(); foreach (var action in actions) { acts.begin_object() diff --git a/src/proxy.vala b/src/proxy.vala index 526cd13..1f5870d 100644 --- a/src/proxy.vala +++ b/src/proxy.vala @@ -1,7 +1,6 @@ namespace AstalNotifd { [DBus (name = "org.freedesktop.Notifications")] internal interface IDaemon : Object { - public abstract string cache_directory { owned get; set; } public abstract bool ignore_timeout { get; set; } public abstract bool dont_disturb { get; set; } @@ -10,12 +9,19 @@ internal interface IDaemon : Object { public signal void notified(uint id); public signal void resolved(uint id, ClosedReason reason); + public signal void action_invoked(uint id, string action); + + public abstract void emit_notified(uint id); + public abstract void emit_resolved(uint id, ClosedReason reason); + public abstract void emit_action_invoked(uint id, string action); } internal class DaemonProxy : Object { - public string cache_directory { - owned get { return proxy.cache_directory; } - set { proxy.cache_directory = value; } + private HashTable notifs = + new HashTable((i) => i, (a, b) => a == b); + + public List notifications { + owned get { return notifs.get_values(); } } public bool ignore_timeout { @@ -32,8 +38,8 @@ internal class DaemonProxy : Object { return proxy.notification_ids(); } - public string get_notification_json(uint id) throws DBusError, IOError { - return proxy.get_notification_json(id); + public Notification get_notification(uint id) { + return notifs.get(id); } public signal void notified(uint id); @@ -72,18 +78,7 @@ internal class DaemonProxy : Object { && version == Daemon.version; if (running) { - proxy = Bus.get_proxy_sync( - BusType.SESSION, - "org.freedesktop.Notifications", - "/org/freedesktop/Notifications" - ); - - ids.append(proxy.notified.connect((id) => notified(id))); - ids.append(proxy.resolved.connect((id, reason) => resolved(id, reason))); - ids.append(proxy.notify.connect((pspec) => { - if (get_class().find_property(pspec.name) != null) - notify_property(pspec.name); - })); + setup_proxy(); return true; } else { critical("cannot get proxy: %s is already running", name); @@ -93,5 +88,43 @@ internal class DaemonProxy : Object { } return false; } + + private void setup_proxy() throws Error { + proxy = Bus.get_proxy_sync( + BusType.SESSION, + "org.freedesktop.Notifications", + "/org/freedesktop/Notifications" + ); + + foreach (var id in proxy.notification_ids()) + add_notification(id); + + ids.append(proxy.notify.connect((pspec) => { + if (get_class().find_property(pspec.name) != null) + notify_property(pspec.name); + })); + + ids.append(proxy.notified.connect((id) => { + add_notification(id); + notified(id); + })); + + ids.append(proxy.resolved.connect((id, reason) => { + notifs.remove(id); + resolved(id, reason); + })); + } + + private void add_notification(uint id) { + try { + var n = Notification.from_json_string(proxy.get_notification_json(id)); + proxy.resolved.connect((id, reason) => n.resolved(reason)); + n.dismissed.connect(() => proxy.emit_resolved(id, ClosedReason.DISMISSED_BY_USER)); + n.invoked.connect((action) => proxy.emit_action_invoked(id, action)); + notifs.set(id, n); + } catch (Error err) { + critical(err.message); + } + } } } diff --git a/src/signals.md b/src/signals.md new file mode 100644 index 0000000..0111596 --- /dev/null +++ b/src/signals.md @@ -0,0 +1,32 @@ +# Signals + +ignore this, I'm just dumb and can't follow where signals go or get emitted from + +## Notification + +* resolved(reason) - by daemon/proxy +* dismissed() - by user with `.dismiss()` +* invoked(action) - by user with `.invoke()` + +## Deamon + +non-spec, used by user +* notified(id) - by outside through dbus with `.Notify()` +* resolved(id, reason) - by `Notification.dismiss()` or outside with `.CloseNotification` + +spec, not used by user +* notification_closed(id, reason) - sideeffect of `resolved` +* action_invoked(id, action) - by `Notification.invoke()` + +## Proxy + +mirrors Daemon +* notified(id) +* resolved(id, reason) + +creates `Notification` objects through daemon's json strings +and hooks them up to call daemon's signals and vice versa + +## Notifd + +acts as a bridge between Proxy/Daemon, everything else is internal only -- cgit v1.2.3