use crate::api::errors::LibnotifyError; use super::utils::c_str_to_rs_str_and_free; use super::libnotify::*; /* Initting * *(what a cool section comment) */ /// Initializes libnotify. This must be called before any other functions. /// /// Starting from 0.8, if the provided app_name is NULL, libnotify will try to figure it out from the running application. Before it was not allowed, and was causing libnotify not to be initialized. /// /// # Example /// /// ``` /// use libnotify_rs::api::{ /// functions::init, /// notification:: Notification, /// }; /// /// fn main() -> Result<(), Box> { /// init("Test app")?; // we need to init libnotify __before__ any other libnotify code /// let notification = Notification::new("Test", None, None)?; // Some libnotify call /// Ok(()) /// } /// /// ``` /// /// # Errors /// This function can return LibnotifyError::{InitError, NulError} pub fn init<'a, T>(app_name: T) -> Result<(), LibnotifyError> where T: Into>, { let app_name = app_name.into().map(std::ffi::CString::new).transpose()?; if unsafe { notify_init( app_name .as_ref() .map_or(std::ptr::null(), |app_name| app_name.as_ptr()), ) } == 0 { return Err(LibnotifyError::InitError); } Ok(()) } /// Uninitializes libnotify. /// /// This should be called when the program no longer needs libnotify for the rest of its lifecycle, typically just before exitting. /// /// # Example /// ``` /// use libnotify_rs::api::functions::{init, uninit}; /// /// fn main() -> Result<(), Box> { /// init("Test app")?; /// // let notify = ... /// uninit(); // __always__ uninit libnotify after use /// Ok(()) /// } /// /// ``` pub fn uninit() { unsafe { notify_uninit(); } } /// Gets whether or not libnotify is initialized. /// /// # Example /// ``` /// use libnotify_rs::api::functions::{init, is_initted}; /// /// fn main() -> Result<(), Box> { /// if !is_initted() { // useful for conditional initing /// init("Test app")?; /// } /// /// Ok(()) /// } /// /// ``` pub fn is_initted() -> bool { unsafe { notify_is_initted() != 0 } } /* Getters * */ /// Gets the application name registered. /// /// # Example /// ``` /// use libnotify_rs::api::functions::{get_app_name, init}; /// /// fn main() -> Result<(), Box> { /// let app_name = "Test app"; /// init(app_name)?; /// let app_name_gotten: String = get_app_name().ok_or::("No name gotten".into())?; /// assert_eq!(app_name, app_name_gotten); /// Ok(()) /// } /// /// ``` pub fn get_app_name() -> Option { let app_name_c: *const std::os::raw::c_char = unsafe { notify_get_app_name() }; if app_name_c.is_null() { None } else { Some( unsafe { std::ffi::CStr::from_ptr(app_name_c) } .to_string_lossy() .to_string(), ) } } /// Gets the application icon registered. /// Available since: 0.8.4 /// /// # Example /// ``` /// use libnotify_rs::api::functions::{get_app_icon, get_app_name, init, set_app_icon}; /// /// fn main() -> Result<(), Box> { /// init("Test app")?; /// let icon = "gimp"; /// set_app_icon(icon)?; /// let icon_gotten = get_app_icon().ok_or::("No app icon set".into())?; /// assert_eq!(icon, icon_gotten); /// Ok(()) /// } /// ``` pub fn get_app_icon() -> Option { let app_icon_c: *const std::os::raw::c_char = unsafe { notify_get_app_icon() }; if app_icon_c.is_null() { None } else { Some( unsafe { std::ffi::CStr::from_ptr(app_icon_c) } .to_string_lossy() .to_string(), ) } } /// Queries the server capabilities. /// Synchronously queries the server for its capabilities and returns them in a list. /// /// # Example /// ``` /// use libnotify_rs::api::functions::{self, uninit}; /// /// fn main() -> Result<(), Box> { /// functions::init("Test app")?; /// /// if let Some(caps) = functions::get_server_caps() { /// println!("=== Server caps ==="); /// for s in caps { /// println!("{}", s); /// } /// } else { /// eprintln!("No server caps"); /// } /// /// Ok(()) /// } /// /// ``` pub fn get_server_caps() -> Option> { let mut vec: Vec = vec![]; let list = unsafe { notify_get_server_caps() }; if list.is_null() { return None; } let mut current = list; loop { let data = unsafe { *current }.data; let data_char = data as *const std::os::raw::c_char; if !data.is_null() { let s: String = unsafe { std::ffi::CStr::from_ptr(data_char) } .to_string_lossy() .to_string(); vec.push(s); } unsafe { g_free(data) }; if unsafe { *current }.next.is_null() { break; } current = unsafe { *current }.next; } unsafe { g_list_free(list) }; if vec.is_empty() { None } else { Some(vec) } } /// Contains server information. Refer to [libnotify get_server_info docs](https://gnome.pages.gitlab.gnome.org/libnotify/func.get_server_info.html) to better understand specific fields pub struct ServerInfo { /// A location to store the server name, or `None` pub name: Option, /// A location to store the server vendor, or `None` pub vendor: Option, /// A location to store the server version, or `None` pub version: Option, /// A location to store the version the service is compliant with, or `None` pub spec_version: Option, } /// Queries the server for information. /// Synchronously queries the server for its information, specifically, the name, vendor, server version, and the version of the notifications specification that it is compliant with. /// /// # Example /// ``` /// /// use libnotify_rs::api::functions::{self, uninit}; /// /// fn main() -> Result<(), Box> { /// functions::init("Test app")?; /// /// if let Some(info) = functions::get_server_info() { /// println!("=== Server info ==="); /// if let Some(name) = info.name { /// println!("Server name: {}", name); /// } /// /// if let Some(vendor) = info.vendor { /// println!("Server vendor: {}", vendor); /// } /// /// if let Some(version) = info.version { /// println!("Server version: {}", version); /// } /// /// if let Some(spec_version) = info.spec_version { /// println!("Server spec version: {}", spec_version); /// } /// } else { /// eprintln!("No server info"); /// } /// /// uninit(); /// /// Ok(()) /// } /// ``` pub fn get_server_info() -> Option { let mut ret_name = std::ptr::null_mut() as *mut std::os::raw::c_char; let mut ret_vendor = std::ptr::null_mut() as *mut std::os::raw::c_char; let mut ret_version = std::ptr::null_mut() as *mut std::os::raw::c_char; let mut ret_spec_version = std::ptr::null_mut() as *mut std::os::raw::c_char; if unsafe { notify_get_server_info( &mut ret_name, &mut ret_vendor, &mut ret_version, &mut ret_spec_version, ) } == 0 { return None; } Some(ServerInfo { name: unsafe { c_str_to_rs_str_and_free(ret_name) }, vendor: unsafe { c_str_to_rs_str_and_free(ret_vendor) }, version: unsafe { c_str_to_rs_str_and_free(ret_version) }, spec_version: unsafe { c_str_to_rs_str_and_free(ret_spec_version) }, }) } /* Setters * */ /// Sets the application name /// /// # Errors /// This function can return LibnotifyError::NulError /// /// # Example /// ``` /// use libnotify_rs::api::functions::{get_app_name, init, set_app_name}; /// /// fn main() -> Result<(), Box> { /// init("Test app")?; /// let app_name = "morbius app"; /// set_app_name(app_name)?; /// assert_eq!(app_name, get_app_name().unwrap()); /// Ok(()) /// } /// ``` pub fn set_app_name(app_name: &str) -> Result<(), LibnotifyError> { let app_name_c = std::ffi::CString::new(app_name)?; unsafe { notify_set_app_name(app_name_c.as_ptr()) }; Ok(()) } /// Sets the application icon. /// Available since: 0.8.4 /// /// app_icon is icon name or path to an image /// /// # Errors /// This function can return LibnotifyError::NulError /// /// # Example /// ``` /// use libnotify_rs::api::functions::{get_app_icon, init, set_app_icon}; /// /// fn main() -> Result<(), Box> { /// init("Test app")?; /// let app_icon = "morbius app"; /// set_app_icon(app_icon)?; /// assert_eq!(app_icon, get_app_icon().unwrap()); /// Ok(()) /// } /// ``` pub fn set_app_icon(app_icon: &str) -> Result<(), LibnotifyError> { let app_icon_c = std::ffi::CString::new(app_icon)?; unsafe { notify_set_app_icon(app_icon_c.as_ptr()) }; Ok(()) } #[cfg(test)] #[serial_test::serial] mod tests { use super::*; #[test] fn test_initializing() { assert!(!is_initted()); assert!(init("Morbius").is_ok()); assert!(is_initted()); uninit(); assert!(!is_initted()); } #[test] fn test_bad_init() { assert!(matches!( init("Mor\0ius").unwrap_err(), LibnotifyError::NulError(_) )); } #[test] fn test_app_name_set_and_get() { let first_name: &str = "Morbius"; let _ = init(first_name); assert_eq!(get_app_name().unwrap(), first_name); let new_name: &str = "Milo"; let _ = set_app_name(new_name); assert_eq!(get_app_name().unwrap(), new_name); uninit(); } #[test] fn test_app_name_bad_name() { let _ = init("Morbius"); let bad_name: &str = "mor\0ius"; assert!(set_app_name(bad_name).is_err()); uninit(); } }