summary history branches tags files
commit:7db3c18b5a7c8bcade41c94b3757e04759df4f4e
author:Trevor Bentley
committer:GitHub
date:Sat Mar 20 19:26:45 2021 +0100
parents:9956b66760de76b5f013e7e6161517100cdd7148, 305136ac4816d266f41438570aa63778807d8bf3
Merge pull request #26 from jorgenpt/open-file-support

Add support for application:openFile:
diff --git a/examples/register_url.rs b/examples/register_url.rs
line changes: +13/-1
index 7a135d8..9ca9aec
--- a/examples/register_url.rs
+++ b/examples/register_url.rs
@@ -102,7 +102,19 @@ CFBundleURLTypes = ( {
                               stopper.stop();
                           }));
 
-    // Run 'forever', until the URL callback fires
+    let stopper = app.stopper();
+    app.register_callback(
+        FruitCallbackKey::Method("application:openFile:"),
+        Box::new(move |file| {
+            // File is a raw NSString.
+            // Fruitbasket has a converter to Rust String:
+            let file: String = fruitbasket::nsstring_to_string(file);
+            info!("Received file: {}", file);
+            stopper.stop();
+        }),
+    );
+
+    // Run 'forever', until one of the URL or file callbacks fire
     info!("Spawned process running!");
     let _ = app.run(RunPeriod::Forever);
     info!("Run loop stopped after URL callback.");

diff --git a/src/lib.rs b/src/lib.rs
line changes: +9/-0
index cbd0825..ceab61a
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -120,6 +120,9 @@ pub use osx::FruitCallbackKey;
 #[cfg(all(target_os = "macos", not(feature="dummy")))]
 pub use osx::parse_url_event;
 
+#[cfg(all(target_os = "macos", not(feature="dummy")))]
+pub use osx::nsstring_to_string;
+
 #[cfg(any(not(target_os = "macos"), feature="dummy"))]
 /// Docs in OS X build.
 pub enum FruitCallbackKey {
@@ -196,6 +199,12 @@ impl FruitApp {
 /// Docs in OS X build.
 pub fn parse_url_event(_event: *mut u64) -> String { "".into() }
 
+#[cfg(any(not(target_os = "macos"), feature = "dummy"))]
+/// Docs in OS X build.
+pub fn nsstring_to_string(_nsstring: *mut u64) -> String {
+    "".into()
+}
+
 /// API to move the executable into a Mac app bundle and relaunch (if necessary)
 ///
 /// Dummy implementation for non-OSX platforms.  See OS X build for proper

diff --git a/src/osx.rs b/src/osx.rs
line changes: +30/-3
index 7bddbaa..b777fe7
--- a/src/osx.rs
+++ b/src/osx.rs
@@ -857,13 +857,22 @@ pub fn parse_url_event(event: *mut Object) -> String {
         }
         let subevent: *mut Object = msg_send![event, paramDescriptorForKeyword: ::keyDirectObject];
         let nsstring: *mut Object = msg_send![subevent, stringValue];
+        nsstring_to_string(nsstring)
+    }
+}
+
+/// Convert an NSString to a Rust `String`
+pub fn nsstring_to_string(nsstring: *mut Object) -> String {
+    unsafe {
         let cstr: *const i8 = msg_send![nsstring, UTF8String];
         if cstr != std::ptr::null() {
-            let rstr = std::ffi::CStr::from_ptr(cstr).to_string_lossy().into_owned();
-            return rstr;
+            std::ffi::CStr::from_ptr(cstr)
+                .to_string_lossy()
+                .into_owned()
+        } else {
+            "".into()
         }
     }
-    "".into()
 }
 
 /// ObjcSubclass is a subclass of the objective-c NSObject base class.
@@ -925,6 +934,22 @@ impl INSObject for ObjcSubclass {
                                           FruitCallbackKey::Method("applicationWillFinishLaunching:"),
                                           event as *mut Object);
             }
+            /// NSApplication delegate callback
+            extern "C" fn objc_open_file(
+                this: &Object,
+                _cmd: Sel,
+                _application: u64,
+                file: u64,
+            ) -> bool {
+                let ptr: u64 = unsafe { *this.get_ivar("_rustwrapper") };
+                ObjcSubclass::dispatch_cb(
+                    ptr,
+                    FruitCallbackKey::Method("application:openFile:"),
+                    file as *mut Object,
+                );
+
+                true
+            }
             /// Register the Rust ObjcWrapper instance that wraps this object
             ///
             /// In order for an instance of this ObjC owned object to reach back
@@ -947,6 +972,8 @@ impl INSObject for ObjcSubclass {
                 decl.add_method(sel!(applicationDidFinishLaunching:), f);
                 let f: extern fn(&Object, Sel, u64) = objc_will_finish;
                 decl.add_method(sel!(applicationWillFinishLaunching:), f);
+                let f: extern "C" fn(&Object, Sel, u64, u64) -> bool = objc_open_file;
+                decl.add_method(sel!(application:openFile:), f);
             }
 
             decl.register();