diff --git a/Cargo.toml b/Cargo.toml index 5728a7b..6212475 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,20 +10,21 @@ readme = "README.md" keywords = ["gui"] [dependencies] +libc="0.2" log="0.3" [target.'cfg(target_os = "windows")'.dependencies] -winapi="0.2" -user32-sys="0.2" -kernel32-sys="0.2" -libc="0.2" +winapi = "0.2" +user32-sys = "0.2" +kernel32-sys = "0.2" +shell32-sys = "0.1" [target.'cfg(target_os = "linux")'.dependencies] -gtk="^0.1.2" -glib="^0.1.2" -libappindicator="0.2" +gtk = "0.2" +glib = "0.3.1" +libappindicator = "0.3" -# [target.'cfg(target_os = "macos")'.dependencies] -# objc="*" -# cocoa="*" -# core-foundation="*" +[target.'cfg(target_os = "macos")'.dependencies] +objc = "0.2" +cocoa = "0.9" +core-foundation = "0.4" diff --git a/examples/rust-logo.png b/examples/rust-logo.png new file mode 100644 index 0000000..f9668c5 Binary files /dev/null and b/examples/rust-logo.png differ diff --git a/examples/systray-example.rs b/examples/trayicon.rs similarity index 67% rename from examples/systray-example.rs rename to examples/trayicon.rs index 837e20b..1ff83e1 100644 --- a/examples/systray-example.rs +++ b/examples/trayicon.rs @@ -1,14 +1,12 @@ extern crate systray; -//#[cfg(target_os = "windows")] +#[cfg(not(target_os = "windows"))] fn main() { let mut app; match systray::Application::new() { Ok(w) => app = w, Err(_) => panic!("Can't create window!") } - // w.set_icon_from_file(&"C:\\Users\\qdot\\code\\git-projects\\systray-rs\\resources\\rust.ico".to_string()); - // w.set_tooltip(&"Whatever".to_string()); app.set_icon_from_file(&"/usr/share/gxkb/flags/ua.png".to_string()).ok(); app.add_menu_item(&"Print a thing".to_string(), |_| { println!("Printing a thing!"); @@ -27,7 +25,15 @@ fn main() { app.wait_for_message(); } -// #[cfg(not(target_os = "windows"))] -// fn main() { -// panic!("Not implemented on this platform!"); -// } +#[cfg(target_os = "macos")] +fn main() { + let mut app; + match systray::Application::new() { + Ok(w) => app = w, + Err(_) => panic!("Can't create tray icon app!") + } + + const ICON_BUFFER: &'static [u8] = include_bytes!("rust-logo.png"); + app.set_icon_from_buffer(ICON_BUFFER, 256, 256).unwrap(); + app.wait_for_message(); +} diff --git a/src/api/cocoa/mod.rs b/src/api/cocoa/mod.rs index 686f2fc..e326405 100644 --- a/src/api/cocoa/mod.rs +++ b/src/api/cocoa/mod.rs @@ -1,28 +1,110 @@ +//! Contains the implementation of the Mac OS X tray icon in the top bar. + use std; -use {SystrayError}; +use cocoa::appkit::{NSApp, NSApplication, NSButton, NSImage, NSStatusBar, NSStatusItem, + NSSquareStatusItemLength}; +use cocoa::base::{id, nil}; +use cocoa::foundation::{NSData, NSSize, NSAutoreleasePool}; +use libc::c_void; + +use {SystrayEvent, SystrayError}; + +/// The generation representation of the Mac OS X application. pub struct Window { + /// A mutable reference to the `NSApplication` instance of the currently running application. + application: id, + /// It seems that we have to use `NSAutoreleasePool` to prevent memory leaks. + autorelease_pool: id, } impl Window { - pub fn new() -> Result { - Err(SystrayError::NotImplementedError) + /// Creates a new instance of the `Window`. + pub fn new(_: std::sync::mpsc::Sender) -> Result { + Ok(Window { + application: unsafe { NSApp() }, + autorelease_pool: unsafe { NSAutoreleasePool::new(nil) }, + }) } + + /// Closes the current application. pub fn quit(&self) { + unsafe { msg_send![self.application, terminate] }; + } + + pub fn shutdown(&self) -> Result<(), SystrayError> { unimplemented!() } + + /// Sets the tooltip (not available for this platfor). pub fn set_tooltip(&self, _: &String) -> Result<(), SystrayError> { + Err(SystrayError::OsError("This operating system does not support tooltips for the tray \ + items".to_owned())) + } + + /// Adds an additional item to the tray icon menu. + pub fn add_menu_entry(&self, _: u32, _: &String) -> Result { unimplemented!() } - pub fn add_menu_item(&self, _: &String, _: F) -> Result - where F: std::ops::Fn(&Window) -> () + 'static - { + + pub fn add_menu_separator(&self, _: u32) -> Result<(), SystrayError> { unimplemented!() } - pub fn wait_for_message(&mut self) { + + /// Sets the application icon displayed in the tray bar. Accepts a `buffer` to the underlying + /// image, you can pass even encoded PNG images here. Supports the same list of formats as + /// `NSImage`. + pub fn set_icon_from_buffer(&mut self, buffer: &[u8], _: u32, _: u32) + -> Result<(), SystrayError> + { + const ICON_WIDTH: f64 = 18.0; + const ICON_HEIGHT: f64 = 18.0; + + let tray_entry = unsafe { + NSStatusBar::systemStatusBar(nil).statusItemWithLength_(NSSquareStatusItemLength) + }; + + let nsdata = unsafe { + NSData::dataWithBytes_length_(nil, + buffer.as_ptr() as *const c_void, + buffer.len() as u64).autorelease() + }; + if nsdata == nil { + return Err(SystrayError::OsError("Could not create `NSData` out of the passed buffer" + .to_owned())); + } + + let nsimage = unsafe { NSImage::initWithData_(NSImage::alloc(nil), nsdata).autorelease() }; + if nsimage == nil { + return Err(SystrayError::OsError("Could not create `NSImage` out of the created \ + `NSData` buffer".to_owned())); + } + + unsafe { + let new_size = NSSize::new(ICON_WIDTH, ICON_HEIGHT); + msg_send![nsimage, setSize:new_size]; + tray_entry.button().setImage_(nsimage); + } + + Ok(()) + } + + pub fn set_icon_from_file(&self, _: &String) -> Result<(), SystrayError> { unimplemented!() } - pub fn set_icon_from_buffer(&self, _: &[u8], _: u32, _: u32) -> Result<(), SystrayError> { + + pub fn set_icon_from_resource(&self, _: &String) -> Result<(), SystrayError> { unimplemented!() } + + /// Starts the application event loop. Calling this function will block the current thread. + pub fn wait_for_message(&mut self) { + unsafe { self.application.run() }; + } +} + +impl Drop for Window { + fn drop(&mut self) { + unsafe { self.autorelease_pool.drain() }; + } } diff --git a/src/api/linux/mod.rs b/src/api/linux/mod.rs index 6847785..25516c5 100644 --- a/src/api/linux/mod.rs +++ b/src/api/linux/mod.rs @@ -171,6 +171,10 @@ impl Window { panic!("Not implemented on this platform!"); } + pub fn set_icon_from_buffer(&self, _: &[u8], _: u32, _: u32) -> Result<(), SystrayError> { + unimplemented!() + } + pub fn quit(&self) { glib::idle_add(|| { gtk::main_quit(); diff --git a/src/lib.rs b/src/lib.rs index 785f180..ea6a128 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,14 @@ extern crate kernel32; #[cfg(target_os = "windows")] extern crate user32; #[cfg(target_os = "windows")] +extern crate shell32; + +#[cfg(target_os = "macos")] +extern crate cocoa; +#[cfg(target_os = "macos")] +#[macro_use] +extern crate objc; + extern crate libc; #[cfg(target_os = "linux")] extern crate gtk; @@ -102,6 +110,12 @@ impl Application { self.window.set_icon_from_resource(resource) } + pub fn set_icon_from_buffer(&mut self, buffer: &[u8], width: u32, height: u32) + -> Result<(), SystrayError> + { + self.window.set_icon_from_buffer(buffer, width, height) + } + pub fn shutdown(&self) -> Result<(), SystrayError> { self.window.shutdown() }