From cf94792f128e2e2b32fe1d15370e3772a4773432 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 1 Sep 2021 15:33:59 -0700 Subject: [PATCH] Initial code for supporting notifications DBus daemon code; needs testing, and connection to UI. --- src/main.rs | 2 + src/notifications.rs | 166 +++++++++++++++++++++++++++++++++ src/status_notifier_watcher.rs | 1 + 3 files changed, 169 insertions(+) create mode 100644 src/notifications.rs diff --git a/src/main.rs b/src/main.rs index 0f275970..dbde99d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use gtk4::{gdk, glib, prelude::*}; mod deref_cell; mod mpris; mod mpris_player; +mod notifications; mod status_area; mod status_menu; mod status_notifier_watcher; @@ -39,6 +40,7 @@ fn main() { }); status_notifier_watcher::start(); + let _notificiations = notifications::Notifications::new(); glib::MainLoop::new(None, false).run(); } diff --git a/src/notifications.rs b/src/notifications.rs new file mode 100644 index 00000000..10e7c87d --- /dev/null +++ b/src/notifications.rs @@ -0,0 +1,166 @@ +use gtk4::{ + gio, + glib::{self, clone}, + prelude::*, +}; +use std::{ + collections::HashMap, + num::NonZeroU32, + sync::{Arc, Mutex}, +}; + +static NOTIFICATIONS_XML: &str = " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; + +pub struct Notifications { + next_id: Mutex, +} + +impl Notifications { + pub fn new() -> Arc { + let notifications = Arc::new(Notifications { + next_id: Mutex::new(NonZeroU32::new(1).unwrap()), + }); + + gio::bus_own_name( + gio::BusType::Session, + "org.freedesktop.Notifications", + gio::BusNameOwnerFlags::NONE, + clone!(@strong notifications => move |connection, name| notifications.bus_acquired(connection, name)), + clone!(@strong notifications => move |connection, name| notifications.name_acquired(connection, name)), + clone!(@strong notifications => move |connection, name| notifications.name_lost(connection, name)), + ); + + notifications + } + + fn notify( + &self, + _app_name: String, + replaces_id: Option, + _app_icon: String, + _summary: String, + _body: String, + _actions: Vec, + _hints: HashMap, + _expire_timeout: i32, + ) -> NonZeroU32 { + let id = replaces_id.unwrap_or_else(|| { + let mut next_id = self.next_id.lock().unwrap(); + let id = *next_id; + *next_id = NonZeroU32::new(u32::from(*next_id).wrapping_add(1)) + .unwrap_or(NonZeroU32::new(1).unwrap()); + id + }); + + // TODO + + id + } + + fn close_notification(&self, _id: u32) {} + + fn bus_acquired(self: &Arc, _connection: gio::DBusConnection, _name: &str) {} + + fn name_acquired(self: &Arc, connection: gio::DBusConnection, _name: &str) { + let introspection_data = gio::DBusNodeInfo::for_xml(NOTIFICATIONS_XML).unwrap(); + let interface_info = introspection_data + .lookup_interface("org.freedesktop.Notifications") + .unwrap(); + let method_call = clone!(@strong self as self_ => move |_connection: gio::DBusConnection, + _sender: &str, + _path: &str, + _interface: &str, + method: &str, + args: glib::Variant, + invocation: gio::DBusMethodInvocation| { + match method { + "Notify" => { + let (app_name, replaces_id, app_icon, summary, body, actions, hints, expire_timeout) = args.get().unwrap(); + let replaces_id = NonZeroU32::new(replaces_id); + let res = self_.notify(app_name, replaces_id, app_icon, summary, body, actions, hints, expire_timeout); + invocation.return_value(Some(&(u32::from(res),).to_variant())); + // TODO error? + } + "CloseNotification" => { + let (id,) = args.get::<(u32,)>().unwrap(); + self_.close_notification(id); + invocation.return_value(None); + // TODO error? + } + "GetCapabilities" => { + // TODO: body-markup, sound + let capabilities = vec!["actions", "body", "icon-static", "persistence"]; + invocation.return_value(Some(&(capabilities,).to_variant())); + } + "GetServerInformation" => { + let information = ("cosmic-panel", "system76", env!("CARGO_PKG_VERSION"), "1.2"); + invocation.return_value(Some(&information.to_variant())); + } + _ => unreachable!() + } + }); + let get_property = |_: gio::DBusConnection, + _sender: &str, + _path: &str, + _interface: &str, + _prop: &str| { unreachable!() }; + let set_property = |_: gio::DBusConnection, + _sender: &str, + _path: &str, + _interface: &str, + _prop: &str, + _value: glib::Variant| { unreachable!() }; + if let Err(err) = connection.register_object( + "/org/freedesktop/Notifications", + &interface_info, + method_call, + get_property, + set_property, + ) { + eprintln!("Failed to register object: {}", err); + } + } + + fn name_lost(self: &Arc, _connection: Option, _name: &str) {} +} diff --git a/src/status_notifier_watcher.rs b/src/status_notifier_watcher.rs index 46fc9ef6..54dee592 100644 --- a/src/status_notifier_watcher.rs +++ b/src/status_notifier_watcher.rs @@ -102,4 +102,5 @@ fn name_acquired(connection: gio::DBusConnection, _name: &str) { eprintln!("Failed to register object: {}", err); } } + fn name_lost(_connection: Option, _name: &str) {}