use std::ffi::{CString, NulError, c_char, c_void};
use std::marker::PhantomData;
use std::sync::Arc;
use libloading::Library;
use libloading::os::unix::Symbol as UnixSymbol;
type NotifyInit = unsafe extern "C" fn(*const c_char) -> bool;
type NotifyUninit = unsafe extern "C" fn();
type NotifyNotificationNew =
unsafe extern "C" fn(*const c_char, *const c_char, *const c_char) -> *mut c_void;
type NotifyNotificationShow = unsafe extern "C" fn(*mut c_void, *mut *mut c_void) -> i32;
type NotifyNotificationSetTimeout = unsafe extern "C" fn(*mut c_void, i32);
struct NotificationSystem {
_lib: Library,
notify_init: UnixSymbol<NotifyInit>,
notify_uninit: UnixSymbol<NotifyUninit>,
notify_notification_new: UnixSymbol<NotifyNotificationNew>,
notify_notification_show: UnixSymbol<NotifyNotificationShow>,
notify_notification_set_timeout: UnixSymbol<NotifyNotificationSetTimeout>,
}
pub struct InitializedNotificationSystem {
inner: Arc<NotificationSystem>,
_marker: PhantomData<()>,
}
impl NotificationSystem {
pub fn new() -> Result<Self, libloading::Error> {
unsafe {
let lib = Library::new("libnotify.so.4")?;
Ok(Self {
notify_init: lib.get::<NotifyInit>(b"notify_init\0")?.into_raw(),
notify_uninit: lib.get::<NotifyUninit>(b"notify_uninit\0")?.into_raw(),
notify_notification_new: lib
.get::<NotifyNotificationNew>(b"notify_notification_new\0")?
.into_raw(),
notify_notification_show: lib
.get::<NotifyNotificationShow>(b"notify_notification_show\0")?
.into_raw(),
notify_notification_set_timeout: lib
.get::<NotifyNotificationSetTimeout>(b"notify_notification_set_timeout\0")?
.into_raw(),
_lib: lib,
})
}
}
pub fn init(self, app_name: &str) -> Result<InitializedNotificationSystem, NulError> {
let app_name = CString::new(app_name)?;
unsafe {
if (self.notify_init)(app_name.as_ptr()) {
Ok(InitializedNotificationSystem {
inner: Arc::new(self), _marker: PhantomData,
})
} else {
panic!("notify_init failed") }
}
}
}
impl Drop for NotificationSystem {
fn drop(&mut self) {
unsafe { (self.notify_uninit)() };
}
}
impl InitializedNotificationSystem {
pub fn new(app_name: &str) -> Result<Self, Box<dyn std::error::Error>> {
let lib = NotificationSystem::new()?;
Ok(lib.init(app_name)?)
}
pub fn show_notification(
&self,
summary: &str,
body: &str,
icon: &str,
timeout_ms: i32,
) -> Result<(), NotificationError> {
let summary = CString::new(summary)?;
let body = CString::new(body)?;
let icon = CString::new(icon)?;
unsafe {
let notification = (self.inner.notify_notification_new)(
summary.as_ptr(),
body.as_ptr(),
icon.as_ptr(),
);
(self.inner.notify_notification_set_timeout)(notification, timeout_ms);
let mut error_ptr: *mut c_void = std::ptr::null_mut();
let success = (self.inner.notify_notification_show)(notification, &mut error_ptr);
if success != 0 {
Ok(())
} else {
Err(NotificationError::NativeError(error_ptr))
}
}
}
}
#[derive(Debug)]
pub enum NotificationError {
NullByteInString(NulError),
NativeError(*mut c_void),
}
impl From<NulError> for NotificationError {
fn from(e: NulError) -> Self {
NotificationError::NullByteInString(e)
}
}