// Usage notes:
//
// To access the touchbar, Rubrail *must* execute inside an OS X app bundle.
-// Since Cargo doesn't do that itself, this example is packaged with a second
-// wrapper example (example_launcher), and a bundling script (example.sh).
-// They can all be used together to setup and run the example from a bundle.
+// Since Cargo doesn't do that itself, this example uses the Trampoline feature
+// of the fruitbasket crate to relaunch itself in an app bundle.
//
// Simply run:
//
-// $ cargo test && cargo run --example example_launcher
+// $ cargo test && cargo run --example example
//
//
extern crate rubrail;
+extern crate fruitbasket;
use rubrail::Touchbar;
use rubrail::TTouchbar;
}
}
-fn populate(bar_rc: Rc<RefCell<Touchbar>>, count: u32) {
+fn populate(bar_rc: Rc<RefCell<Touchbar>>, count: u32, stopper: fruitbasket::FruitStopper) {
// Get touchbar from the refcell. It's wrapped in a cell so
// it can be passed around in the button callbacks.
let mut tb = (bar_rc).borrow_mut();
let mut barid = tb.create_bar();
// Create a quit button for root bar
- let quit_id = tb.create_button(None, Some("Quit"), Box::new(move |_| {rubrail::app::quit()}));
+ let quit_stopper = stopper.clone();
+ let quit_id = tb.create_button(None, Some("Quit"), Box::new(move |_| {quit_stopper.stop();}));
// Create an action button for the root bar. When clicked, it will
// close the bar and re-create itself.
let bar_copy = bar_rc.clone();
let text = format!("button{}", count);
let button1_id = tb.create_button(None, Some(&text), Box::new(move |_| {
- populate(bar_copy.clone(), count+1)
+ populate(bar_copy.clone(), count+1, stopper.clone())
}));
// Create a text label for the root bar
fn main() {
// Write log to home directory
- rubrail::app::create_logger(".rubrail.log");
-
- // Initialize OS X application. A real app should probably not use this.
- rubrail::app::init_app();
+ fruitbasket::create_logger(".rubrail.log", fruitbasket::LogDir::Home, 5, 2).unwrap();
+
+ // Initialize OS X application.
+ let icon = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
+ .join("examples").join("icon.png");
+ let mut nsapp = fruitbasket::Trampoline::new(
+ "rubrail_example", "rubrail_example", "com.trevorbentley.rubrail_example")
+ .icon("icon.png")
+ .version(env!("CARGO_PKG_VERSION"))
+ .plist_key("LSBackgroundOnly", "1")
+ .resource(icon.to_str().unwrap())
+ .build(fruitbasket::InstallDir::Custom("target/".to_string())).unwrap();
+ nsapp.set_activation_policy(fruitbasket::ActivationPolicy::Prohibited);
// Initialize the touchbar
let bar_rc = Rc::new(RefCell::new(Touchbar::alloc("bar")));
+ let stopper = nsapp.stopper();
// Populate the touchbar with UI elements
- populate(bar_rc.clone(), 1);
+ populate(bar_rc.clone(), 1, stopper);
- // Enter OS X application loop. A real application should probably implement
- // this itself.
- rubrail::app::run_forever();
+ // Enter OS X application loop.
+ nsapp.run(fruitbasket::RunPeriod::Forever);
}
-#!/bin/bash
-
-DST="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-APPDIR="RubrailExample.app"
-
-
-echo "Building OS X app..."
-
-rm -rf "$DST/$APPDIR"
-mkdir "$DST/$APPDIR/"
-mkdir "$DST/$APPDIR/Contents/"
-mkdir "$DST/$APPDIR/Contents/Resources/"
-mkdir "$DST/$APPDIR/Contents/MacOS/"
-
-cp -a "$DST/example" "$DST/$APPDIR/Contents/MacOS/"
-cp -a "$DST/icon.png" "$DST/$APPDIR/Contents/Resources/"
-
-cat > "$DST/$APPDIR/Contents/Info.plist" << EOF
-{
- CFBundleName = rubrail;
- CFBundleDisplayName = RubrailExample;
- CFBundleIdentifier = "com.trevorbentley.rubrail";
- CFBundleExecutable = example;
- CFBundleIconFile = "rubrail.icns";
-
- CFBundleVersion = "0.0.2";
- CFBundleInfoDictionaryVersion = "6.0";
- CFBundlePackageType = APPL;
- CFBundleSignature = xxxx;
-
- LSMinimumSystemVersion = "10.10.0";
-}
-EOF
-echo "Done!"
-
-fn main() {
- let exe = std::env::current_exe().unwrap();
- let mut exe_dir = exe.clone();
- exe_dir.pop();
- let mut resource_dir = exe_dir.clone();
- resource_dir.pop();
- resource_dir.pop();
- resource_dir.pop();
- resource_dir.push("examples");
- let mut example_app = exe_dir.clone();
- example_app.push("RubrailExample.app");
- let mut icon_src = resource_dir.clone();
- icon_src.push("icon.png");
- let mut script_src = resource_dir.clone();
- script_src.push("example.sh");
- let mut icon_dst = exe_dir.clone();
- icon_dst.push("icon.png");
- let mut script_dst = exe_dir.clone();
- script_dst.push("example.sh");
- let script_exe = script_dst.clone();
- std::fs::copy(icon_src, icon_dst).unwrap();
- std::fs::copy(script_src, script_dst).unwrap();
- let _ = std::process::Command::new(script_exe)
- .output()
- .expect("Failed to run bundling script");
- let _ = std::process::Command::new("open")
- .arg(example_app)
- .output()
- .expect("Failed to launch app bundle");
-}
//! be registered with the system and will not display on the Control Strip,
//! making it inaccessible to the user.
//!
-//! The examples are bundled with a script to generate a minimal valid app
-//! bundle, and a wrapper example to move the real example into a bundle and
-//! execute it. You can execute the examples correctly with this comand:
+//! The included example uses [fruitbasket](https://github.com/mrmekon/fruitbasket)
+//! to automatically bundle itself into an OS X app at runtime. You can run the
+//! example with:
//!
-//! `$ cargo test && cargo run --example example_launcher`
+//! `$ cargo test && cargo run --example example`
//!
//! # Memory Management
//!
#[cfg(not(all(target_os = "macos", feature = "private_api")))]
pub use dummy::util;
-/// Module for creating and running a simple Mac application
-///
-/// The `app` module contains helper functions for creating and running a very
-/// simple Mac application environment (NSApplication) with no window or dock
-/// icon. It is provided here for the Rubrail examples to use, and may be
-/// useful for simple Touch Bar-only applications, but 'real' applications
-/// should probably implement the application logic themselves.
-pub mod app {
- #[cfg(target_os = "macos")]
- #[cfg(feature = "private_api")]
- extern crate objc;
- extern crate log4rs;
- use std::env;
- #[cfg(not(all(target_os = "macos", feature = "private_api")))]
- use std::process;
-
- #[cfg(target_os = "macos")]
- #[cfg(feature = "private_api")]
- /// Initialize a Mac application environment (NSApplication)
- ///
- /// This is a debug/convenience function for creating a window-less Mac
- /// application. It can be used in simple Touch Bar-only applications,
- /// and is used by the examples, but more complex applications will want
- /// to handle the application creation themselves.
- ///
- /// It initializes an NSApplication with the
- /// _NSApplicationActivationPolicyAccessory_ policy, which means it will
- /// have no window and no dock icon.
- pub fn init_app() {
- unsafe {
- let cls = objc::runtime::Class::get("NSApplication").unwrap();
- let app: *mut objc::runtime::Object = msg_send![cls, sharedApplication];
- let _ = msg_send![app, setActivationPolicy: 1]; // NSApplicationActivationPolicyAccessory
- }
- }
- #[cfg(not(all(target_os = "macos", feature = "private_api")))]
- ///
- pub fn init_app() {}
-
- #[cfg(target_os = "macos")]
- #[cfg(feature = "private_api")]
- /// Run the application's event loop forever.
- ///
- /// This is a debug/convenience function to run the Mac application's event loop
- /// forever, without returning. It can be used with `init_app()` to run a
- /// simple application or example, but more complicated applications should
- /// implement the run loop themselves.
- pub fn run_forever() {
- unsafe {
- let cls = objc::runtime::Class::get("NSApplication").unwrap();
- let app: *mut objc::runtime::Object = msg_send![cls, sharedApplication];
- let _ = msg_send![app, run];
- }
- }
- #[cfg(not(all(target_os = "macos", feature = "private_api")))]
- ///
- pub fn run_forever() { loop {} }
-
- #[cfg(target_os = "macos")]
- #[cfg(feature = "private_api")]
- /// Terminate the application run loop and quit.
- ///
- /// This is a debug/convenience function to terminate the Mac application and
- /// end the process. This can be used with `init_app()` and `run_forever()` for
- /// simple applications and examples, but more complex applications will want
- /// to implement custom handling for terminating.
- pub fn quit() {
- unsafe {
- let cls = objc::runtime::Class::get("NSApplication").unwrap();
- let app: *mut objc::runtime::Object = msg_send![cls, sharedApplication];
- let _ = msg_send![app, terminate: 0];
- }
- }
- #[cfg(not(all(target_os = "macos", feature = "private_api")))]
- ///
- pub fn quit() {
- process::exit(0);
- }
-
- /// Enable logging to a file in the user's home directory
- ///
- /// This is a debug function which redirects the log output of Rubrail and its
- /// examples to a text file of the given name in the user's home directory.
- ///
- /// # Arguments
- ///
- /// * `filename` - the filename **without** a path. It is always saved in the
- /// home directory.
- ///
- /// # Example
- ///
- /// ```rust,no_run
- /// extern crate rubrail;
- /// #[macro_use]
- /// extern crate log;
- /// fn main() {
- /// rubrail::app::create_logger(".rubrail.log");
- /// info!("This message is in ~/.rubrail.log");
- /// }
- /// ```
- pub fn create_logger(filename: &str) {
- use log::LogLevelFilter;
- use self::log4rs::append::console::ConsoleAppender;
- use self::log4rs::append::file::FileAppender;
- use self::log4rs::encode::pattern::PatternEncoder;
- use self::log4rs::config::{Appender, Config, Logger, Root};
-
- let log_path = format!("{}/{}", env::home_dir().unwrap().display(), filename);
- let stdout = ConsoleAppender::builder()
- .encoder(Box::new(PatternEncoder::new("{m}{n}")))
- .build();
- let requests = FileAppender::builder()
- .build(&log_path)
- .unwrap();
-
- let config = Config::builder()
- .appender(Appender::builder().build("stdout", Box::new(stdout)))
- .appender(Appender::builder().build("requests", Box::new(requests)))
- .logger(Logger::builder().build("app::backend::db", LogLevelFilter::Info))
- .logger(Logger::builder()
- .appender("requests")
- .additive(false)
- .build("app::requests", LogLevelFilter::Info))
- .build(Root::builder().appender("stdout").appender("requests").build(LogLevelFilter::Info))
- .unwrap();
- let _ = log4rs::init_config(config).unwrap();
- }
-}
-
#[cfg(test)]
mod tests {
use Touchbar;