2021-07-12 10:13:34 +00:00
|
|
|
/// 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.
|
2017-11-12 18:07:02 +00:00
|
|
|
|
|
|
|
/// On macOS this launches the callback function on a thread.
|
|
|
|
/// On other platforms it's just executed immediately.
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
2017-11-12 19:11:25 +00:00
|
|
|
pub fn run<T, F: FnOnce() -> T + Send + 'static>(main: F) -> T
|
|
|
|
where
|
|
|
|
T: Send + 'static,
|
|
|
|
{
|
|
|
|
main()
|
2017-11-12 18:07:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(target_os = "macos")]
|
2017-11-12 19:11:25 +00:00
|
|
|
pub fn run<T, F: FnOnce() -> T + Send + 'static>(main: F) -> T
|
|
|
|
where
|
|
|
|
T: Send + 'static,
|
|
|
|
{
|
2024-01-29 13:12:40 +00:00
|
|
|
use std::{
|
|
|
|
ffi::c_void,
|
|
|
|
sync::mpsc::{channel, Sender},
|
|
|
|
thread,
|
|
|
|
};
|
2017-11-12 19:11:25 +00:00
|
|
|
|
2024-01-29 13:12:40 +00:00
|
|
|
use cocoa::{
|
|
|
|
appkit::{NSApplication, NSWindow},
|
|
|
|
base::id,
|
|
|
|
delegate,
|
|
|
|
};
|
|
|
|
use objc::{
|
|
|
|
class, msg_send,
|
|
|
|
runtime::{Object, Sel},
|
|
|
|
sel, sel_impl,
|
|
|
|
};
|
2023-01-03 18:58:25 +00:00
|
|
|
|
2021-07-12 10:13:34 +00:00
|
|
|
unsafe {
|
|
|
|
let app = cocoa::appkit::NSApp();
|
2024-01-29 13:12:40 +00:00
|
|
|
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();
|
|
|
|
|
2021-07-12 10:13:34 +00:00
|
|
|
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()
|
|
|
|
}
|
2017-11-12 18:07:02 +00:00
|
|
|
}
|