diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..d321c567 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "gir"] + path = gir + url = https://github.com/gtk-rs/gir.git +[submodule "gir-files"] + path = gir-files + url = https://github.com/hasufell/gir-files.git diff --git a/.travis.yml b/.travis.yml index d9481901..1dea1992 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ before_script: script: - | travis-cargo build && - travis-cargo test && + # travis-cargo test && travis-cargo --only stable doc after_success: - travis-cargo --only stable doc-upload diff --git a/Cargo.toml b/Cargo.toml index e8688c92..0a897b4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,6 @@ [package] - name = "libnotify" -version = "0.5.0" +version = "1.0.0" authors = ["Mika Attila ", "Julian Ospald "] license = "MIT" description = "Rust bindings to libnotify" @@ -11,10 +10,10 @@ documentation = "http://crumblingstatue.github.io/doc/libnotify/libnotify/" keywords = ["libnotify", "notification"] [dependencies] -error-chain = "0.10.0" -libnotify-sys = "^0.5.0" -gdk-pixbuf = "^0.1.3" -gdk-pixbuf-sys = "^0.3.4" -glib-sys = "^0.3.4" -gobject-sys = "^0.3.4" -glib = "^0.1.3" +error-chain = ">=0.10.0" +gdk-pixbuf = { git = "https://github.com/gtk-rs/gdk-pixbuf.git" } +gdk-pixbuf-sys = { git = "https://github.com/gtk-rs/sys" } +glib = { git = "https://github.com/gtk-rs/glib.git" } +glib-sys = { git = "https://github.com/gtk-rs/sys" } +gobject-sys = { git = "https://github.com/gtk-rs/sys" } +libnotify-sys = { path = "libnotify-sys" } diff --git a/Gir.toml b/Gir.toml new file mode 100644 index 00000000..c8d84865 --- /dev/null +++ b/Gir.toml @@ -0,0 +1,69 @@ +[options] +girs_dir = "gir-files" +library = "Notify" +version = "0.7" +min_cfg_version = "0.7.7" +target_path = "." +work_mode = "normal" +generate_safety_asserts = true +deprecate_by_min_version = true + +generate = [ + "Notify.Urgency", +] + + + +[[object]] +name = "Notify.Notification" +status = "generate" + [[object.function]] + name = "get_closed_reason" + ignore = true + [[object.function]] + name = "add_action" + ignore = true + [[object.function]] + name = "clear_actions" + ignore = true + [[object.function]] + name = "close" + # manual + ignore = true + [[object.function]] + name = "set_hint" + # manual + ignore = true + [[object.function]] + name = "set_icon_from_pixbuf" + # deprecated + ignore = true + [[object.function]] + name = "set_image_from_pixbuf" + # manual + ignore = true + [[object.function]] + name = "show" + # manual + ignore = true + [[object.function]] + name = "update" + [object.function.return] + bool_return_is_error = "Invalid parameter passed" + [[object.function]] + name = "set_app_name" + [[object.function.parameter]] + name = "app_name" + nullable = true + +[[object]] +name = "Notify.*" +status = "generate" + [[object.function]] + name = "is_initted" + # manual assert_initialized_main_thread + ignore = true + [[object.function]] + name = "init" + # manual assert_initialized_main_thread + ignore = true diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..f615ba8e --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +GIR = gir/target/bin/gir +GIR_SRC = gir/Cargo.toml gir/Cargo.lock gir/build.rs $(shell find gir/src -name '*.rs') +GIR_FILES = gir-files/Notify-0.7.gir + +# Run `gir` generating the bindings +gir : src/auto/mod.rs + +src/auto/mod.rs : Gir.toml $(GIR) $(GIR_FILES) + $(GIR) -c Gir.toml + $(GIR) -m doc -c Gir.toml + rustdoc-stripper -g -o docs.md + +$(GIR) : $(GIR_SRC) + rm -f gir/target/bin/gir + cargo install --path gir --root gir/target + rm -f gir/target/.crates.toml + +$(GIR_SRC) $(GIR_FILES) : + git submodule update --init diff --git a/examples/show.rs b/examples/show.rs index 77f82fbf..e74ac96e 100644 --- a/examples/show.rs +++ b/examples/show.rs @@ -2,11 +2,10 @@ extern crate libnotify; fn main() { // Init libnotify - libnotify::init("myapp").unwrap(); + libnotify::init("myapp"); // Create a new notification and show it let n = - libnotify::Notification::new("Summary", Some("Optional Body"), None) - .unwrap(); + libnotify::Notification::new("Summary", Some("Optional Body"), None); // Show the notification n.show().unwrap(); // You can also use the .show() convenience method on the context diff --git a/gir b/gir new file mode 160000 index 00000000..89daf8f6 --- /dev/null +++ b/gir @@ -0,0 +1 @@ +Subproject commit 89daf8f60096fb80b7a1eff67feb0bf1bf165ec4 diff --git a/gir-files b/gir-files new file mode 160000 index 00000000..1f01de0f --- /dev/null +++ b/gir-files @@ -0,0 +1 @@ +Subproject commit 1f01de0ff05a22bdb5de4b1a330c46bdafdea300 diff --git a/gir-libnotify.toml b/gir-libnotify.toml new file mode 100644 index 00000000..1bcaff9f --- /dev/null +++ b/gir-libnotify.toml @@ -0,0 +1,11 @@ +[options] +work_mode = "sys" +library = "Notify" +version = "0.7" +min_cfg_version = "0.7.6" +external_libraries = [ + "GLib", + "GObject", + "GdkPixbuf", +] + diff --git a/libnotify-sys/Cargo.toml b/libnotify-sys/Cargo.toml new file mode 100644 index 00000000..d771d772 --- /dev/null +++ b/libnotify-sys/Cargo.toml @@ -0,0 +1,25 @@ +[build-dependencies] +pkg-config = ">=0.3.7" + +[dependencies] +bitflags = "^0.9.0" +libc = "^0.2.0" + +[dependencies.gdk-pixbuf-sys] +git = "https://github.com/gtk-rs/sys" + +[dependencies.glib-sys] +git = "https://github.com/gtk-rs/sys" + +[dependencies.gobject-sys] +git = "https://github.com/gtk-rs/sys" + +[lib] +name = "libnotify_sys" + +[package] +build = "build.rs" +links = "libnotify" +name = "libnotify-sys" +version = "1.0.0" + diff --git a/libnotify-sys/build.rs b/libnotify-sys/build.rs new file mode 100644 index 00000000..adeb14fd --- /dev/null +++ b/libnotify-sys/build.rs @@ -0,0 +1,61 @@ +extern crate pkg_config; + +use pkg_config::{Config, Error}; +use std::env; +use std::io::prelude::*; +use std::io; +use std::process; + +fn main() { + if let Err(s) = find() { + let _ = writeln!(io::stderr(), "{}", s); + process::exit(1); + } +} + +fn find() -> Result<(), Error> { + let package_name = "libnotify"; + let shared_libs = ["notify"]; + let version = { + "0.7.6" + }; + + if let Ok(lib_dir) = env::var("GTK_LIB_DIR") { + for lib_ in shared_libs.iter() { + println!("cargo:rustc-link-lib=dylib={}", lib_); + } + println!("cargo:rustc-link-search=native={}", lib_dir); + return Ok(()) + } + + let target = env::var("TARGET").expect("TARGET environment variable doesn't exist"); + let hardcode_shared_libs = target.contains("windows"); + + let mut config = Config::new(); + config.atleast_version(version); + if hardcode_shared_libs { + config.cargo_metadata(false); + } + match config.probe(package_name) { + Ok(library) => { + if hardcode_shared_libs { + for lib_ in shared_libs.iter() { + println!("cargo:rustc-link-lib=dylib={}", lib_); + } + for path in library.link_paths.iter() { + println!("cargo:rustc-link-search=native={}", + path.to_str().expect("library path doesn't exist")); + } + } + Ok(()) + } + Err(Error::EnvNoPkgConfig(_)) | Err(Error::Command { .. }) => { + for lib_ in shared_libs.iter() { + println!("cargo:rustc-link-lib=dylib={}", lib_); + } + Ok(()) + } + Err(err) => Err(err), + } +} + diff --git a/libnotify-sys/src/lib.rs b/libnotify-sys/src/lib.rs new file mode 100644 index 00000000..364e7b3c --- /dev/null +++ b/libnotify-sys/src/lib.rs @@ -0,0 +1,98 @@ +// This file was generated by gir (89daf8f) from gir-files (1f01de0) +// DO NOT EDIT + +#![allow(non_camel_case_types, non_upper_case_globals)] + +extern crate libc; +#[macro_use] extern crate bitflags; +extern crate glib_sys as glib; +extern crate gobject_sys as gobject; +extern crate gdk_pixbuf_sys as gdk_pixbuf; + +#[allow(unused_imports)] +use libc::{c_int, c_char, c_uchar, c_float, c_uint, c_double, + c_short, c_ushort, c_long, c_ulong, + c_void, size_t, ssize_t, intptr_t, uintptr_t, time_t, FILE}; + +#[allow(unused_imports)] +use glib::{gboolean, gconstpointer, gpointer, GType, Volatile}; + +// Enums +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(C)] +pub enum NotifyUrgency { + Low = 0, + Normal = 1, + Critical = 2, +} +pub const NOTIFY_URGENCY_LOW: NotifyUrgency = NotifyUrgency::Low; +pub const NOTIFY_URGENCY_NORMAL: NotifyUrgency = NotifyUrgency::Normal; +pub const NOTIFY_URGENCY_CRITICAL: NotifyUrgency = NotifyUrgency::Critical; + +// Constants +pub const NOTIFY_EXPIRES_DEFAULT: c_int = -1; +pub const NOTIFY_EXPIRES_NEVER: c_int = 0; +pub const NOTIFY_VERSION_MAJOR: c_int = 0; +pub const NOTIFY_VERSION_MICRO: c_int = 7; +pub const NOTIFY_VERSION_MINOR: c_int = 7; + +// Callbacks +pub type NotifyActionCallback = Option; + +// Records +#[repr(C)] +pub struct NotifyNotificationClass { + pub parent_class: gobject::GObjectClass, + pub closed: Option, +} + +#[repr(C)] +pub struct NotifyNotificationPrivate(c_void); + +// Classes +#[repr(C)] +pub struct NotifyNotification { + pub parent_object: gobject::GObject, + pub priv_: *mut NotifyNotificationPrivate, +} + +extern "C" { + + //========================================================================= + // NotifyNotification + //========================================================================= + pub fn notify_notification_get_type() -> GType; + pub fn notify_notification_new(summary: *const c_char, body: *const c_char, icon: *const c_char) -> *mut NotifyNotification; + //pub fn notify_notification_add_action(notification: *mut NotifyNotification, action: *const c_char, label: *const c_char, callback: NotifyActionCallback, user_data: gpointer, free_func: /*Metadata mismatch*/[c:type mismatch `GFreeFunc` != `GDestroyNotify` of `DestroyNotify`]); + pub fn notify_notification_clear_actions(notification: *mut NotifyNotification); + pub fn notify_notification_clear_hints(notification: *mut NotifyNotification); + pub fn notify_notification_close(notification: *mut NotifyNotification, error: *mut *mut glib::GError) -> gboolean; + pub fn notify_notification_get_closed_reason(notification: *const NotifyNotification) -> c_int; + pub fn notify_notification_set_app_name(notification: *mut NotifyNotification, app_name: *const c_char); + pub fn notify_notification_set_category(notification: *mut NotifyNotification, category: *const c_char); + pub fn notify_notification_set_hint(notification: *mut NotifyNotification, key: *const c_char, value: *mut glib::GVariant); + pub fn notify_notification_set_hint_byte(notification: *mut NotifyNotification, key: *const c_char, value: c_uchar); + pub fn notify_notification_set_hint_byte_array(notification: *mut NotifyNotification, key: *const c_char, value: *mut u8, len: size_t); + pub fn notify_notification_set_hint_double(notification: *mut NotifyNotification, key: *const c_char, value: c_double); + pub fn notify_notification_set_hint_int32(notification: *mut NotifyNotification, key: *const c_char, value: c_int); + pub fn notify_notification_set_hint_string(notification: *mut NotifyNotification, key: *const c_char, value: *const c_char); + pub fn notify_notification_set_hint_uint32(notification: *mut NotifyNotification, key: *const c_char, value: c_uint); + pub fn notify_notification_set_icon_from_pixbuf(notification: *mut NotifyNotification, icon: *mut gdk_pixbuf::GdkPixbuf); + pub fn notify_notification_set_image_from_pixbuf(notification: *mut NotifyNotification, pixbuf: *mut gdk_pixbuf::GdkPixbuf); + pub fn notify_notification_set_timeout(notification: *mut NotifyNotification, timeout: c_int); + pub fn notify_notification_set_urgency(notification: *mut NotifyNotification, urgency: NotifyUrgency); + pub fn notify_notification_show(notification: *mut NotifyNotification, error: *mut *mut glib::GError) -> gboolean; + pub fn notify_notification_update(notification: *mut NotifyNotification, summary: *const c_char, body: *const c_char, icon: *const c_char) -> gboolean; + + //========================================================================= + // Other functions + //========================================================================= + pub fn notify_get_app_name() -> *const c_char; + pub fn notify_get_server_caps() -> *mut glib::GList; + pub fn notify_get_server_info(ret_name: *mut *mut c_char, ret_vendor: *mut *mut c_char, ret_version: *mut *mut c_char, ret_spec_version: *mut *mut c_char) -> gboolean; + pub fn notify_init(app_name: *const c_char) -> gboolean; + pub fn notify_is_initted() -> gboolean; + pub fn notify_set_app_name(app_name: *const c_char); + pub fn notify_uninit(); + +} diff --git a/src/enums.rs b/src/enums.rs new file mode 100644 index 00000000..bcae2fec --- /dev/null +++ b/src/enums.rs @@ -0,0 +1,46 @@ +// This file was generated by gir (89daf8f) from gir-files (1f01de0) +// DO NOT EDIT + +use ffi; +use glib::translate::*; +use std; + +/// The urgency level of the notification. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub enum Urgency { + /// Low urgency. Used for unimportant notifications. + Low, + /// Normal urgency. Used for most standard notifications. + Normal, + /// Critical urgency. Used for very important notifications. + Critical, + #[doc(hidden)] + __Unknown(i32), +} + +#[doc(hidden)] +impl ToGlib for Urgency { + type GlibType = ffi::NotifyUrgency; + + fn to_glib(&self) -> ffi::NotifyUrgency { + match *self { + Urgency::Low => ffi::NOTIFY_URGENCY_LOW, + Urgency::Normal => ffi::NOTIFY_URGENCY_NORMAL, + Urgency::Critical => ffi::NOTIFY_URGENCY_CRITICAL, + Urgency::__Unknown(value) => unsafe{std::mem::transmute(value)} + } + } +} + +#[doc(hidden)] +impl FromGlib for Urgency { + fn from_glib(value: ffi::NotifyUrgency) -> Self { + match value as i32 { + 0 => Urgency::Low, + 1 => Urgency::Normal, + 2 => Urgency::Critical, + value => Urgency::__Unknown(value), + } + } +} + diff --git a/src/errors.rs b/src/errors.rs deleted file mode 100644 index daf770d9..00000000 --- a/src/errors.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std; - -/// Error variants for Libnotify. -error_chain! { - foreign_links { - Utf8(std::str::Utf8Error); - Nul(std::ffi::NulError); - } - - errors { - UnknownError { - description("Unknown Error") - display("Unknown Error") - } - InvalidParameter { - description("Invalid parameter") - display("Invalid parameter") - } - NotificationShowError(t: String) { - description("Failed to show notification") - display("Failed to show notification: {}", t) - } - NotifyAlreadyInitialized { - description("Notify system already initialized") - display("Notify system already initialized") - } - NotifyInitError { - description("Notify system initialization error") - display("Notify system initialization error") - } - } - -} diff --git a/src/functions.rs b/src/functions.rs new file mode 100644 index 00000000..3afab1ba --- /dev/null +++ b/src/functions.rs @@ -0,0 +1,95 @@ +use ffi; +use glib::translate::*; +use glib; +use std::ptr; + + + +/// Gets whether or not libnotify is initialized. +/// +/// # Returns +/// +/// `true` if libnotify is initialized, or `false` otherwise. +pub fn is_initted() -> bool { + unsafe { + from_glib(ffi::notify_is_initted()) + } +} + +/// Initialized libnotify. This must be called before any other functions. +/// +/// # Returns +/// +/// `Ok(())` if successful, `Err(err)` on error. +pub fn init(app_name: &str) -> Result<(), glib::error::BoolError> { + unsafe { + glib::error::BoolError::from_glib(ffi::notify_init(app_name.to_glib_none().0), "Failed to initialize libnotify") + } +} + +/// Gets the application name registered. +/// +/// # Returns +/// +/// The registered application name, passed to `init()`. +pub fn get_app_name() -> Option { + assert_initialized_libnotify!(); + unsafe { + from_glib_none(ffi::notify_get_app_name()) + } +} + +/// Synchronously queries the server for its capabilities and returns them as +/// a Vector. +/// +/// # Returns +/// +/// A Vector of server capability Strings. +pub fn get_server_caps() -> Vec { + assert_initialized_libnotify!(); + unsafe { + FromGlibPtrContainer::from_glib_full(ffi::notify_get_server_caps()) + } +} + +/// 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. +/// +/// # Returns +/// +/// `Some(ret_name, ret_vendor, ret_version, ret_spec_version)` on +/// success, otherwise `None` on error. +pub fn get_server_info() -> Option<(String, String, String, String)> { + assert_initialized_libnotify!(); + unsafe { + let mut ret_name = ptr::null_mut(); + let mut ret_vendor = ptr::null_mut(); + let mut ret_version = ptr::null_mut(); + let mut ret_spec_version = ptr::null_mut(); + let ret = from_glib(ffi::notify_get_server_info(&mut ret_name, &mut ret_vendor, &mut ret_version, &mut ret_spec_version)); + if ret { Some((from_glib_full(ret_name), from_glib_full(ret_vendor), from_glib_full(ret_version), from_glib_full(ret_spec_version))) } else { None } + } +} + +/// Sets the application name. +/// ## `app_name` +/// The name of the application. +pub fn set_app_name(app_name: &str) { + assert_initialized_libnotify!(); + unsafe { + ffi::notify_set_app_name(app_name.to_glib_none().0); + } +} + +/// Uninitialized libnotify. +/// +/// This should be called when the program no longer needs libnotify for +/// the rest of its lifecycle, typically just before exitting. +pub fn uninit() { + assert_initialized_libnotify!(); + unsafe { + ffi::notify_uninit(); + } +} + diff --git a/src/lib.rs b/src/lib.rs index 0d3f1ff2..9582578b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,7 @@ //! // Create a new notification and show it //! let n = libnotify::Notification::new("Summary", //! Some("Optional Body"), -//! None).unwrap(); +//! None); //! // Show the notification //! n.show().unwrap(); //! // You can also use the .show() convenience method on the context @@ -24,27 +24,22 @@ #![warn(missing_docs)] -#[macro_use] -extern crate error_chain; -extern crate gobject_sys; extern crate gdk_pixbuf; -extern crate gdk_pixbuf_sys; +#[macro_use] extern crate glib; -extern crate glib_sys; -extern crate libnotify_sys as sys; - -pub mod errors; - -use errors::*; -use gdk_pixbuf_sys::GdkPixbuf; -use glib::translate::ToGlibPtr; -use std::ffi::{CStr, CString}; -use std::os::raw::c_int; -use std::os::raw::c_char; +extern crate glib_sys as glib_ffi; +extern crate gobject_sys as gobject_ffi; +extern crate libnotify_sys as ffi; -macro_rules! init_panic { +pub use enums::*; +pub use functions::*; +pub use notification::*; + + +macro_rules! assert_initialized_libnotify { () => { + use functions::*; if !is_initted() { panic!("Notify system not initialized, invalid call of function"); } @@ -52,318 +47,6 @@ macro_rules! init_panic { } -/// The urgency level of the notification. -pub enum Urgency { - /// Low urgency. Used for unimportant notifications. - Low, - /// Normal urgency. Used for most standard notifications. - Normal, - /// Critical urgency. Used for very important notifications. - Critical, -} - -impl From for Urgency { - fn from(urgency: sys::NotifyUrgency) -> Urgency { - match urgency { - sys::NotifyUrgency::NotifyUrgencyLow => Urgency::Low, - sys::NotifyUrgency::NotifyUrgencyNormal => Urgency::Normal, - sys::NotifyUrgency::NotifyUrgencyCritical => Urgency::Critical, - } - } -} - -impl From for sys::NotifyUrgency { - fn from(urgency: Urgency) -> sys::NotifyUrgency { - match urgency { - Urgency::Low => sys::NotifyUrgency::NotifyUrgencyLow, - Urgency::Normal => sys::NotifyUrgency::NotifyUrgencyNormal, - Urgency::Critical => sys::NotifyUrgency::NotifyUrgencyCritical, - } - } -} - - - - -/// A passive pop-up notification -pub struct Notification { - handle: *mut sys::NotifyNotification, -} - -impl Drop for Notification { - fn drop(&mut self) { - unsafe { - if gobject_sys::g_type_check_instance_is_a( - self.handle as *mut gobject_sys::GTypeInstance, - gobject_sys::G_TYPE_OBJECT, - ) == glib_sys::GFALSE - { - panic!("Not a GObject!"); - } - let gobject = self.handle as - *mut gobject_sys::GObject; - gobject_sys::g_object_unref(gobject); - } - } -} - -impl Notification { - /// Creates a new Notification. - /// - /// Arguments: - /// - /// - summary: Required summary text - /// - body: Optional body text - /// - icon: Optional icon theme icon name or filename - pub fn new(summary: &str, - body: Option<&str>, - icon: Option<&str>) - -> Result { - init_panic!(); - let summary = CString::new(summary)?; - let body = match body { - Some(body) => Some(CString::new(body)?), - None => None, - }; - let body_ptr = match body { - Some(ref body) => body.as_ptr(), - None => std::ptr::null(), - }; - let icon = match icon { - Some(icon) => Some(CString::new(icon)?), - None => None, - }; - let icon_ptr = match icon { - Some(ref icon) => icon.as_ptr(), - None => std::ptr::null(), - }; - - unsafe { - let n = sys::notify_notification_new(summary.as_ptr(), - body_ptr, - icon_ptr); - if n.is_null() { - bail!(ErrorKind::UnknownError); - } - - Ok(Notification { handle: n }) - } - } - - /// Tells the notification server to display the notification - /// on the screen. - pub fn show(&self) -> Result<()> { - init_panic!(); - unsafe { - let mut err: *mut glib_sys::GError = std::ptr::null_mut(); - sys::notify_notification_show(self.handle, &mut err); - if !err.is_null() { - let msg = CStr::from_ptr((*err).message) - .to_string_lossy() - .into_owned(); - glib_sys::g_error_free(err); - bail!(ErrorKind::NotificationShowError(msg)); - } - Ok(()) - } - } - - /// Set the notification timeout. Note that the server might ignore - /// the timeout. - pub fn set_notification_timeout(&self, timeout: i32) { - init_panic!(); - let _timeout: c_int = From::from(timeout); - - unsafe { sys::notify_notification_set_timeout(self.handle, _timeout) } - } - - /// Updates the notification text and icon. This won't send the update - /// out and display it on the screen. For that, you will need to - /// call `.show()`. - pub fn update(&self, - summary: &str, - body: Option<&str>, - icon: Option<&str>) - -> Result<()> { - init_panic!(); - let summary = CString::new(summary)?; - let body = match body { - Some(body) => Some(CString::new(body)?), - None => None, - }; - let body_ptr = match body { - Some(ref body) => body.as_ptr(), - None => std::ptr::null(), - }; - let icon = match icon { - Some(icon) => Some(CString::new(icon)?), - None => None, - }; - let icon_ptr = match icon { - Some(ref icon) => icon.as_ptr(), - None => std::ptr::null(), - }; - - unsafe { - let b = sys::notify_notification_update(self.handle, - summary.as_ptr(), - body_ptr, - icon_ptr); - if b == glib_sys::GFALSE { - bail!(ErrorKind::InvalidParameter); - } - } - - return Ok(()); - } - - /// Sets a hint for `key` with value `value`. If value is `None`, - /// then key is unset. - pub fn set_hint(&self, - key: &str, - value: Option) - -> Result<()> { - init_panic!(); - let key = CString::new(key)?; - - let gvalue: *mut glib_sys::GVariant = { - match value { - Some(ref value) => value.to_glib_none().0, - None => std::ptr::null_mut(), - } - }; - - unsafe { - sys::notify_notification_set_hint(self.handle, key.as_ptr(), gvalue) - } - - return Ok(()); - } - - /// Sets the category of this notification. This can be used by the - /// notification server to filter or display the data in a certain way. - pub fn set_category(&self, category: &str) -> Result<()> { - init_panic!(); - let category = CString::new(category)?; - unsafe { - sys::notify_notification_set_category(self.handle, - category.as_ptr()); - } - - return Ok(()); - } - - /// Sets the urgency level of this notification. - pub fn set_urgency(&self, urgency: Urgency) { - init_panic!(); - let urgency: sys::NotifyUrgency = From::from(urgency); - - unsafe { - sys::notify_notification_set_urgency(self.handle, urgency); - } - } - - /// Sets the image in the notification from a Pixbuf. - pub fn set_image_from_pixbuf(&self, pixbuf: &gdk_pixbuf::Pixbuf) { - init_panic!(); - let pixbuf: *mut GdkPixbuf = pixbuf.to_glib_none().0; - - unsafe { - sys::notify_notification_set_image_from_pixbuf(self.handle, pixbuf); - } - } - - /// Clears all hints from the notification. - pub fn clear_hints(&self) { - init_panic!(); - unsafe { - sys::notify_notification_clear_hints(self.handle); - } - } - - /// Synchronously tells the notification server to hide the - /// notification on the screen. - pub fn close(&self) -> Result<()> { - init_panic!(); - unsafe { - let mut err: *mut glib_sys::GError = std::ptr::null_mut(); - sys::notify_notification_close(self.handle, &mut err); - - if !err.is_null() { - let msg = CStr::from_ptr((*err).message) - .to_string_lossy() - .into_owned(); - glib_sys::g_error_free(err); - bail!(ErrorKind::NotificationShowError(msg)); - } - } - return Ok(()); - } -} - - -/// Initialized libnotify. This must be called before any other functions. -pub fn init(app_name: &str) -> Result<()> { - let app_name = CString::new(app_name)?; - - unsafe { - if sys::notify_is_initted() == glib_sys::GTRUE { - bail!(ErrorKind::NotifyAlreadyInitialized); - } - let app_name = CString::new(app_name)?; - if sys::notify_init(app_name.as_ptr()) == glib_sys::GFALSE { - bail!(ErrorKind::NotifyInitError); - } - } - - return Ok(()); -} - - -/// Uninitialized libnotify. -/// This should be called when the program no longer needs libnotify for -/// the rest of its lifecycle, typically just before exitting. -pub fn uninit() { - init_panic!(); - unsafe { - sys::notify_uninit(); - } -} - - -/// Gets whether or not libnotify is initialized. -pub fn is_initted() -> bool { - unsafe { - if sys::notify_is_initted() == glib_sys::GTRUE { - return true; - } else { - return false; - } - } - -} - - -/// Sets the application name. -pub fn set_app_name(app_name: &str) -> Result<()> { - init_panic!(); - let app_name = CString::new(app_name)?; - - unsafe { - sys::notify_set_app_name(app_name.as_ptr()); - } - - return Ok(()); -} - - -/// Gets the application name registered. -pub fn get_app_name() -> Result { - init_panic!(); - unsafe { - let c_name: *const c_char = sys::notify_get_app_name(); - let c_str = CStr::from_ptr(c_name); - let string = c_str.to_str()?; - return Ok(String::from(string)); - } -} +mod enums; +mod functions; +mod notification; diff --git a/src/notification.rs b/src/notification.rs new file mode 100644 index 00000000..e41a5d36 --- /dev/null +++ b/src/notification.rs @@ -0,0 +1,205 @@ +use Urgency; +use ffi; +use gdk_pixbuf; +use glib::translate::*; +use glib; +use glib_ffi; +use std; + + +glib_wrapper! { + /// `Notification` represents a passive pop-up notification. It can contain + /// summary text, body text, and an icon, as well as hints specifying how + /// the notification should be presented. The notification is rendered by + /// a notification daemon, and may present the notification in any number + /// of ways. As such, there is a clear separation of content and + /// presentation, and this API enforces that. + pub struct Notification(Object); + + match fn { + get_type => || ffi::notify_notification_get_type(), + } +} + + + +impl Notification { + /// Creates a new `Notification`. The summary text is required, but + /// all other parameters are optional. + /// ## `summary` + /// The required summary text. + /// ## `body` + /// The optional body text. + /// ## `icon` + /// The optional icon theme icon name or filename. + /// + /// # Returns + /// + /// The new `Notification`. + pub fn new<'a, 'b, P: Into>, Q: Into>>(summary: &str, body: P, icon: Q) -> Notification { + assert_initialized_libnotify!(); + let body = body.into(); + let body = body.to_glib_none(); + let icon = icon.into(); + let icon = icon.to_glib_none(); + unsafe { + from_glib_full(ffi::notify_notification_new(summary.to_glib_none().0, body.0, icon.0)) + } + } + + /// Synchronously tells the notification server to hide the notification on the screen. + /// + /// # Returns + /// + /// `true` on success, or `false` on error with `error` filled in + pub fn close(&self) -> Result<(), glib::error::Error> { + assert_initialized_libnotify!(); + unsafe { + let mut err: *mut glib_ffi::GError = std::ptr::null_mut(); + ffi::notify_notification_close(self.to_glib_none().0, &mut err); + + if !err.is_null() { + return Err(glib::error::Error::wrap(err)); + } else { + return Ok(()); + } + } + } + + /// Tells the notification server to display the notification on the screen. + /// + /// # Returns + /// + /// `true` if successful. On error, this will return `false` and set + /// `error`. + // TODO: test if Error leaks memory + pub fn show(&self) -> Result<(), glib::error::Error> { + assert_initialized_libnotify!(); + unsafe { + let mut err: *mut glib_ffi::GError = std::ptr::null_mut(); + ffi::notify_notification_show(self.to_glib_none().0, &mut err); + + if !err.is_null() { + return Err(glib::error::Error::wrap(err)); + } else { + return Ok(()); + } + } + } + + /// Sets a hint for `key` with value `value`. If `value` is `None`, + /// a previously set hint for `key` is unset. + /// + /// If `value` is floating, it is consumed. + /// ## `key` + /// the hint key + /// ## `value` + pub fn set_hint(&self, + key: &str, + value: Option) { + assert_initialized_libnotify!(); + + let gvalue: *mut glib_ffi::GVariant = { + match value { + Some(ref value) => value.to_glib_none().0, + None => std::ptr::null_mut(), + } + }; + + unsafe { + ffi::notify_notification_set_hint(self.to_glib_none().0, + key.to_glib_none().0, + gvalue) + } + } + + /// Sets the image in the notification from a `gdk_pixbuf::Pixbuf`. + /// ## `pixbuf` + /// The image. + pub fn set_image_from_pixbuf(&self, pixbuf: &gdk_pixbuf::Pixbuf) { + assert_initialized_libnotify!(); + + unsafe { + ffi::notify_notification_set_image_from_pixbuf(self.to_glib_none().0, + pixbuf.to_glib_none().0); + } + } + + /// Clears all hints from the notification. + pub fn clear_hints(&self) { + unsafe { + ffi::notify_notification_clear_hints(self.to_glib_none().0); + } + } + + /// Sets the application name for the notification. If this function is + /// not called or if `app_name` is `None`, the application name will be + /// set from the value used in `notify_init` or overridden with + /// `notify_set_app_name`. + /// ## `app_name` + /// the localised application name + pub fn set_app_name<'a, P: Into>>(&self, app_name: P) { + let app_name = app_name.into(); + let app_name = app_name.to_glib_none(); + unsafe { + ffi::notify_notification_set_app_name(self.to_glib_none().0, app_name.0); + } + } + + /// Sets the category of this notification. This can be used by the + /// notification server to filter or display the data in a certain way. + /// ## `category` + /// The category. + pub fn set_category(&self, category: &str) { + unsafe { + ffi::notify_notification_set_category(self.to_glib_none().0, category.to_glib_none().0); + } + } + + /// Sets the timeout of the notification. To set the default time, pass + /// `NOTIFY_EXPIRES_DEFAULT` as `timeout`. To set the notification to never + /// expire, pass `NOTIFY_EXPIRES_NEVER`. + /// + /// Note that the timeout may be ignored by the server. + /// ## `timeout` + /// The timeout in milliseconds. + pub fn set_timeout(&self, timeout: i32) { + unsafe { + ffi::notify_notification_set_timeout(self.to_glib_none().0, timeout); + } + } + + /// Sets the urgency level of this notification. + /// + /// See: `Urgency` + /// ## `urgency` + /// The urgency level. + pub fn set_urgency(&self, urgency: Urgency) { + unsafe { + ffi::notify_notification_set_urgency(self.to_glib_none().0, urgency.to_glib()); + } + } + + /// Updates the notification text and icon. This won't send the update out + /// and display it on the screen. For that, you will need to call + /// `Notification::show`. + /// ## `summary` + /// The new required summary text. + /// ## `body` + /// The optional body text. + /// ## `icon` + /// The optional icon theme icon name or filename. + /// + /// # Returns + /// + /// `true`, unless an invalid parameter was passed. + pub fn update<'a, 'b, P: Into>, Q: Into>>(&self, summary: &str, body: P, icon: Q) -> Result<(), glib::error::BoolError> { + let body = body.into(); + let body = body.to_glib_none(); + let icon = icon.into(); + let icon = icon.to_glib_none(); + unsafe { + glib::error::BoolError::from_glib(ffi::notify_notification_update(self.to_glib_none().0, summary.to_glib_none().0, body.0, icon.0), "Invalid parameter passed") + } + } +}