use std::{io::Error as IOError, path::PathBuf, process::ExitCode};
mod platform_impl;
use platform_impl::{MainThreadMarker, NSApplication, NSBundle, Retained};
extern crate dirs;
pub enum InstallDir {
Temp,
SystemApplications,
UserApplications,
Custom(PathBuf),
}
pub struct Trampoline {
name: String,
ident: String,
version: String,
}
impl Trampoline {
pub fn new(name: &str, ident: &str) -> Self {
Trampoline {
name: name.to_string(),
ident: ident.to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
}
}
pub fn name(&mut self, name: &str) -> &mut Self {
self.name = name.to_string();
self
}
pub fn ident(&mut self, ident: &str) -> &mut Self {
self.ident = ident.to_string();
self
}
pub fn version(&mut self, version: &str) -> &mut Self {
self.version = version.to_string();
self
}
fn get_bundle() -> Option<Retained<NSBundle>> {
let bundle = NSBundle::mainBundle();
unsafe {
bundle.bundleIdentifier().map(|_| bundle)
}
}
pub fn is_bundled() -> bool {
Self::get_bundle().is_some()
}
pub fn bundle(&self, location: InstallDir) -> Result<Application, IOError> {
platform_impl::bundle(self, location)
}
#[cfg(feature = "winit")]
pub fn run_once<T>(&self, location: InstallDir, cb: T)
where
T: FnOnce(&Application) -> ExitCode + 'static,
{
use winit::{
application::ApplicationHandler,
event::WindowEvent,
event_loop::{ActiveEventLoop, EventLoop},
window::WindowId,
};
#[allow(clippy::type_complexity)]
struct WinitApp {
relaunch_app: Application,
cb: Option<Box<dyn FnOnce(&Application) -> ExitCode + 'static>>,
}
impl ApplicationHandler for WinitApp {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
let _ = (event_loop, window_id, event);
}
fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
if let Some(cb) = self.cb.take() {
let _ = cb(&self.relaunch_app);
event_loop.exit();
}
}
}
let event_loop = EventLoop::new().expect("Failed to create event loop");
let relaunch_app = self.bundle(location).unwrap_or_else(|error| {
eprintln!("Application relaunch failed: {}", error);
std::process::exit(1);
});
let mut winit_app = WinitApp {
relaunch_app,
cb: Some(Box::new(cb)),
};
if let Err(err) = event_loop.run_app(&mut winit_app) {
eprintln!("Event loop terminated with error: {}", err);
};
}
}
pub struct Application {
pub name: String,
pub ident: String,
pub bundle_path: PathBuf,
pub bundle: Retained<NSBundle>,
pub app: Retained<NSApplication>,
}
impl Application {
fn new(name: String, ident: String, bundle: Retained<NSBundle>) -> Self {
let mut bundle_path =
std::env::current_exe().expect("Could not determine path to current executable.");
bundle_path.pop(); bundle_path.pop(); bundle_path.pop();
let mtm =
MainThreadMarker::new().expect("Must call Application::new() from the main thread.");
let app = NSApplication::sharedApplication(mtm);
Self {
name,
ident,
bundle_path,
bundle,
app,
}
}
}