examples/tutorials: fix macOS run() wrapper terminating too early

Using terminate() kills the whole process instead of just stopping the event loop, so we're back to the 'old' way.
However, if the provided function finishes too early, that can also fail (will call stop() on a not-yet-running NSApp).
Creating a delegate and waiting for the callback makes sure NSApp is running before the actual main() is called.

Also, for whatever reason only tutorials were changed to use terminate(). Now both tutorials and examples are
using identical code.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/1378>
This commit is contained in:
Piotr Brzeziński 2024-01-29 14:12:40 +01:00 committed by GStreamer Marge Bot
parent 047f4a3f75
commit 446bb7ec3e
4 changed files with 92 additions and 8 deletions

1
Cargo.lock generated
View file

@ -507,6 +507,7 @@ dependencies = [
"image", "image",
"memfd", "memfd",
"memmap2", "memmap2",
"objc",
"pango", "pango",
"pangocairo", "pangocairo",
"raw-window-handle", "raw-window-handle",

View file

@ -52,6 +52,7 @@ windows = { version = "0.52", features=["Win32_Graphics_Direct3D11",
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.25" cocoa = "0.25"
objc = "0.2.7"
[build-dependencies] [build-dependencies]
gl_generator = { version = "0.14", optional = true } gl_generator = { version = "0.14", optional = true }

View file

@ -17,13 +17,47 @@ pub fn run<T, F: FnOnce() -> T + Send + 'static>(main: F) -> T
where where
T: Send + 'static, 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 { unsafe {
let app = cocoa::appkit::NSApp(); 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 res = main();
let app = cocoa::appkit::NSApp(); let app = cocoa::appkit::NSApp();

View file

@ -17,18 +17,66 @@ pub fn run<T, F: FnOnce() -> T + Send + 'static>(main: F) -> T
where where
T: Send + 'static, T: Send + 'static,
{ {
use std::thread; use std::{
ffi::c_void,
sync::mpsc::{channel, Sender},
thread,
};
use cocoa::appkit::NSApplication; use cocoa::{
use objc::{msg_send, sel, sel_impl}; appkit::{NSApplication, NSWindow},
base::id,
delegate,
};
use objc::{
class, msg_send,
runtime::{Object, Sel},
sel, sel_impl,
};
unsafe { unsafe {
let app = cocoa::appkit::NSApp(); 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 res = main();
let app = cocoa::appkit::NSApp(); 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 res
}); });