mod formats; mod notification; use notification::to_notif; use std::collections::HashMap; use futures_util::stream::TryStreamExt; use zbus::{Connection, Result}; const SERVER_NAME: &str = "SNot"; // Server software name const VENDOR: &str = "candifloss.cc"; // Server software vendor const VERSION: &str = "0.1.0"; // Server software version const SPEC_VERSION: &str = "0.42"; // DBus specification version const NOTIF_INTERFACE: &str = "org.freedesktop.Notifications"; // DBus interface // Properties of this server fn server_properties() -> HashMap { let mut properties: HashMap = HashMap::new(); properties.insert("ServerName".to_string(), String::from(SERVER_NAME)); properties.insert("Vendor".to_string(), String::from(VENDOR)); properties.insert("Version".to_string(), String::from(VERSION)); properties.insert("SpecVersion".to_string(), String::from(SPEC_VERSION)); properties } #[tokio::main] async fn main() -> Result<()> { let connection = Connection::session().await?; connection .request_name(NOTIF_INTERFACE) // Requesting dbus for this service name. Any other services/procs using this name should be stopped/disabled before this .await?; let mut stream = zbus::MessageStream::from(&connection); // Convert connection to a MessageStream, yields Message items // Iterate on the message stream while let Some(msg) = stream.try_next().await? { // Check the method calls in the received message's header if let Some(member) = msg.header().member() { let member_name = member.as_str(); // Convert to &str for the match match member_name { "GetAll" => { // Client requested all properties of the server. Extract the interface name from the message body let interface_name: String = msg.body().deserialize()?; // This method has only one parameter, the interface name // Check if the requested interface is the right one if interface_name == NOTIF_INTERFACE { // Properties for the interface let properties = server_properties(); // Reply with the properties connection.reply(&msg, &properties).await?; println!("Request received: GetAll for interface {interface_name}"); } else { println!("Unknown interface requested: {interface_name}"); // Reply with an error or empty hashmap let empty_props: HashMap = HashMap::new(); connection.reply(&msg, &empty_props).await?; } } "GetServerInformation" => { // Client requested server information. Respond with: (Server_name, author, software_version, dbus_spec_version) let response = (SERVER_NAME, VENDOR, VERSION, SPEC_VERSION); connection.reply(&msg, &response).await?; println!("Request received: {member}\n\tName: {SERVER_NAME}, Vendor: {VENDOR}, Version: {VERSION}, DBus spec version: {SPEC_VERSION}"); // Remove this LATER } "GetCapabilities" => { // Client requested server capabilities. Respond with the supported capabilities let capabilities = vec!["actions", "body", "body-hyperlinks"]; // Add more LATER connection.reply(&msg, &capabilities).await?; println!("Request received: {member}\n\tCapabilities: {capabilities:?}"); // Remove this LATER } "Notify" => { // New notification received. Now, respond to the client with a notification ID let notification_id: u32 = 1; // This could be incremented or generated. DO IT LATER connection.reply(&msg, ¬ification_id).await?; // The client waits for this response in order to disconnect // Get the body of the message let msg_body = msg.body(); // Convert the msg body to a Notification object let notif = to_notif(&msg_body)?; // Handle the notif println!("New notification!\n{}", ¬if.plain()); // Print the plain version } "CloseNotification" => { // Client sent a close signal. Extract notification ID of the notif to be closed from the message body let notification_id: u32 = msg.body().deserialize()?; // This method has only one parameter, the id // Tracking notifications by their IDs, closing them, and other features may be implemented later // close_notification(notification_id); println!("Closing notification with ID: {notification_id}"); // Respond to the client, acknowledging the closure connection.reply(&msg, &()).await?; } _ => { println!("Unhandled method: {member}"); // Other methods are either irrelevant or unhandled at this stage of development } } } } Ok(()) }