1#![allow(non_upper_case_globals)]
11
12use core_foundation_sys::base::CFIndex;
13use core_foundation_sys::base::{kCFAllocatorDefault, CFOptionFlags};
14pub use core_foundation_sys::runloop::*;
15use core_foundation_sys::string::CFStringRef;
16
17use crate::base::TCFType;
18use crate::date::{CFAbsoluteTime, CFTimeInterval};
19use crate::filedescriptor::CFFileDescriptor;
20use crate::string::CFString;
21
22pub type CFRunLoopMode = CFStringRef;
23
24declare_TCFType!(CFRunLoop, CFRunLoopRef);
25impl_TCFType!(CFRunLoop, CFRunLoopRef, CFRunLoopGetTypeID);
26impl_CFTypeDescription!(CFRunLoop);
27
28unsafe impl Send for CFRunLoop {}
30unsafe impl Sync for CFRunLoop {}
31
32#[derive(Copy, Clone, Debug, PartialEq)]
33pub enum CFRunLoopRunResult {
34 Finished = 1,
35 Stopped = 2,
36 TimedOut = 3,
37 HandledSource = 4,
38}
39
40impl CFRunLoop {
41 pub fn get_current() -> CFRunLoop {
42 unsafe {
43 let run_loop_ref = CFRunLoopGetCurrent();
44 TCFType::wrap_under_get_rule(run_loop_ref)
45 }
46 }
47
48 pub fn get_main() -> CFRunLoop {
49 unsafe {
50 let run_loop_ref = CFRunLoopGetMain();
51 TCFType::wrap_under_get_rule(run_loop_ref)
52 }
53 }
54
55 pub fn run_current() {
56 unsafe {
57 CFRunLoopRun();
58 }
59 }
60
61 pub fn run_in_mode(
62 mode: CFStringRef,
63 duration: std::time::Duration,
64 return_after_source_handled: bool,
65 ) -> CFRunLoopRunResult {
66 let seconds = duration.as_secs_f64();
67 let return_after_source_handled = if return_after_source_handled { 1 } else { 0 };
68
69 unsafe {
70 match CFRunLoopRunInMode(mode, seconds, return_after_source_handled) {
71 2 => CFRunLoopRunResult::Stopped,
72 3 => CFRunLoopRunResult::TimedOut,
73 4 => CFRunLoopRunResult::HandledSource,
74 _ => CFRunLoopRunResult::Finished,
75 }
76 }
77 }
78
79 pub fn stop(&self) {
80 unsafe {
81 CFRunLoopStop(self.0);
82 }
83 }
84
85 pub fn current_mode(&self) -> Option<String> {
86 unsafe {
87 let string_ref = CFRunLoopCopyCurrentMode(self.0);
88 if string_ref.is_null() {
89 return None;
90 }
91
92 let cf_string: CFString = TCFType::wrap_under_create_rule(string_ref);
93 Some(cf_string.to_string())
94 }
95 }
96
97 pub fn contains_timer(&self, timer: &CFRunLoopTimer, mode: CFRunLoopMode) -> bool {
98 unsafe { CFRunLoopContainsTimer(self.0, timer.0, mode) != 0 }
99 }
100
101 pub fn add_timer(&self, timer: &CFRunLoopTimer, mode: CFRunLoopMode) {
102 unsafe {
103 CFRunLoopAddTimer(self.0, timer.0, mode);
104 }
105 }
106
107 pub fn remove_timer(&self, timer: &CFRunLoopTimer, mode: CFRunLoopMode) {
108 unsafe {
109 CFRunLoopRemoveTimer(self.0, timer.0, mode);
110 }
111 }
112
113 pub fn contains_source(&self, source: &CFRunLoopSource, mode: CFRunLoopMode) -> bool {
114 unsafe { CFRunLoopContainsSource(self.0, source.0, mode) != 0 }
115 }
116
117 pub fn add_source(&self, source: &CFRunLoopSource, mode: CFRunLoopMode) {
118 unsafe {
119 CFRunLoopAddSource(self.0, source.0, mode);
120 }
121 }
122
123 pub fn remove_source(&self, source: &CFRunLoopSource, mode: CFRunLoopMode) {
124 unsafe {
125 CFRunLoopRemoveSource(self.0, source.0, mode);
126 }
127 }
128
129 pub fn contains_observer(&self, observer: &CFRunLoopObserver, mode: CFRunLoopMode) -> bool {
130 unsafe { CFRunLoopContainsObserver(self.0, observer.0, mode) != 0 }
131 }
132
133 pub fn add_observer(&self, observer: &CFRunLoopObserver, mode: CFRunLoopMode) {
134 unsafe {
135 CFRunLoopAddObserver(self.0, observer.0, mode);
136 }
137 }
138
139 pub fn remove_observer(&self, observer: &CFRunLoopObserver, mode: CFRunLoopMode) {
140 unsafe {
141 CFRunLoopRemoveObserver(self.0, observer.0, mode);
142 }
143 }
144}
145
146declare_TCFType!(CFRunLoopTimer, CFRunLoopTimerRef);
147impl_TCFType!(CFRunLoopTimer, CFRunLoopTimerRef, CFRunLoopTimerGetTypeID);
148
149impl CFRunLoopTimer {
150 pub fn new(
151 fireDate: CFAbsoluteTime,
152 interval: CFTimeInterval,
153 flags: CFOptionFlags,
154 order: CFIndex,
155 callout: CFRunLoopTimerCallBack,
156 context: *mut CFRunLoopTimerContext,
157 ) -> CFRunLoopTimer {
158 unsafe {
159 let timer_ref = CFRunLoopTimerCreate(
160 kCFAllocatorDefault,
161 fireDate,
162 interval,
163 flags,
164 order,
165 callout,
166 context,
167 );
168 TCFType::wrap_under_create_rule(timer_ref)
169 }
170 }
171}
172
173declare_TCFType!(CFRunLoopSource, CFRunLoopSourceRef);
174impl_TCFType!(
175 CFRunLoopSource,
176 CFRunLoopSourceRef,
177 CFRunLoopSourceGetTypeID
178);
179
180impl CFRunLoopSource {
181 pub fn from_file_descriptor(fd: &CFFileDescriptor, order: CFIndex) -> Option<CFRunLoopSource> {
182 fd.to_run_loop_source(order)
183 }
184}
185
186declare_TCFType!(CFRunLoopObserver, CFRunLoopObserverRef);
187impl_TCFType!(
188 CFRunLoopObserver,
189 CFRunLoopObserverRef,
190 CFRunLoopObserverGetTypeID
191);
192
193#[cfg(test)]
194mod test {
195 use super::*;
196 use crate::base::Boolean;
197 use crate::date::{CFAbsoluteTime, CFDate};
198 use std::mem;
199 use std::os::raw::c_void;
200 use std::ptr::null_mut;
201 use std::sync::mpsc;
202 use std::thread::spawn;
203 use std::time::Duration;
204
205 #[test]
206 fn wait_200_milliseconds() {
207 let run_loop = CFRunLoop::get_current();
208
209 let now = CFDate::now().abs_time();
210 let (elapsed_tx, elapsed_rx) = mpsc::channel();
211 let mut info = Info {
212 start_time: now,
213 elapsed_tx,
214 };
215 let mut context = CFRunLoopTimerContext {
216 version: 0,
217 info: &mut info as *mut _ as *mut c_void,
218 retain: None,
219 release: None,
220 copyDescription: None,
221 };
222
223 let run_loop_timer =
224 CFRunLoopTimer::new(now + 0.20f64, 0f64, 0, 0, timer_popped, &mut context);
225 unsafe {
226 run_loop.add_timer(&run_loop_timer, kCFRunLoopDefaultMode);
227 }
228 CFRunLoop::run_current();
229 let elapsed = elapsed_rx.try_recv().unwrap();
230 println!("wait_200_milliseconds, elapsed: {}", elapsed);
231 assert!(elapsed > 0.19 && elapsed < 0.35);
232 }
233
234 struct Info {
235 start_time: CFAbsoluteTime,
236 elapsed_tx: mpsc::Sender<f64>,
237 }
238
239 extern "C" fn timer_popped(_timer: CFRunLoopTimerRef, raw_info: *mut c_void) {
240 let info: *mut Info = unsafe { mem::transmute(raw_info) };
241 let now = CFDate::now().abs_time();
242 let elapsed = now - unsafe { (*info).start_time };
243 let _ = unsafe { (*info).elapsed_tx.send(elapsed) };
244 CFRunLoop::get_current().stop();
245 }
246
247 extern "C" fn observe(_: CFRunLoopObserverRef, _: CFRunLoopActivity, context: *mut c_void) {
248 let tx: &mpsc::Sender<CFRunLoop> = unsafe { &*(context as *const _) };
249 let _ = tx.send(CFRunLoop::get_current());
250 }
251
252 extern "C" fn observe_timer_popped(_: CFRunLoopTimerRef, _: *mut c_void) {
253 panic!("timer popped unexpectedly");
254 }
255
256 #[test]
257 fn observe_runloop() {
258 let (tx, rx) = mpsc::channel();
259 spawn(move || {
260 let mut context = CFRunLoopObserverContext {
261 version: 0,
262 info: &tx as *const _ as *mut c_void,
263 retain: None,
264 release: None,
265 copyDescription: None,
266 };
267
268 let observer = unsafe {
269 CFRunLoopObserver::wrap_under_create_rule(CFRunLoopObserverCreate(
270 kCFAllocatorDefault,
271 kCFRunLoopEntry,
272 false as Boolean,
273 0,
274 observe,
275 &mut context,
276 ))
277 };
278
279 let runloop = CFRunLoop::get_current();
280 runloop.add_observer(&observer, unsafe { kCFRunLoopDefaultMode });
281
282 let timer = CFRunLoopTimer::new(
283 CFDate::now().abs_time() + 1f64,
284 0f64,
285 0,
286 0,
287 observe_timer_popped,
288 null_mut(),
289 );
290 runloop.add_timer(&timer, unsafe { kCFRunLoopDefaultMode });
291
292 let result = unsafe {
293 CFRunLoop::run_in_mode(kCFRunLoopDefaultMode, Duration::from_secs(10), false)
294 };
295
296 assert_eq!(result, CFRunLoopRunResult::Stopped);
297
298 drop(tx);
299 });
300
301 let runloop: CFRunLoop = rx.recv().unwrap();
302 runloop.stop();
303 }
304}