diff --git a/.github/app/Neothesia.app/Contents/Resources/default.sf2 b/.github/app/Neothesia.app/Contents/Resources/default.sf2 new file mode 100644 index 00000000..10221210 Binary files /dev/null and b/.github/app/Neothesia.app/Contents/Resources/default.sf2 differ diff --git a/.github/app/Neothesia.app/Contents/Resources/settings.ron b/.github/app/Neothesia.app/Contents/Resources/settings.ron new file mode 100644 index 00000000..47b3625b --- /dev/null +++ b/.github/app/Neothesia.app/Contents/Resources/settings.ron @@ -0,0 +1,4 @@ +( + speed_multiplier: 1, + playback_offset: 0, +) \ No newline at end of file diff --git a/.gitignore b/.gitignore index 53eddc96..725d200f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ /target **/*.rs.bk -settings.ron \ No newline at end of file +/settings.ron \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 35313b79..179726dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1803,6 +1803,7 @@ dependencies = [ "log", "midir", "nfd2", + "objc", "ron", "serde", "wgpu", diff --git a/Cargo.toml b/Cargo.toml index 885d7a02..86ae4cc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,5 +47,8 @@ serde = { version = "1.0.118", features = ["serde_derive"] } # console_error_panic_hook = "0.1.6" # console_log = "0.2.0" +[target.'cfg(target_os = "macos")'.dependencies] +objc = "0.2.7" + [build-dependencies] glsl-to-spirv = "0.1.7" diff --git a/src/config.rs b/src/config.rs index 35dcf7fc..bc9cb07c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,7 +8,8 @@ pub struct Config { impl Config { pub fn new() -> Self { - let config: Option = if let Ok(file) = std::fs::read_to_string("./settings.ron") { + let path = crate::resources::settings_ron(); + let config: Option = if let Ok(file) = std::fs::read_to_string(path) { match ron::from_str(&file) { Ok(config) => Some(config), Err(err) => { @@ -30,7 +31,8 @@ impl Config { impl Drop for Config { fn drop(&mut self) { if let Ok(s) = ron::ser::to_string_pretty(self, Default::default()) { - std::fs::write("./settings.ron", &s).ok(); + let path = crate::resources::settings_ron(); + std::fs::write(path, &s).ok(); } } } diff --git a/src/main.rs b/src/main.rs index f347e3b6..2e10a3f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,6 +28,8 @@ use winit::{ mod rectangle_pipeline; +mod resources; + pub struct MainState { pub midi_file: Option, pub output_manager: OutputManager, diff --git a/src/output_manager/mod.rs b/src/output_manager/mod.rs index 341fc6ce..c44f54b3 100644 --- a/src/output_manager/mod.rs +++ b/src/output_manager/mod.rs @@ -107,7 +107,7 @@ impl OutputManager { (desc, Box::new(synth.new_output_connection(&font))); self.selected_font_path = Some(font); } else { - let path = std::path::Path::new("./default.sf2"); + let path = crate::resources::default_sf2(); if path.exists() { let path = path.into(); self.output_connection = diff --git a/src/resources.rs b/src/resources.rs new file mode 100644 index 00000000..06d66ba3 --- /dev/null +++ b/src/resources.rs @@ -0,0 +1,50 @@ +pub fn default_sf2() -> PathBuff { + #[cfg(not(target_os = "macos"))] + return std::path::Path::new("./default.sf2"); + #[cfg(target_os = "macos")] + return bundled_resource_path("default", "sf2") + .unwrap_or(std::path::Path::new("./default.sf2")); +} + +pub fn settings_ron() -> PathBuff { + #[cfg(not(target_os = "macos"))] + return std::path::Path::new("./settings.ron"); + #[cfg(target_os = "macos")] + return bundled_resource_path("settings", "ron") + .unwrap_or(std::path::Path::new("./settings.ron")); +} + +#[cfg(target_os = "macos")] +fn bundled_resource_path(name: &str, extension: &str) -> Option { + use objc::runtime::{Class, Object}; + use objc::{msg_send, sel, sel_impl}; + + unsafe { + let cls = Class::get("NSBundle").unwrap(); + let bundle: *mut Object = msg_send![cls, mainBundle]; + let cls = Class::get("NSString").unwrap(); + let objc_str: *mut Object = msg_send![cls, alloc]; + let objc_name: *mut Object = msg_send![objc_str, + initWithBytes:name.as_ptr() + length:name.len() + encoding: 4]; // UTF8_ENCODING + let objc_str: *mut Object = msg_send![cls, alloc]; + let objc_ext: *mut Object = msg_send![objc_str, + initWithBytes:extension.as_ptr() + length:extension.len() + encoding: 4]; // UTF8_ENCODING + let ini: *mut Object = msg_send![bundle, + pathForResource:objc_name + ofType:objc_ext]; + let _: () = msg_send![objc_name, release]; + let _: () = msg_send![objc_ext, release]; + let cstr: *const i8 = msg_send![ini, UTF8String]; + if cstr != std::ptr::null() { + let rstr = std::ffi::CStr::from_ptr(cstr) + .to_string_lossy() + .into_owned(); + return Some(rstr); + } + None + } +}