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) {}
}