From a091ea201cf6eb650356d83921cf88dfa3f1bc2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 12 Jul 2021 13:13:34 +0300 Subject: [PATCH] examples/tutorials: Use cocoa crate to initialize the shared NSApplication instance This is required for OpenGL to work nowadays on macOS. Simply running an CFRunLoop on the main thread is not sufficient. Thanks to Philippe Normand for testing this on macOS and making sure it actually compiles and works. --- examples/Cargo.toml | 3 ++ examples/src/examples-common.rs | 79 +++++++++++++----------------- tutorials/Cargo.toml | 3 ++ tutorials/src/tutorials-common.rs | 81 +++++++++++++------------------ 4 files changed, 74 insertions(+), 92 deletions(-) diff --git a/examples/Cargo.toml b/examples/Cargo.toml index f2718e919..b990777a6 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -36,6 +36,9 @@ glutin = { version = "0.27", optional = true } once_cell = "1.0" image = { version="0.23", optional = true } +[target.'cfg(target_os = "macos")'.dependencies] +cocoa = "0.24" + [build-dependencies] gl_generator = { version = "0.14", optional = true } diff --git a/examples/src/examples-common.rs b/examples/src/examples-common.rs index 06aee38aa..48b7b42dd 100644 --- a/examples/src/examples-common.rs +++ b/examples/src/examples-common.rs @@ -1,41 +1,6 @@ -/// 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. - -#[cfg(target_os = "macos")] -mod runloop { - use std::os::raw::c_void; - pub struct CFRunLoop(*mut c_void); - - #[link(name = "foundation", kind = "framework")] - extern "C" { - fn CFRunLoopRun(); - fn CFRunLoopGetMain() -> *mut c_void; - fn CFRunLoopStop(l: *mut c_void); - } - - impl CFRunLoop { - pub fn run() { - unsafe { - CFRunLoopRun(); - } - } - - #[doc(alias = "get_main")] - pub fn main() -> CFRunLoop { - unsafe { - let r = CFRunLoopGetMain(); - assert!(!r.is_null()); - CFRunLoop(r) - } - } - - pub fn stop(&self) { - unsafe { CFRunLoopStop(self.0) } - } - } - - unsafe impl Send for CFRunLoop {} -} +/// 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. @@ -52,16 +17,38 @@ pub fn run T + Send + 'static>(main: F) -> T where T: Send + 'static, { + use cocoa::appkit::NSApplication; + use std::thread; - let l = runloop::CFRunLoop::main(); - let t = thread::spawn(move || { - let res = main(); - l.stop(); - res - }); + unsafe { + let app = cocoa::appkit::NSApp(); + let t = thread::spawn(|| { + let res = main(); - runloop::CFRunLoop::run(); + let app = cocoa::appkit::NSApp(); + app.stop_(cocoa::base::nil); - t.join().unwrap() + // 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() + } } diff --git a/tutorials/Cargo.toml b/tutorials/Cargo.toml index 1766e42d5..a0cc23e3d 100644 --- a/tutorials/Cargo.toml +++ b/tutorials/Cargo.toml @@ -18,6 +18,9 @@ byte-slice-cast = "1" anyhow = "1" termion = { version = "1.5", optional = true } +[target.'cfg(target_os = "macos")'.dependencies] +cocoa = "0.24" + [features] tutorial5 = ["gtk", "gdk", "gst-video"] tutorial5-x11 = ["tutorial5"] diff --git a/tutorials/src/tutorials-common.rs b/tutorials/src/tutorials-common.rs index 06aee38aa..889d0c263 100644 --- a/tutorials/src/tutorials-common.rs +++ b/tutorials/src/tutorials-common.rs @@ -1,41 +1,6 @@ -/// 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. - -#[cfg(target_os = "macos")] -mod runloop { - use std::os::raw::c_void; - pub struct CFRunLoop(*mut c_void); - - #[link(name = "foundation", kind = "framework")] - extern "C" { - fn CFRunLoopRun(); - fn CFRunLoopGetMain() -> *mut c_void; - fn CFRunLoopStop(l: *mut c_void); - } - - impl CFRunLoop { - pub fn run() { - unsafe { - CFRunLoopRun(); - } - } - - #[doc(alias = "get_main")] - pub fn main() -> CFRunLoop { - unsafe { - let r = CFRunLoopGetMain(); - assert!(!r.is_null()); - CFRunLoop(r) - } - } - - pub fn stop(&self) { - unsafe { CFRunLoopStop(self.0) } - } - } - - unsafe impl Send for CFRunLoop {} -} +/// 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. @@ -52,16 +17,40 @@ pub fn run T + Send + 'static>(main: F) -> T where T: Send + 'static, { + use cocoa::appkit::NSApplication; + use std::thread; - let l = runloop::CFRunLoop::main(); - let t = thread::spawn(move || { - let res = main(); - l.stop(); - res - }); + unsafe { + let app = cocoa::appkit::NSApp(); + let t = thread::spawn(|| { + let res = main(); - runloop::CFRunLoop::run(); + let app = cocoa::appkit::NSApp(); + app.stop_(cocoa::base::nil); - t.join().unwrap() + // 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); + + std::process::exit(0); + + res + }); + + app.run(); + + t.join().unwrap() + } }