/// macOS has a specific requirement that there must be a run loop running on the main thread in /// order to open windows and use OpenGL, and that the global NSApplication instance must be /// initialized. /// On macOS this launches the callback function on a thread. /// On other platforms it's just executed immediately. #[cfg(not(target_os = "macos"))] pub fn run T + Send + 'static>(main: F) -> T where T: Send + 'static, { main() } #[cfg(target_os = "macos")] pub fn run T + Send + 'static>(main: F) -> T where T: Send + 'static, { use std::{ ffi::c_void, sync::mpsc::{channel, Sender}, thread, }; 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 (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(); 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 }); app.run(); t.join().unwrap() } }