winio_ui_app_kit/
runtime.rs1use std::{
2 future::Future,
3 ptr::NonNull,
4 task::{RawWaker, RawWakerVTable, Waker},
5 time::Duration,
6};
7
8use objc2::rc::Retained;
9use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSEventMask};
10use objc2_core_foundation::{CFRetained, CFRunLoop, kCFRunLoopDefaultMode};
11use objc2_foundation::{MainThreadMarker, NSDate, NSDefaultRunLoopMode};
12
13#[cfg(feature = "compio-compat")]
14use crate::get_context;
15use crate::{Error, Result, catch};
16
17#[cfg(not(feature = "compio-compat"))]
18fn get_context() -> (Option<Duration>, Option<Waker>) {
19 (None, None)
20}
21
22pub struct App {
23 ns_app: Retained<NSApplication>,
24 waker: Waker,
25}
26
27impl App {
28 pub fn new() -> Result<Self> {
29 let mtm = MainThreadMarker::new().ok_or(Error::NotMainThread)?;
30 let ns_app = catch(|| {
31 let ns_app = NSApplication::sharedApplication(mtm);
32 ns_app.setActivationPolicy(NSApplicationActivationPolicy::Regular);
33 #[allow(deprecated)]
34 ns_app.activateIgnoringOtherApps(true);
35 ns_app
36 })?;
37 let waker = run_loop_waker(CFRunLoop::current().ok_or(Error::NullPointer)?);
38 Ok(Self { ns_app, waker })
39 }
40
41 pub fn block_on<F: Future>(&self, future: F) -> F::Output {
42 winio_pollable::block_on(future, self.waker.clone(), || {
43 let (timeout, waker) = get_context();
44 CFRunLoop::run_in_mode(
45 unsafe { kCFRunLoopDefaultMode },
46 timeout.unwrap_or(Duration::MAX).as_secs_f64(),
47 true,
48 );
49 if let Some(waker) = waker {
50 waker.wake();
51 }
52 unsafe {
53 loop {
54 let event = self.ns_app.nextEventMatchingMask_untilDate_inMode_dequeue(
55 NSEventMask::Any,
56 Some(&NSDate::distantPast()),
57 NSDefaultRunLoopMode,
58 true,
59 );
60 if let Some(event) = event {
61 self.ns_app.sendEvent(&event);
62 } else {
63 break;
64 }
65 }
66 }
67 })
68 }
69}
70
71fn run_loop_waker(run_loop: CFRetained<CFRunLoop>) -> Waker {
72 unsafe { Waker::from_raw(run_loop_raw_waker(run_loop)) }
73}
74
75fn run_loop_raw_waker(run_loop: CFRetained<CFRunLoop>) -> RawWaker {
76 let data = CFRetained::into_raw(run_loop);
77 RawWaker::new(
78 data.as_ptr().cast_const().cast(),
79 &RawWakerVTable::new(
80 run_loop_clone,
81 run_loop_wake,
82 run_loop_wake_by_ref,
83 run_loop_drop,
84 ),
85 )
86}
87
88unsafe fn run_loop_clone(data: *const ()) -> RawWaker {
89 let data = NonNull::new(data.cast_mut().cast()).expect("data pointer is null");
90 let run_loop = unsafe { CFRetained::<CFRunLoop>::retain(data) };
91 run_loop_raw_waker(run_loop)
92}
93
94unsafe fn run_loop_wake(data: *const ()) {
95 if let Some(data) = NonNull::new(data.cast_mut().cast()) {
96 let run_loop = unsafe { CFRetained::<CFRunLoop>::from_raw(data) };
97 run_loop.wake_up();
98 }
99}
100
101unsafe fn run_loop_wake_by_ref(data: *const ()) {
102 if let Some(data) = NonNull::new(data.cast_mut().cast()) {
103 let run_loop = unsafe { CFRetained::<CFRunLoop>::retain(data) };
104 run_loop.wake_up();
105 }
106}
107
108unsafe fn run_loop_drop(data: *const ()) {
109 if let Some(data) = NonNull::new(data.cast_mut().cast()) {
110 let _ = unsafe { CFRetained::<CFRunLoop>::from_raw(data) };
111 }
112}