commit: | bbb3d0e88759321f1fd7d04cb55afc431a6b0bee |
author: | Trevor Bentley |
committer: | Trevor Bentley |
date: | Sun Jul 30 16:13:26 2017 +0200 |
parents: | eae57f0726a434dc8763e710f269d3c84788ab03 |
diff --git a/Cargo.toml b/Cargo.toml line changes: +6/-7 index 656e25f..da27fc3 --- a/Cargo.toml +++ b/Cargo.toml
@@ -1,15 +1,14 @@ [package] name = "connectr" version = "0.1.1-rc" -authors = [ "Trevor Bentley <trevor@trevorbentley.com>" ] - +authors = [ "Trevor Bentley <mrmekon@gmail.com>" ] description = "Spotify Connect library and systray/menubar application for controlling Spotify devices." keywords = ["spotify", "connect", "webapi", "systray", "menubar"] categories = ["api-bindings"] homepage = "https://github.com/mrmekon/connectr" repository = "https://github.com/mrmekon/connectr" +documentation = "https://mrmekon.github.io/connectr/connectr/" license = "Apache-2.0" - build = "build.rs" [lib]
@@ -45,10 +44,10 @@ rust-ini = "0.9" time = "0.1" timer = "0.1.6" chrono = "0.3.0" -libc = "0.2" log = "0.3.7" log4rs = "0.6.3" ctrlc = "3.0.1" +fruitbasket = "0.4" [target."cfg(windows)".dependencies] #systray = "0.1.1"
@@ -57,7 +56,7 @@ systray = {path = "deps/systray-rs", version="0.1.1-connectr"} [target."cfg(windows)".dependencies.rubrail] default-features=false -version = "0.4" +version = "0.5" #path = "deps/rubrail-rs" #version="0.4.3-rc"
@@ -65,7 +64,7 @@ version = "0.4" [target."cfg(all(unix, not(target_os = \"macos\")))".dependencies.rubrail] default-features = false -version = "0.4" +version = "0.5" #path = "deps/rubrail-rs" #version = "0.4.3-rc"
@@ -73,7 +72,7 @@ version = "0.4" cocoa = "0.9" objc-foundation = "0.1.1" objc_id = "0.1" -rubrail = "0.4" +rubrail = "0.5" #rubrail = {path = "deps/rubrail-rs", version="0.4.3-rc"} [target."cfg(target_os = \"macos\")".dependencies.objc]
diff --git a/connectr.xcf b/connectr.xcf line changes: +0/-0 index 27f3ce5..d065bbe --- a/connectr.xcf +++ b/connectr.xcf
diff --git a/deps/rubrail-rs b/deps/rubrail-rs line changes: +1/-1 index b56ccf5..6233cb4 --- a/deps/rubrail-rs +++ b/deps/rubrail-rs
@@ -1 +1 @@ -Subproject commit b56ccf5ddc4de61dcff892a61ba8de5cc92c5539 +Subproject commit 6233cb4575014f374c2f2549b2438f9e32f9d809
diff --git a/icon/connectr.icns b/icon/connectr.icns line changes: +0/-0 index 0000000..ca9553a --- /dev/null +++ b/icon/connectr.icns
diff --git a/icon/connectr1024.png b/icon/connectr1024.png line changes: +0/-0 index 0000000..18d782a --- /dev/null +++ b/icon/connectr1024.png
diff --git a/icon/gen_iconset.sh b/icon/gen_iconset.sh line changes: +15/-0 index 0000000..ff6998c --- /dev/null +++ b/icon/gen_iconset.sh
@@ -0,0 +1,15 @@ +#!/bin/bash +NAME="connectr" +mkdir "$NAME.iconset" +sips -z 16 16 "${NAME}1024.png" --out "$NAME.iconset"/icon_16x16.png +sips -z 32 32 "${NAME}1024.png" --out "$NAME.iconset"/icon_16x16@2x.png +sips -z 32 32 "${NAME}1024.png" --out "$NAME.iconset"/icon_32x32.png +sips -z 64 64 "${NAME}1024.png" --out "$NAME.iconset"/icon_32x32@2x.png +sips -z 128 128 "${NAME}1024.png" --out "$NAME.iconset"/icon_128x128.png +sips -z 256 256 "${NAME}1024.png" --out "$NAME.iconset"/icon_128x128@2x.png +sips -z 256 256 "${NAME}1024.png" --out "$NAME.iconset"/icon_256x256.png +sips -z 512 512 "${NAME}1024.png" --out "$NAME.iconset"/icon_256x256@2x.png +sips -z 512 512 "${NAME}1024.png" --out "$NAME.iconset"/icon_512x512.png +cp "${NAME}1024.png" "$NAME.iconset"/icon_512x512@2x.png +iconutil -c icns "$NAME.iconset" +rm -R "$NAME.iconset"
diff --git a/src/main.rs b/src/main.rs line changes: +23/-1 index 097d0b9..6ee5868 --- a/src/main.rs +++ b/src/main.rs
@@ -12,6 +12,8 @@ use rubrail::ImageTemplate; use rubrail::SpacerType; use rubrail::SwipeState; +extern crate fruitbasket; + extern crate ctrlc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc;
@@ -785,6 +787,22 @@ fn create_spotify_thread(rx_cmd: Receiver<String>) -> SpotifyThread { } fn main() { + // Relaunch in a Mac app bundle if running on OS X and not already bundled. + let icon = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("icon").join("connectr.icns"); + let touchbar_icon = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("connectr_80px_300dpi.png"); + if let Ok(nsapp) = fruitbasket::Trampoline::new( + "connectr", "connectr", "com.trevorbentley.connectr") + .icon("connectr.icns") + .version(env!("CARGO_PKG_VERSION")) + .plist_key("LSBackgroundOnly", "1") + .resource(icon.to_str().unwrap()) + .resource(touchbar_icon.to_str().unwrap()) + .build(fruitbasket::InstallDir::Custom("target/".to_string())) { + nsapp.set_activation_policy(fruitbasket::ActivationPolicy::Prohibited); + } + create_logger(); info!("Started Connectr");
@@ -832,11 +850,15 @@ fn main() { warn!("Didn't find Wine in search path."); } + let mut need_redraw: bool = false; while running.load(Ordering::SeqCst) { if spotify_thread.rx.recv_timeout(Duration::from_millis(100)).is_ok() { - // TODO: && status.can_redraw() + need_redraw = true; + } + if need_redraw && status.can_redraw() { clear_menu(&mut app, &mut status); fill_menu(&mut app, &spotify_thread, &mut status, &mut touchbar); + need_redraw = false; } status.run(false); }
diff --git a/src/osx/mod.rs b/src/osx/mod.rs line changes: +16/-72 index 41c607e..2a9753b --- a/src/osx/mod.rs +++ b/src/osx/mod.rs
@@ -3,7 +3,9 @@ mod rustnsobject; extern crate objc; extern crate objc_foundation; extern crate cocoa; -extern crate libc; + +extern crate fruitbasket; +use self::fruitbasket::FruitApp; pub use ::TStatusBar; pub use ::NSCallback;
@@ -12,11 +14,8 @@ use objc::runtime::Class; use self::cocoa::base::{nil, YES}; use self::cocoa::appkit::NSStatusBar; -use self::cocoa::foundation::{NSAutoreleasePool,NSString}; -use self::cocoa::appkit::{NSApp, - NSApplication, - NSApplicationActivationPolicyAccessory, - NSMenu, +use self::cocoa::foundation::NSString; +use self::cocoa::appkit::{NSMenu, NSMenuItem, NSImage, NSVariableStatusItemLength,
@@ -27,29 +26,15 @@ use self::rustnsobject::{NSObj, NSObjTrait, NSObjCallbackTrait}; use std::sync::mpsc::Sender; use std::ptr; -use std::cell::Cell; use std::ffi::CStr; -use std::thread::sleep; -use std::time::Duration; - -extern crate objc_id; -use self::objc_id::Id; pub type Object = objc::runtime::Object; pub struct OSXStatusBar { object: NSObj, - app: *mut objc::runtime::Object, + app: FruitApp, status_bar_item: *mut objc::runtime::Object, menu_bar: *mut objc::runtime::Object, - - // Run loop state - // Keeping these in persistent state instead of recalculating saves quite a - // bit of CPU during idle. - pool: Cell<*mut objc::runtime::Object>, - run_count: Cell<u64>, - run_mode: *mut objc::runtime::Object, - run_date: *mut objc::runtime::Object, } impl TStatusBar for OSXStatusBar {
@@ -57,21 +42,15 @@ impl TStatusBar for OSXStatusBar { fn new(tx: Sender<String>) -> OSXStatusBar { let mut bar; unsafe { - let app = NSApp(); + let nsapp = FruitApp::new(); + nsapp.set_activation_policy(fruitbasket::ActivationPolicy::Prohibited); let status_bar = NSStatusBar::systemStatusBar(nil); - let date_cls = Class::get("NSDate").unwrap(); bar = OSXStatusBar { - app: app, + app: nsapp, status_bar_item: status_bar.statusItemWithLength_(NSVariableStatusItemLength), menu_bar: NSMenu::new(nil), object: NSObj::alloc(tx).setup(), - pool: Cell::new(nil), - run_count: Cell::new(0), - run_mode: NSString::alloc(nil).init_str("kCFRunLoopDefaultMode"), - run_date: msg_send![date_cls, distantPast], }; - // Don't become foreground app on launch - bar.app.setActivationPolicy_(NSApplicationActivationPolicyAccessory); // Default mode for menu bar items: blue highlight when selected msg_send![bar.status_bar_item, setHighlightMode:YES];
@@ -85,7 +64,7 @@ impl TStatusBar for OSXStatusBar { // See docs/icons.md for explanation of icon files. // TODO: Use the full list of search paths. let icon_name = "connectr_80px_300dpi"; - let img_path = match bundled_resource_path(icon_name, "png") { + let img_path = match fruitbasket::FruitApp::bundled_resource_path(icon_name, "png") { Some(path) => path, None => format!("{}.png", icon_name), };
@@ -118,7 +97,6 @@ impl TStatusBar for OSXStatusBar { cb(sender, &s.tx); } )); - let _: () = msg_send![app, finishLaunching]; } bar }
@@ -212,27 +190,11 @@ impl TStatusBar for OSXStatusBar { } } fn run(&mut self, block: bool) { - loop { - unsafe { - let run_count = self.run_count.get(); - // Create a new release pool every once in a while, draining the old one - if run_count % 100 == 0 { - let old_pool = self.pool.get(); - if run_count != 0 { - let _ = msg_send![old_pool, drain]; - } - self.pool.set(NSAutoreleasePool::new(nil)); - } - let mode = self.run_mode; - let event: Id<Object> = msg_send![self.app, nextEventMatchingMask: -1 - untilDate: self.run_date inMode:mode dequeue: YES]; - let _ = msg_send![self.app, sendEvent: event]; - let _ = msg_send![self.app, updateWindows]; - self.run_count.set(run_count + 1); - } - if !block { break; } - sleep(Duration::from_millis(50)); - } + let period = match block { + true => fruitbasket::RunPeriod::Forever, + _ => fruitbasket::RunPeriod::Once, + }; + self.app.run(period); } }
@@ -257,25 +219,7 @@ pub fn resource_dir() -> Option<String> { let cls = Class::get("NSBundle").unwrap(); let bundle: *mut Object = msg_send![cls, mainBundle]; let path: *mut Object = msg_send![bundle, resourcePath]; - let cstr: *const libc::c_char = msg_send![path, UTF8String]; - if cstr != ptr::null() { - let rstr = CStr::from_ptr(cstr).to_string_lossy().into_owned(); - return Some(rstr); - } - None - } -} - -pub fn bundled_resource_path(name: &str, extension: &str) -> Option<String> { - unsafe { - let cls = Class::get("NSBundle").unwrap(); - let bundle: *mut Object = msg_send![cls, mainBundle]; - let res = NSString::alloc(nil).init_str(name); - let ext = NSString::alloc(nil).init_str(extension); - let ini: *mut Object = msg_send![bundle, pathForResource:res ofType:ext]; - let _ = msg_send![res, release]; - let _ = msg_send![ext, release]; - let cstr: *const libc::c_char = msg_send![ini, UTF8String]; + let cstr: *const i8 = msg_send![path, UTF8String]; if cstr != ptr::null() { let rstr = CStr::from_ptr(cstr).to_string_lossy().into_owned(); return Some(rstr);
diff --git a/src/settings/mod.rs b/src/settings/mod.rs line changes: +2/-10 index 7133d14..ae2a3d0 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs
@@ -2,9 +2,7 @@ extern crate ini; use self::ini::Ini; extern crate time; - -#[cfg(target_os = "macos")] -use super::osx; +extern crate fruitbasket; use std::env; use std::fs;
@@ -22,19 +20,13 @@ pub struct Settings { pub presets: Vec<(String,String)>, } -#[cfg(target_os = "macos")] fn bundled_ini() -> String { - match osx::bundled_resource_path("connectr", "ini") { + match fruitbasket::FruitApp::bundled_resource_path("connectr", "ini") { Some(path) => path, None => String::new(), } } -#[cfg(not(target_os = "macos"))] -fn bundled_ini() -> String { - String::new() -} - fn inifile() -> String { // Try to load INI file from home directory let path = format!("{}/.{}", env::home_dir().unwrap().display(), INIFILE);