summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAylur <[email protected]>2024-07-31 14:01:04 +0200
committerAylur <[email protected]>2024-07-31 14:01:04 +0200
commite0c70f09729dfe40c2328bd68257556ac411473e (patch)
tree27e7e1b46703b60d45e37baa4a0713344efaa2ca
parent7757661ac79455f6765ddde7ea0239771bc9cfce (diff)
fixes, features
* cli dnd-toggle * save dnd and ignore_timeout state * block contstructor until dbus acqusition * fix proxy notify signals
-rw-r--r--src/cli.vala54
-rw-r--r--src/daemon.vala86
-rw-r--r--src/notifd.vala44
-rw-r--r--src/notification.vala33
-rw-r--r--src/proxy.vala23
5 files changed, 142 insertions, 98 deletions
diff --git a/src/cli.vala b/src/cli.vala
index 433ba43..afce774 100644
--- a/src/cli.vala
+++ b/src/cli.vala
@@ -5,6 +5,7 @@ static bool list;
static string invoke;
static int close_n;
static int get_n;
+static bool toggle_dnd;
const OptionEntry[] options = {
{ "version", 'v', OptionFlags.NONE, OptionArg.NONE, ref version, null, null },
@@ -14,6 +15,7 @@ const OptionEntry[] options = {
{ "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 },
+ { "toggle-dnd", 't', OptionFlags.NONE, OptionArg.NONE, ref toggle_dnd, null, null },
{ null },
};
@@ -34,17 +36,17 @@ int main(string[] argv) {
print("Usage:\n");
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(" -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");
+ 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");
+ print(" -t, --toggle-dnd Toggle do not disturb\n");
return 0;
}
- var loop = new MainLoop();
var notifd = new AstalNotifd.Notifd();
if (version) {
@@ -53,16 +55,27 @@ int main(string[] argv) {
}
if (list) {
- var cache = Environment.get_user_cache_dir() + "/astal/notifd/notifications.json";
- if (FileUtils.test(cache, FileTest.EXISTS)) {
+ var state = Environment.get_user_state_dir() + "/astal/notifd/notifications.json";
+ if (FileUtils.test(state, FileTest.EXISTS)) {
try {
uint8[] json;
- File.new_for_path(cache).load_contents(null, out json, null);
- stdout.printf("%s", (string)json);
+ File.new_for_path(state).load_contents(null, out json, null);
+
+ var obj = Json.from_string((string)json);
+
+ var list = obj.get_object().get_member("notifications");
+ stdout.printf("%s\n", Json.to_string(list, true));
+ return 0;
} catch (Error err) {
stderr.printf("failed to load cache: %s", err.message);
}
}
+ stdout.printf("[]\n");
+ return 0;
+ }
+
+ if (toggle_dnd) {
+ notifd.dont_disturb = !notifd.dont_disturb;
return 0;
}
@@ -71,6 +84,7 @@ int main(string[] argv) {
stdout.printf("%s\n", notifd.get_notification_json(id));
stdout.flush();
});
+ new MainLoop().run();
}
if (invoke != null) {
@@ -83,29 +97,19 @@ int main(string[] argv) {
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();
- });
+ notifd.get_notification(n_id).invoke(a_id);
}
if (close_n > 0) {
- notifd.active.connect(() => {
- notifd.get_notification(close_n).dismiss();
- loop.quit();
- });
+ notifd.get_notification(close_n).dismiss();
}
if (get_n > 0) {
- notifd.active.connect(() => {
- stdout.printf("%s", notifd.get_notification(get_n).to_json_string());
- loop.quit();
- });
+ stdout.printf("%s", notifd.get_notification(get_n).to_json_string());
}
if (!daemonize && invoke == null && close_n == 0 && get_n == 0)
return 1;
- loop.run();
return 0;
}
diff --git a/src/daemon.vala b/src/daemon.vala
index fe38364..b8fb598 100644
--- a/src/daemon.vala
+++ b/src/daemon.vala
@@ -1,30 +1,39 @@
-namespace AstalNotifd {
-public enum ClosedReason {
- EXPIRED = 1,
- DISMISSED_BY_USER = 2,
- CLOSED = 3,
- UNDEFINED = 4,
-}
-
[DBus (name = "org.freedesktop.Notifications")]
-internal class Daemon : Object {
+internal class AstalNotifd.Daemon : Object {
public static string name = "notifd";
public static string vendor = "astal";
public static string version = "0.1";
- private string cache_file;
+ private string state_file;
+ private string state_directory;
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 bool ignore_timeout { get; set; }
- public bool dont_disturb { get; set; }
+ private bool _ignore_timeout;
+ public bool ignore_timeout {
+ get { return _ignore_timeout; }
+ set {
+ _ignore_timeout = value;
+ write_state();
+ }
+ }
+
+ private bool _dont_disturb;
+ public bool dont_disturb {
+ get { return _dont_disturb; }
+ set {
+ _dont_disturb = value;
+ write_state();
+ }
+ }
public signal void notified(uint id, bool replaced);
public signal void resolved(uint id, ClosedReason reason);
public signal void action_invoked(uint id, string action);
+ public signal void prop_changed(string prop);
// emitting an event from proxy doesn't seem to work
public void emit_resolved(uint id, ClosedReason reason) { resolved(id, reason); }
@@ -32,24 +41,31 @@ internal class Daemon : Object {
construct {
cache_directory = Environment.get_user_cache_dir() + "/astal/notifd";
- cache_file = cache_directory + "/notifications.json";
+ state_directory = Environment.get_user_state_dir() + "/astal/notifd";
+ state_file = state_directory + "/notifications.json";
- if (FileUtils.test(cache_file, FileTest.EXISTS)) {
+ if (FileUtils.test(state_file, FileTest.EXISTS)) {
try {
uint8[] json;
- File.new_for_path(cache_file).load_contents(null, out json, null);
- var parser = new Json.Parser();
- parser.load_from_data((string)json);
- var list = parser.get_root().get_array();
+ File.new_for_path(state_file).load_contents(null, out json, null);
+
+ var obj = Json.from_string((string)json);
+
+ var list = obj.get_object().get_array_member("notifications");
for (var i = 0; i < list.get_length(); ++i) {
add_notification(new Notification.from_json(list.get_object_element(i)));
}
n_id = list.get_length() + 1;
+
+ _dont_disturb = obj.get_object().get_boolean_member("dont_disturb");
+ _ignore_timeout = obj.get_object().get_boolean_member("ignore_timeout");
} catch (Error err) {
warning("failed to load cache: %s", err.message);
}
}
+ notify.connect((prop) => prop_changed(prop.name));
+
notified.connect(() => {
notify_property("notifications");
});
@@ -57,7 +73,7 @@ internal class Daemon : Object {
resolved.connect((id, reason) => {
notifs.get(id).resolved(reason);
notifs.remove(id);
- cache();
+ write_state();
notify_property("notifications");
notification_closed(id, reason);
});
@@ -123,7 +139,7 @@ internal class Daemon : Object {
notified(id, replaced);
- cache();
+ write_state();
return id;
}
@@ -135,21 +151,25 @@ internal class Daemon : Object {
return replaced;
}
- private void cache() {
+ private void write_state() {
var list = new Json.Builder().begin_array();
foreach (var n in notifications) {
list.add_value(n.to_json());
}
list.end_array();
- var generator = new Json.Generator();
- generator.set_root(list.get_root());
- var json = generator.to_data(null);
+
+ var obj = new Json.Builder()
+ .begin_object()
+ .set_member_name("notifications").add_value(list.get_root())
+ .set_member_name("ignore_timeout").add_boolean_value(ignore_timeout)
+ .set_member_name("dont_disturb").add_boolean_value(dont_disturb)
+ .end_object();
try {
- if (!FileUtils.test(cache_directory, FileTest.EXISTS))
- File.new_for_path(cache_directory).make_directory_with_parents(null);
+ if (!FileUtils.test(state_directory, FileTest.EXISTS))
+ File.new_for_path(state_directory).make_directory_with_parents(null);
- FileUtils.set_contents_full(cache_file, json);
+ FileUtils.set_contents_full(state_file, Json.to_string(obj.get_root(), false));
} catch (Error err) {
warning("failed to cache notifications: %s", err.message);
}
@@ -201,6 +221,9 @@ internal class Daemon : Object {
var file_name = cache_directory + "/" + data.hash().to_string("%u.png");
try {
+ if (!FileUtils.test(cache_directory, FileTest.EXISTS))
+ File.new_for_path(cache_directory).make_directory_with_parents(null);
+
var output_stream = File.new_for_path(file_name)
.replace(null, false, FileCreateFlags.NONE, null);
@@ -214,8 +237,7 @@ internal class Daemon : Object {
return file_name;
}
- [DBus (visible = false)]
- public Daemon register(DBusConnection conn) {
+ internal Daemon register(DBusConnection conn) {
try {
conn.register_object("/org/freedesktop/Notifications", this);
} catch (Error err) {
@@ -224,4 +246,10 @@ internal class Daemon : Object {
return this;
}
}
+
+public enum AstalNotifd.ClosedReason {
+ EXPIRED = 1,
+ DISMISSED_BY_USER = 2,
+ CLOSED = 3,
+ UNDEFINED = 4,
}
diff --git a/src/notifd.vala b/src/notifd.vala
index bb027f6..c962862 100644
--- a/src/notifd.vala
+++ b/src/notifd.vala
@@ -1,9 +1,10 @@
namespace AstalNotifd {
-public Notifd get_default() {
- return Notifd.get_default();
+ public Notifd get_default() {
+ return Notifd.get_default();
+ }
}
-public class Notifd : Object {
+public class AstalNotifd.Notifd : Object {
private static Notifd _instance;
public static Notifd get_default() {
if (_instance == null)
@@ -62,7 +63,13 @@ public class Notifd : Object {
construct {
// hack to make it synchronous
- var loop = new MainLoop();
+ MainLoop? loop = null;
+
+ if (!MainContext.default().is_owner()) {
+ loop = new MainLoop();
+ }
+
+ bool done = false;
Bus.own_name(
BusType.SESSION,
@@ -74,22 +81,23 @@ public class Notifd : Object {
);
active.connect(() => {
- if (loop.is_running())
+ done = true;
+ if (loop != null && loop.is_running()) {
loop.quit();
+ }
});
- loop.run();
+ if (loop != null) {
+ loop.run();
+ } else {
+ while (!done) {
+ MainContext.default().iteration(false);
+ }
+ }
}
private void acquire_daemon(DBusConnection conn) {
daemon = new Daemon().register(conn);
- daemon.notified.connect((id, replaced) => notified(id, replaced));
- daemon.resolved.connect((id, reason) => resolved(id, reason));
- daemon.notify.connect((prop) => {
- if (get_class().find_property(prop.name) != null) {
- notify_property(prop.name);
- }
- });
}
private void on_daemon_acquired() {
@@ -97,6 +105,13 @@ public class Notifd : Object {
proxy.stop();
proxy = null;
}
+ daemon.notified.connect((id, replaced) => notified(id, replaced));
+ daemon.resolved.connect((id, reason) => resolved(id, reason));
+ daemon.notify.connect((prop) => {
+ if (get_class().find_property(prop.name) != null) {
+ notify_property(prop.name);
+ }
+ });
active(ActiveType.DAEMON);
}
@@ -119,8 +134,7 @@ public class Notifd : Object {
}
}
-public enum ActiveType {
+public enum AstalNotifd.ActiveType {
DAEMON,
PROXY,
}
-}
diff --git a/src/notification.vala b/src/notification.vala
index 55d62d1..0b4af06 100644
--- a/src/notification.vala
+++ b/src/notification.vala
@@ -1,21 +1,16 @@
-namespace AstalNotifd {
-public enum Urgency {
+public enum AstalNotifd.Urgency {
LOW = 0,
NORMAL = 1,
CRITICAL = 2,
}
-public class Action {
- public Action(string id, string label) {
- this.id = id;
- this.label = label;
- }
+public struct AstalNotifd.Action {
public string id;
public string label;
}
-public class Notification : Object {
- private List<Action> _actions;
+public class AstalNotifd.Notification : Object {
+ private List<Action?> _actions;
private HashTable<string, Variant> hints;
public int64 time { construct set; get; }
@@ -25,7 +20,7 @@ public class Notification : Object {
public string body { construct set; get; }
public uint id { construct set; get; }
public int expire_timeout { construct set; get; }
- public List<Action> actions { get { return _actions; } }
+ public List<Action?> actions { get { return _actions; } }
public string image { get { return get_str_hint("image-path"); } }
public bool action_icons { get { return get_bool_hint("action-icons"); } }
@@ -61,9 +56,12 @@ public class Notification : Object {
);
this.hints = hints;
- _actions = new List<Action>();
+ _actions = new List<Action?>();
for (var i = 0; i < actions.length; i += 2) {
- _actions.append(new Action(actions[i], actions[i + 1]));
+ _actions.append(Action() {
+ id = actions[i],
+ label = actions[i + 1]
+ });
}
}
@@ -109,13 +107,13 @@ public class Notification : Object {
}
break;
case "actions":
- _actions = new List<Action>();
+ _actions = new List<Action?>();
for (var i = 0; i < node.get_array().get_length(); ++i) {
var o = node.get_array().get_object_element(i);
- _actions.append(new Action(
- o.get_member("id").get_string(),
- o.get_member("label").get_string()
- ));
+ _actions.append(Action() {
+ id = o.get_member("id").get_string(),
+ label = o.get_member("label").get_string()
+ });
}
break;
default: break;
@@ -160,4 +158,3 @@ public class Notification : Object {
.get_root();
}
}
-}
diff --git a/src/proxy.vala b/src/proxy.vala
index f32abbd..bedb8b9 100644
--- a/src/proxy.vala
+++ b/src/proxy.vala
@@ -1,6 +1,5 @@
-namespace AstalNotifd {
[DBus (name = "org.freedesktop.Notifications")]
-internal interface IDaemon : Object {
+internal interface AstalNotifd.IDaemon : DBusProxy {
public abstract bool ignore_timeout { get; set; }
public abstract bool dont_disturb { get; set; }
@@ -9,12 +8,13 @@ internal interface IDaemon : Object {
public signal void notified(uint id, bool replaced);
public signal void resolved(uint id, ClosedReason reason);
+ public signal void prop_changed(string prop);
public abstract void emit_resolved(uint id, ClosedReason reason);
public abstract void emit_action_invoked(uint id, string action);
}
-internal class DaemonProxy : Object {
+internal class AstalNotifd.DaemonProxy : Object {
private HashTable<uint, Notification> notifs =
new HashTable<uint, Notification>((i) => i, (a, b) => a == b);
@@ -43,8 +43,8 @@ internal class DaemonProxy : Object {
public signal void notified(uint id, bool replaced);
public signal void resolved(uint id, ClosedReason reason);
- IDaemon proxy;
- List<ulong> ids = new List<ulong>();
+ private IDaemon proxy;
+ private List<ulong> ids = new List<ulong>();
public void stop() {
if (ids.length() > 0) {
@@ -72,8 +72,8 @@ internal class DaemonProxy : Object {
var version = variant.get_child_value(2).get_string();
var running = name == Daemon.name
- && vendor == Daemon.vendor
- && version == Daemon.version;
+ && vendor == Daemon.vendor
+ && version == Daemon.version;
if (running) {
setup_proxy();
@@ -97,19 +97,21 @@ internal class DaemonProxy : Object {
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.prop_changed.connect((prop) => {
+ if (prop == "ignore-timeout" || prop == "dont-disturb")
+ notify_property(prop);
}));
ids.append(proxy.notified.connect((id, replaced) => {
add_notification(id);
notified(id, replaced);
+ notify_property("notifications");
}));
ids.append(proxy.resolved.connect((id, reason) => {
notifs.remove(id);
resolved(id, reason);
+ notify_property("notifications");
}));
}
@@ -125,4 +127,3 @@ internal class DaemonProxy : Object {
}
}
}
-}