notif-picker/src/main.rs

76 lines
2.1 KiB
Rust
Raw Permalink Normal View History

mod format;
mod icon;
mod notification;
use std::io::Write;
use std::process::{Command, Stdio};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let notifications = notification::history()?;
if notifications.is_empty() {
return Ok(());
}
let entries: Vec<String> = notifications
.iter()
.map(|n| {
let app_name = n.app_name.as_deref().unwrap_or("unknown");
let summary = n.summary.as_deref().unwrap_or("");
let body = n.body.as_deref().unwrap_or("");
let text = format::entry_text(app_name, summary, body);
match icon::resolve(
n.app_icon.as_deref(),
n.desktop_entry.as_deref(),
n.app_name.as_deref(),
) {
Some(path) => format!("img:{}:text:{text}", path.display()),
None => text,
}
})
.collect();
let Some(index) = prompt_selection(&entries)? else {
return Ok(());
};
if let Some(notification) = notifications.get(index) {
notification::invoke(notification.id)?;
}
Ok(())
}
/// Run wofi in dmenu mode with the given entries and return the selected
/// index, or `None` if the user cancelled (Escape / clicked outside).
fn prompt_selection(entries: &[String]) -> Result<Option<usize>, Box<dyn std::error::Error>> {
let mut child = Command::new("wofi")
.args([
"--dmenu",
"-p",
"Notifications",
"-D",
"dmenu-print_line_num=true",
])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
let mut stdin = child.stdin.take().expect("stdin was piped");
stdin.write_all(entries.join("\n").as_bytes())?;
drop(stdin);
let output = child.wait_with_output()?;
if !output.status.success() {
return Ok(None);
}
let selection = String::from_utf8(output.stdout)?;
let selection = selection.trim();
if selection.is_empty() {
return Ok(None);
}
Ok(selection.parse::<usize>().ok())
}