2024-08-25 20:37:59 +00:00
|
|
|
use dbus::arg::{RefArg, Variant};
|
|
|
|
use dbus::blocking::Connection;
|
|
|
|
use dbus::message::MatchRule;
|
|
|
|
use dbus::strings::Interface;
|
|
|
|
use dbus::MessageType::Signal;
|
|
|
|
use dbus::{Message, Path};
|
2024-08-11 14:27:37 +00:00
|
|
|
use std::collections::HashMap;
|
2024-08-20 04:56:36 +00:00
|
|
|
use std::fmt;
|
2024-08-25 20:37:59 +00:00
|
|
|
use std::time::Duration;
|
2024-08-11 14:27:37 +00:00
|
|
|
|
2024-08-11 19:12:21 +00:00
|
|
|
// Structure of a notification
|
2024-08-11 11:31:05 +00:00
|
|
|
struct Notif {
|
|
|
|
app_name: String,
|
2024-08-11 19:12:21 +00:00
|
|
|
replace_id: Option<u32>,
|
|
|
|
ico: String,
|
2024-08-11 11:31:05 +00:00
|
|
|
summary: String,
|
|
|
|
body: String,
|
2024-08-11 14:27:37 +00:00
|
|
|
actions: Vec<(String, String)>,
|
|
|
|
hints: HashMap<String, String>,
|
2024-08-11 19:12:21 +00:00
|
|
|
expir_timeout: Option<u32>,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function to print the contents of the notification
|
|
|
|
impl fmt::Display for Notif {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2024-08-20 04:56:36 +00:00
|
|
|
writeln!(f, "AppName: {}", &self.app_name)?;
|
2024-08-11 19:12:21 +00:00
|
|
|
match self.replace_id {
|
2024-08-20 04:56:36 +00:00
|
|
|
Some(notif_id) => writeln!(f, "ReplaceId: {notif_id}")?,
|
|
|
|
None => writeln!(f, "None")?,
|
2024-08-11 19:12:21 +00:00
|
|
|
}
|
2024-08-20 04:56:36 +00:00
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Icon: {}\nSummary: {}\nBody: {}\nActions:\n",
|
|
|
|
&self.ico, &self.summary, &self.body
|
2024-08-11 19:12:21 +00:00
|
|
|
)?;
|
|
|
|
for (key, label) in &self.actions {
|
2024-08-20 04:56:36 +00:00
|
|
|
writeln!(f, "\t{key}: {label}")?;
|
2024-08-11 19:12:21 +00:00
|
|
|
}
|
2024-08-20 04:56:36 +00:00
|
|
|
writeln!(f, "Hints:")?;
|
2024-08-11 19:12:21 +00:00
|
|
|
for (key, value) in &self.hints {
|
2024-08-20 04:56:36 +00:00
|
|
|
writeln!(f, "\t{key}: {value}")?;
|
2024-08-11 19:12:21 +00:00
|
|
|
}
|
|
|
|
match self.expir_timeout {
|
2024-08-20 04:56:36 +00:00
|
|
|
Some(millisec) => writeln!(f, "Expiration Timeout: {millisec}")?,
|
|
|
|
None => writeln!(f, "None")?,
|
2024-08-11 19:12:21 +00:00
|
|
|
}
|
|
|
|
Ok(()) // Return Ok to indicate success
|
|
|
|
}
|
2024-08-10 21:29:13 +00:00
|
|
|
}
|
|
|
|
|
2024-08-11 11:31:05 +00:00
|
|
|
// Summary should be generally <= 40 chars, as per the specification.
|
|
|
|
fn truncate_summary(notif: &mut Notif) {
|
|
|
|
if notif.summary.len() > 40 {
|
|
|
|
notif.summary.truncate(39);
|
2024-08-20 04:56:36 +00:00
|
|
|
notif.summary.push('…');
|
2024-08-10 21:29:13 +00:00
|
|
|
}
|
2024-08-11 11:31:05 +00:00
|
|
|
}
|
2024-08-10 21:29:13 +00:00
|
|
|
|
2024-08-23 02:18:27 +00:00
|
|
|
// Callback function
|
2024-08-25 20:37:59 +00:00
|
|
|
fn handle_notif(signal: &dbus::Message) -> Result<(), dbus::Error> {
|
|
|
|
// Extract all items as variants
|
|
|
|
let items = signal.get_items();
|
|
|
|
|
|
|
|
// Helper functions to safely extract data
|
|
|
|
let get_string = |item: &dyn RefArg| item.as_str().unwrap_or_default().to_string();
|
|
|
|
let get_u32 = |item: &dyn RefArg| item.as_u64().unwrap_or_default() as u32;
|
|
|
|
let get_i32 = |item: &dyn RefArg| item.as_i64().unwrap_or_default() as i32;
|
|
|
|
|
|
|
|
// Extract the fields from the message
|
|
|
|
let app_name = get_string(&items[0]);
|
|
|
|
let replace_id = get_u32(&items[1]);
|
|
|
|
let ico = get_string(&items[2]);
|
|
|
|
let summary = get_string(&items[3]);
|
|
|
|
let body = get_string(&items[4]);
|
|
|
|
|
|
|
|
// Extract actions as Vec<String>
|
|
|
|
let actions = items[5].as_array().unwrap_or(&[]).iter()
|
|
|
|
.filter_map(|v| v.as_str().map(|s| s.to_string()))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
// Extract hints as HashMap<String, String>
|
|
|
|
let hints = items[6].as_dict().unwrap_or(&[]).iter()
|
|
|
|
.map(|(k, v)| (get_string(k), get_string(v)))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let expir_timeout = get_i32(&items[7]);
|
|
|
|
|
|
|
|
// Convert actions from Vec<String> to Vec<(String, String)>
|
|
|
|
let notif_actions = actions.chunks(2)
|
|
|
|
.map(|chunk| if chunk.len() == 2 { (chunk[0].clone(), chunk[1].clone()) } else { (chunk[0].clone(), "".to_string()) })
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let mut notif = Notif {
|
|
|
|
app_name,
|
|
|
|
replace_id: Some(replace_id),
|
|
|
|
ico,
|
|
|
|
summary,
|
|
|
|
body,
|
|
|
|
actions: notif_actions,
|
|
|
|
hints: notif_hints,
|
|
|
|
expir_timeout: if expir_timeout >= 0 {
|
|
|
|
Some(expir_timeout as u32)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
},
|
|
|
|
};
|
2024-08-23 02:18:27 +00:00
|
|
|
|
2024-08-25 20:37:59 +00:00
|
|
|
truncate_summary(&mut notif);
|
2024-08-23 02:18:27 +00:00
|
|
|
|
2024-08-25 20:37:59 +00:00
|
|
|
println!("{}", notif);
|
2024-08-23 02:18:27 +00:00
|
|
|
|
2024-08-25 20:37:59 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() -> Result<(), dbus::Error> {
|
2024-08-23 02:18:27 +00:00
|
|
|
// Connect to the system bus
|
|
|
|
let conn = Connection::new_session()?;
|
|
|
|
|
|
|
|
// Get the object path of the notification service
|
2024-08-25 20:37:59 +00:00
|
|
|
//let object_path = "/org/freedesktop/Notifications";
|
|
|
|
//let member = "Notify";
|
2024-08-23 02:18:27 +00:00
|
|
|
|
|
|
|
// Get the interface name of the notification service
|
2024-08-25 20:37:59 +00:00
|
|
|
//let interface_name = "org.freedesktop.Notifications".to_string();
|
2024-08-23 02:18:27 +00:00
|
|
|
|
2024-08-25 20:37:59 +00:00
|
|
|
// Create a match rule to listen for the notifications
|
|
|
|
let match_rule = MatchRule::new_signal("org.freedesktop.Notifications".into(), "Notify".into());
|
2024-08-23 02:18:27 +00:00
|
|
|
|
2024-08-25 20:37:59 +00:00
|
|
|
// Add the match rule to the connection
|
|
|
|
conn.add_match(match_rule, |msg, _conn, _| {
|
|
|
|
if let Err(e) = handle_notif(&msg) {
|
|
|
|
eprintln!("Error handling notification: {:?}", e);
|
|
|
|
}
|
|
|
|
true // Returning true keeps the callback active
|
|
|
|
})?;
|
2024-08-20 04:56:36 +00:00
|
|
|
|
2024-08-23 02:18:27 +00:00
|
|
|
// Wait for signals
|
2024-08-25 20:37:59 +00:00
|
|
|
loop {
|
|
|
|
conn.process(Duration::from_millis(1000))?;
|
|
|
|
}
|
2024-08-20 21:02:06 +00:00
|
|
|
|
2024-08-23 02:18:27 +00:00
|
|
|
Ok(())
|
2024-08-11 19:12:21 +00:00
|
|
|
}
|