winio_ui_app_kit/
compat.rs1use std::{
2 cell::RefCell,
3 ffi::c_void,
4 io,
5 ops::Deref,
6 os::fd::AsRawFd,
7 pin::Pin,
8 task::{Context, Poll, Waker},
9 time::Duration,
10};
11
12use compio::{compat::Adapter, runtime::Runtime};
13use objc2_core_foundation::{
14 CFFileDescriptor, CFRetained, CFRunLoop, CFRunLoopSource, kCFAllocatorDefault,
15 kCFFileDescriptorReadCallBack, kCFRunLoopDefaultMode,
16};
17
18pub struct CompioAdapter {
19 runtime: Runtime,
20 fd_source: CFRetained<CFFileDescriptor>,
21 source: CFRetained<CFRunLoopSource>,
22 run_loop: CFRetained<CFRunLoop>,
23}
24
25impl Deref for CompioAdapter {
26 type Target = Runtime;
27
28 fn deref(&self) -> &Self::Target {
29 &self.runtime
30 }
31}
32
33impl Adapter for CompioAdapter {
34 fn new(runtime: Runtime) -> io::Result<Self> {
35 unsafe extern "C-unwind" fn fd_callback(
36 _fdref: *mut CFFileDescriptor,
37 _callback_types: usize,
38 _info: *mut c_void,
39 ) {
40 }
41
42 let fd_source = unsafe {
43 CFFileDescriptor::new(
44 kCFAllocatorDefault,
45 runtime.as_raw_fd(),
46 false,
47 Some(fd_callback),
48 std::ptr::null(),
49 )
50 }
51 .ok_or(io::ErrorKind::InvalidData)?;
52 let source = unsafe {
53 CFFileDescriptor::new_run_loop_source(kCFAllocatorDefault, Some(&fd_source), 0)
54 }
55 .ok_or(io::ErrorKind::InvalidData)?;
56
57 let run_loop = CFRunLoop::current().ok_or(io::ErrorKind::InvalidData)?;
58 run_loop.add_source(Some(&source), unsafe { kCFRunLoopDefaultMode });
59
60 Ok(Self {
61 runtime,
62 fd_source,
63 source,
64 run_loop,
65 })
66 }
67
68 async fn wait(&self, timeout: Option<Duration>) -> io::Result<()> {
69 self.fd_source
70 .enable_call_backs(kCFFileDescriptorReadCallBack);
71
72 WaitFuture::new(timeout).await
73 }
74
75 fn clear(&self) -> io::Result<()> {
76 Ok(())
77 }
78}
79
80impl Drop for CompioAdapter {
81 fn drop(&mut self) {
82 self.fd_source
83 .disable_call_backs(kCFFileDescriptorReadCallBack);
84 self.run_loop
85 .remove_source(Some(&self.source), unsafe { kCFRunLoopDefaultMode });
86 }
87}
88
89thread_local! {
90 static CONTEXT: RefCell<Option<(Option<Duration>, Waker)>> = const { RefCell::new(None) };
91}
92
93pub(crate) fn get_context() -> (Option<Duration>, Option<Waker>) {
94 CONTEXT.with_borrow(|ctx| {
95 if let Some((timeout, waker)) = ctx.as_ref() {
96 (*timeout, Some(waker.clone()))
97 } else {
98 (None, None)
99 }
100 })
101}
102
103fn set_context(timeout: Option<Duration>, waker: Waker) {
104 CONTEXT.with_borrow_mut(|ctx| ctx.replace((timeout, waker)));
105}
106
107fn reset_context() {
108 CONTEXT.with_borrow_mut(|ctx| ctx.take());
109}
110
111struct WaitFuture {
112 timeout: Option<Duration>,
113 polled: bool,
114}
115
116impl WaitFuture {
117 fn new(timeout: Option<Duration>) -> Self {
118 Self {
119 timeout,
120 polled: false,
121 }
122 }
123}
124
125impl Future for WaitFuture {
126 type Output = io::Result<()>;
127
128 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
129 if self.polled {
130 Poll::Ready(Ok(()))
131 } else {
132 set_context(self.timeout, cx.waker().clone());
133 self.polled = true;
134 Poll::Pending
135 }
136 }
137}
138
139impl Drop for WaitFuture {
140 fn drop(&mut self) {
141 reset_context();
142 }
143}