summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--flake.nix2
-rw-r--r--src/cli.vala.in88
-rw-r--r--src/daemon.vala34
-rw-r--r--src/notifd.vala55
-rw-r--r--src/notification.vala6
-rw-r--r--src/proxy.vala69
-rw-r--r--src/signals.md32
8 files changed, 186 insertions, 104 deletions
diff --git a/README.md b/README.md
index 2958e22..da93a6a 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,3 @@ A notification daemon library and cli tool
## TODO
- docs
-- cli options for
- - [ ] getting a list of every notification
- - [ ] getting a notification by its id
- - [ ] invoke notification action by id
diff --git a/flake.nix b/flake.nix
index 15232d3..78a2ab1 100644
--- a/flake.nix
+++ b/flake.nix
@@ -27,7 +27,7 @@
default = notifd;
notifd = pkgs.stdenv.mkDerivation {
inherit nativeBuildInputs buildInputs;
- pname = "notifd";
+ pname = "astal-notifd";
version = version;
src = ./.;
outputs = ["out" "dev"];
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 <notif-id>:<action-id>");
+ 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<uint, Notification> notifs =
new HashTable<uint, Notification>((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<uint, Notification> notifs =
- new HashTable<uint, Notification>((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<weak Notification> 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<uint, Notification> notifs =
+ new HashTable<uint, Notification>((i) => i, (a, b) => a == b);
+
+ public List<weak Notification> 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