diff --git a/Cargo.lock b/Cargo.lock index 7e4db226b..97fcc5e0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -507,6 +507,7 @@ dependencies = [ "image", "memfd", "memmap2", + "objc", "pango", "pangocairo", "raw-window-handle", diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 960d9a088..94c772e95 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -52,6 +52,7 @@ windows = { version = "0.52", features=["Win32_Graphics_Direct3D11", [target.'cfg(target_os = "macos")'.dependencies] cocoa = "0.25" +objc = "0.2.7" [build-dependencies] gl_generator = { version = "0.14", optional = true } diff --git a/examples/src/examples-common.rs b/examples/src/examples-common.rs index 6d0e801ae..cf307b405 100644 --- a/examples/src/examples-common.rs +++ b/examples/src/examples-common.rs @@ -17,13 +17,47 @@ pub fn run T + Send + 'static>(main: F) -> T where T: Send + 'static, { - use std::thread; + use std::{ + ffi::c_void, + sync::mpsc::{channel, Sender}, + thread, + }; - use cocoa::appkit::NSApplication; + use cocoa::{ + appkit::{NSApplication, NSWindow}, + base::id, + delegate, + }; + use objc::{ + class, msg_send, + runtime::{Object, Sel}, + sel, sel_impl, + }; unsafe { let app = cocoa::appkit::NSApp(); - let t = thread::spawn(|| { + let (send, recv) = channel::<()>(); + + extern "C" fn on_finish_launching(this: &Object, _cmd: Sel, _notification: id) { + let send = unsafe { + let send_pointer = *this.get_ivar::<*const c_void>("send"); + let boxed = Box::from_raw(send_pointer as *mut Sender<()>); + *boxed + }; + send.send(()).unwrap(); + } + + let delegate = delegate!("AppDelegate", { + app: id = app, + send: *const c_void = Box::into_raw(Box::new(send)) as *const c_void, + (applicationDidFinishLaunching:) => on_finish_launching as extern fn(&Object, Sel, id) + }); + app.setDelegate_(delegate); + + let t = thread::spawn(move || { + // Wait for the NSApp to launch to avoid possibly calling stop_() too early + recv.recv().unwrap(); + let res = main(); let app = cocoa::appkit::NSApp(); diff --git a/tutorials/src/tutorials-common.rs b/tutorials/src/tutorials-common.rs index cac316fff..cf307b405 100644 --- a/tutorials/src/tutorials-common.rs +++ b/tutorials/src/tutorials-common.rs @@ -17,18 +17,66 @@ pub fn run T + Send + 'static>(main: F) -> T where T: Send + 'static, { - use std::thread; + use std::{ + ffi::c_void, + sync::mpsc::{channel, Sender}, + thread, + }; - use cocoa::appkit::NSApplication; - use objc::{msg_send, sel, sel_impl}; + use cocoa::{ + appkit::{NSApplication, NSWindow}, + base::id, + delegate, + }; + use objc::{ + class, msg_send, + runtime::{Object, Sel}, + sel, sel_impl, + }; unsafe { let app = cocoa::appkit::NSApp(); - let t = thread::spawn(|| { + let (send, recv) = channel::<()>(); + + extern "C" fn on_finish_launching(this: &Object, _cmd: Sel, _notification: id) { + let send = unsafe { + let send_pointer = *this.get_ivar::<*const c_void>("send"); + let boxed = Box::from_raw(send_pointer as *mut Sender<()>); + *boxed + }; + send.send(()).unwrap(); + } + + let delegate = delegate!("AppDelegate", { + app: id = app, + send: *const c_void = Box::into_raw(Box::new(send)) as *const c_void, + (applicationDidFinishLaunching:) => on_finish_launching as extern fn(&Object, Sel, id) + }); + app.setDelegate_(delegate); + + let t = thread::spawn(move || { + // Wait for the NSApp to launch to avoid possibly calling stop_() too early + recv.recv().unwrap(); + let res = main(); let app = cocoa::appkit::NSApp(); - let _: () = msg_send![app, terminate: cocoa::base::nil]; + app.stop_(cocoa::base::nil); + + // Stopping the event loop requires an actual event + let event = cocoa::appkit::NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_( + cocoa::base::nil, + cocoa::appkit::NSEventType::NSApplicationDefined, + cocoa::foundation::NSPoint { x: 0.0, y: 0.0 }, + cocoa::appkit::NSEventModifierFlags::empty(), + 0.0, + 0, + cocoa::base::nil, + cocoa::appkit::NSEventSubtype::NSApplicationActivatedEventType, + 0, + 0, + ); + app.postEvent_atStart_(event, cocoa::base::YES); res });