//! **Experimental** server taking the place of your Desktop Environment's Notification Server. //! //! This is not nearly meant for anything but testing, as it only prints notifications to stdout. //! It does not respond properly either yet. //! //! This server will not replace an already running notification server. //! #![allow(unused_imports, unused_variables, dead_code)] use std::cell::Cell; use std::collections::{HashMap, HashSet}; use std::sync::{Arc, Mutex}; #[cfg(feature = "dbus")] use dbus::{ arg::{self, RefArg}, ffidisp::{BusType, Connection, NameFlag}, tree::{self, Factory, Interface, MTFn, MTSync, Tree}, Path, }; use super::xdg::{NOTIFICATION_NAMESPACE, NOTIFICATION_OBJECTPATH}; use super::{Hint, Notification, Timeout}; static DBUS_ERROR_FAILED: &str = "org.freedesktop.DBus.Error.Failed"; /// Version of the crate equals the version server. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); /// An **experimental** notification server. /// See [the module level documentation](index.html) for more. #[derive(Debug, Default)] pub struct NotificationServer { /// Counter for generating notification ids counter: Mutex>, /// A flag that stops the server stopped: Mutex>, } impl NotificationServer { fn count_up(&self) { if let Ok(counter) = self.counter.lock() { counter.set(counter.get() + 1); } } fn stop(&self) { if let Ok(stop) = self.stopped.lock() { stop.set(true); } } fn is_stopped(&self) -> bool { if let Ok(stop) = self.stopped.lock() { stop.get() } else { true } } /// Create a new `NotificationServer` instance. pub fn create() -> Arc { Arc::new(NotificationServer::default()) } // pub fn notify_mothod(&mut self, closure: F) // -> Method // where F: Fn(&Notification) // { // fn handle_notification /// Start listening for incoming notifications pub fn start(me: &Arc, closure: F) where F: Fn(&Notification), { let connection = Connection::get_private(BusType::Session).unwrap(); connection.release_name(NOTIFICATION_NAMESPACE).unwrap(); connection .register_name(NOTIFICATION_NAMESPACE, NameFlag::ReplaceExisting as u32) .unwrap(); connection .register_object_path(NOTIFICATION_OBJECTPATH) .unwrap(); let mytex = Arc::new(Mutex::new(me.clone())); let factory = Factory::new_fn::<()>(); // D::Tree = () let tree = factory.tree(()).add( factory .object_path(NOTIFICATION_OBJECTPATH, ()) .introspectable() .add( factory .interface(NOTIFICATION_NAMESPACE, ()) .add_m(method_notify(&factory, closure)) .add_m(method_close_notification(&factory)) .add_m(Self::stop_server(mytex.clone(), &factory)) // .add_signal(method_notification_closed(&factory)) // .add_signal(method_action_invoked(&factory)) .add_m(method_get_capabilities(&factory)) .add_m(method_get_server_information(&factory)), ), ); connection.add_handler(tree); while !me.is_stopped() { // Wait for incoming messages. This will block up to one second. // Discard the result - relevant messages have already been handled. if let Some(received) = connection.incoming(1000).next() { println!("RECEIVED {:?}", received); } } } fn stop_server( me: Arc>>, factory: &Factory, ) -> tree::Method, ()> { factory .method("Stop", (), move |minfo| { if let Ok(me) = me.lock() { me.stop(); println!("STOPPING"); Ok(vec![]) } else { Err(tree::MethodErr::failed(&String::from("nope!"))) } }) .out_arg(("", "u")) } } fn hints_from_variants(hints: &HashMap) -> HashSet { hints.iter().map(Into::into).collect() } fn method_notify( factory: &Factory, on_notification: F, ) -> tree::Method, ()> where F: Fn(&Notification), { factory .method("Notify", (), move |minfo| { let mut i = minfo.msg.iter_init(); let appname: String = i.read()?; let replaces_id: u32 = i.read()?; let icon: String = i.read()?; let summary: String = i.read()?; let body: String = i.read()?; let actions: Vec = i.read()?; let hints: ::std::collections::HashMap>> = i.read()?; let timeout: i32 = i.read()?; println!("hints {:?} ", hints); // let arg0 = try!(d.notify(app_name, replaces_id, app_icon, summary, body, actions, hints, timeout)); let notification = Notification { appname, icon, summary, body, actions, hints: hints_from_variants(&hints), timeout: Timeout::from(timeout), id: if replaces_id == 0 { None } else { Some(replaces_id) }, subtitle: None, }; on_notification(¬ification); let arg0 = 43; let rm = minfo.msg.method_return(); let rm = rm.append1(arg0); Ok(vec![rm]) }) .in_arg(("app_name", "s")) .in_arg(("replaces_id", "u")) .in_arg(("app_icon", "s")) .in_arg(("summary", "s")) .in_arg(("body", "s")) .in_arg(("actions", "as")) .in_arg(("hints", "a{sv}")) .in_arg(("timeout", "i")) .out_arg(("", "u")) } fn method_close_notification(factory: &Factory) -> tree::Method, ()> { factory .method("CloseNotification", (), |minfo| { let i = minfo.msg.iter_init(); let rm = minfo.msg.method_return(); Ok(vec![rm]) }) .in_arg(("id", "u")) } fn method_get_capabilities(factory: &Factory) -> tree::Method, ()> { factory .method("GetCapabilities", (), |minfo| { let caps: Vec = vec![]; let rm = minfo.msg.method_return(); let rm = rm.append1(caps); Ok(vec![rm]) }) .out_arg(("caps", "as")) } fn method_get_server_information(factory: &Factory) -> tree::Method, ()> { factory .method("GetServerInformation", (), |minfo| { let (name, vendor, version, spec_version) = ( "notify-rust", "notify-rust", env!("CARGO_PKG_VERSION"), "0.0.0", ); let rm = minfo.msg.method_return(); let rm = rm.append1(name); let rm = rm.append1(vendor); let rm = rm.append1(version); let rm = rm.append1(spec_version); Ok(vec![rm]) }) .out_arg(("name", "s")) .out_arg(("vendor", "s")) .out_arg(("version", "s")) .out_arg(("spec_version", "s")) }