mirror of
https://github.com/zoriya/astal.git
synced 2026-06-04 10:44:36 +00:00
fix proxy, add remaining cli functionality
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
default = notifd;
|
||||
notifd = pkgs.stdenv.mkDerivation {
|
||||
inherit nativeBuildInputs buildInputs;
|
||||
pname = "notifd";
|
||||
pname = "astal-notifd";
|
||||
version = version;
|
||||
src = ./.;
|
||||
outputs = ["out" "dev"];
|
||||
|
||||
+74
-14
@@ -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;
|
||||
}
|
||||
|
||||
+20
-14
@@ -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);
|
||||
|
||||
// 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 {
|
||||
if (cache_directory == null)
|
||||
cache_directory = Environment.get_user_cache_dir() + "/astal/notifd";
|
||||
|
||||
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 {
|
||||
|
||||
+5
-50
@@ -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();
|
||||
return get_notification(id).to_json_string();
|
||||
}
|
||||
|
||||
public signal void notified(uint id) {
|
||||
add_notification(id);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
+51
-18
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user