pub fn request_authorization() {
#[cfg(target_os = "macos")]
platform::request_authorization();
}
pub fn send_notification(title: &str, body: &str) {
let title = if title.is_empty() {
"Rio".to_string()
} else {
title.to_string()
};
let body = body.to_string();
std::thread::spawn(move || {
platform::notify(&title, &body);
});
}
#[cfg(target_os = "macos")]
mod platform {
use block2::RcBlock;
use objc::runtime::Object;
use objc::{class, msg_send, sel, sel_impl};
use objc2::runtime::Bool;
use objc2_foundation::{NSError, NSString};
use objc2_user_notifications::{
UNAuthorizationOptions, UNMutableNotificationContent, UNNotificationRequest,
UNUserNotificationCenter,
};
use std::sync::Once;
pub(crate) fn request_authorization() {
static INIT: Once = Once::new();
INIT.call_once(|| unsafe {
let bundle: *mut Object = msg_send![class!(NSBundle), mainBundle];
if bundle.is_null() {
return;
}
let bundle_id: *mut Object = msg_send![bundle, bundleIdentifier];
if bundle_id.is_null() {
return;
}
let center = UNUserNotificationCenter::currentNotificationCenter();
center.requestAuthorizationWithOptions_completionHandler(
UNAuthorizationOptions::UNAuthorizationOptionAlert
| UNAuthorizationOptions::UNAuthorizationOptionSound,
&RcBlock::new(|_ok: Bool, _err: *mut NSError| {}),
);
});
}
pub fn notify(title: &str, body: &str) {
unsafe {
let bundle: *mut Object = msg_send![class!(NSBundle), mainBundle];
if bundle.is_null() {
return;
}
let bundle_id: *mut Object = msg_send![bundle, bundleIdentifier];
if bundle_id.is_null() {
return;
}
let center = UNUserNotificationCenter::currentNotificationCenter();
let content = UNMutableNotificationContent::new();
content.setTitle(&NSString::from_str(title));
content.setBody(&NSString::from_str(body));
let identifier = NSString::from_str("rio-notification");
let request = UNNotificationRequest::requestWithIdentifier_content_trigger(
&identifier,
&content,
None,
);
center.addNotificationRequest_withCompletionHandler(&request, None);
}
}
}
#[cfg(all(not(target_os = "macos"), not(target_os = "windows")))]
mod platform {
use std::collections::HashMap;
pub fn notify(title: &str, body: &str) {
let Ok(connection) = zbus::blocking::Connection::session() else {
return;
};
let Ok(proxy) = zbus::blocking::Proxy::new(
&connection,
"org.freedesktop.Notifications",
"/org/freedesktop/Notifications",
"org.freedesktop.Notifications",
) else {
return;
};
let hints: HashMap<&str, zbus::zvariant::Value<'_>> = HashMap::new();
let _: Result<u32, _> = proxy.call(
"Notify",
&(
"Rio", 0u32, "rio", title, body, &[] as &[&str], &hints, -1i32, ),
);
}
}
#[cfg(target_os = "windows")]
mod platform {
pub fn notify(title: &str, body: &str) {
use windows::core::HSTRING;
use windows::Data::Xml::Dom::XmlDocument;
use windows::UI::Notifications::{ToastNotification, ToastNotificationManager};
let Ok(xml) = XmlDocument::new() else {
return;
};
let toast_xml = format!(
r#"<toast><visual><binding template="ToastGeneric"><text>{}</text><text>{}</text></binding></visual></toast>"#,
title, body,
);
if xml.LoadXml(&HSTRING::from(&toast_xml)).is_err() {
return;
}
let Ok(toast) = ToastNotification::CreateToastNotification(&xml) else {
return;
};
let Ok(notifier) =
ToastNotificationManager::CreateToastNotifierWithId(&HSTRING::from("Rio"))
else {
return;
};
let _ = notifier.Show(&toast);
}
}