1 // Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
3 //
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
9 
10 #![allow(non_upper_case_globals)]
11 
12 pub use core_foundation_sys::runloop::*;
13 use core_foundation_sys::base::CFIndex;
14 use core_foundation_sys::base::{kCFAllocatorDefault, CFOptionFlags};
15 use core_foundation_sys::string::CFStringRef;
16 
17 use base::{TCFType};
18 use date::{CFAbsoluteTime, CFTimeInterval};
19 use filedescriptor::CFFileDescriptor;
20 use string::{CFString};
21 
22 pub type CFRunLoopMode = CFStringRef;
23 
24 
25 declare_TCFType!(CFRunLoop, CFRunLoopRef);
26 impl_TCFType!(CFRunLoop, CFRunLoopRef, CFRunLoopGetTypeID);
27 impl_CFTypeDescription!(CFRunLoop);
28 
29 impl CFRunLoop {
get_current() -> CFRunLoop30     pub fn get_current() -> CFRunLoop {
31         unsafe {
32             let run_loop_ref = CFRunLoopGetCurrent();
33             TCFType::wrap_under_get_rule(run_loop_ref)
34         }
35     }
36 
get_main() -> CFRunLoop37     pub fn get_main() -> CFRunLoop {
38         unsafe {
39             let run_loop_ref = CFRunLoopGetMain();
40             TCFType::wrap_under_get_rule(run_loop_ref)
41         }
42     }
43 
run_current()44     pub fn run_current() {
45         unsafe {
46             CFRunLoopRun();
47         }
48     }
49 
stop(&self)50     pub fn stop(&self) {
51         unsafe {
52             CFRunLoopStop(self.0);
53         }
54     }
55 
current_mode(&self) -> Option<String>56     pub fn current_mode(&self) -> Option<String> {
57         unsafe {
58             let string_ref = CFRunLoopCopyCurrentMode(self.0);
59             if string_ref.is_null() {
60                 return None;
61             }
62 
63             let cf_string: CFString = TCFType::wrap_under_create_rule(string_ref);
64             Some(cf_string.to_string())
65         }
66     }
67 
contains_timer(&self, timer: &CFRunLoopTimer, mode: CFRunLoopMode) -> bool68     pub fn contains_timer(&self, timer: &CFRunLoopTimer, mode: CFRunLoopMode) -> bool {
69         unsafe {
70             CFRunLoopContainsTimer(self.0, timer.0, mode) != 0
71         }
72     }
73 
add_timer(&self, timer: &CFRunLoopTimer, mode: CFRunLoopMode)74     pub fn add_timer(&self, timer: &CFRunLoopTimer, mode: CFRunLoopMode) {
75         unsafe {
76             CFRunLoopAddTimer(self.0, timer.0, mode);
77         }
78     }
79 
remove_timer(&self, timer: &CFRunLoopTimer, mode: CFRunLoopMode)80     pub fn remove_timer(&self, timer: &CFRunLoopTimer, mode: CFRunLoopMode) {
81         unsafe {
82             CFRunLoopRemoveTimer(self.0, timer.0, mode);
83         }
84     }
85 
contains_source(&self, source: &CFRunLoopSource, mode: CFRunLoopMode) -> bool86     pub fn contains_source(&self, source: &CFRunLoopSource, mode: CFRunLoopMode) -> bool {
87         unsafe {
88             CFRunLoopContainsSource(self.0, source.0, mode) != 0
89         }
90     }
91 
add_source(&self, source: &CFRunLoopSource, mode: CFRunLoopMode)92     pub fn add_source(&self, source: &CFRunLoopSource, mode: CFRunLoopMode) {
93         unsafe {
94             CFRunLoopAddSource(self.0, source.0, mode);
95         }
96     }
97 
remove_source(&self, source: &CFRunLoopSource, mode: CFRunLoopMode)98     pub fn remove_source(&self, source: &CFRunLoopSource, mode: CFRunLoopMode) {
99         unsafe {
100             CFRunLoopRemoveSource(self.0, source.0, mode);
101         }
102     }
103 
contains_observer(&self, observer: &CFRunLoopObserver, mode: CFRunLoopMode) -> bool104     pub fn contains_observer(&self, observer: &CFRunLoopObserver, mode: CFRunLoopMode) -> bool {
105         unsafe {
106             CFRunLoopContainsObserver(self.0, observer.0, mode) != 0
107         }
108     }
109 
add_observer(&self, observer: &CFRunLoopObserver, mode: CFRunLoopMode)110     pub fn add_observer(&self, observer: &CFRunLoopObserver, mode: CFRunLoopMode) {
111         unsafe {
112             CFRunLoopAddObserver(self.0, observer.0, mode);
113         }
114     }
115 
remove_observer(&self, observer: &CFRunLoopObserver, mode: CFRunLoopMode)116     pub fn remove_observer(&self, observer: &CFRunLoopObserver, mode: CFRunLoopMode) {
117         unsafe {
118             CFRunLoopRemoveObserver(self.0, observer.0, mode);
119         }
120     }
121 
122 }
123 
124 
125 declare_TCFType!(CFRunLoopTimer, CFRunLoopTimerRef);
126 impl_TCFType!(CFRunLoopTimer, CFRunLoopTimerRef, CFRunLoopTimerGetTypeID);
127 
128 impl CFRunLoopTimer {
new(fireDate: CFAbsoluteTime, interval: CFTimeInterval, flags: CFOptionFlags, order: CFIndex, callout: CFRunLoopTimerCallBack, context: *mut CFRunLoopTimerContext) -> CFRunLoopTimer129     pub fn new(fireDate: CFAbsoluteTime, interval: CFTimeInterval, flags: CFOptionFlags, order: CFIndex, callout: CFRunLoopTimerCallBack, context: *mut CFRunLoopTimerContext) -> CFRunLoopTimer {
130         unsafe {
131             let timer_ref = CFRunLoopTimerCreate(kCFAllocatorDefault, fireDate, interval, flags, order, callout, context);
132             TCFType::wrap_under_create_rule(timer_ref)
133         }
134     }
135 }
136 
137 
138 declare_TCFType!(CFRunLoopSource, CFRunLoopSourceRef);
139 impl_TCFType!(CFRunLoopSource, CFRunLoopSourceRef, CFRunLoopSourceGetTypeID);
140 
141 impl CFRunLoopSource {
from_file_descriptor(fd: &CFFileDescriptor, order: CFIndex) -> Option<CFRunLoopSource>142     pub fn from_file_descriptor(fd: &CFFileDescriptor, order: CFIndex) -> Option<CFRunLoopSource> {
143         fd.to_run_loop_source(order)
144     }
145 }
146 
147 declare_TCFType!(CFRunLoopObserver, CFRunLoopObserverRef);
148 impl_TCFType!(CFRunLoopObserver, CFRunLoopObserverRef, CFRunLoopObserverGetTypeID);
149 
150 #[cfg(test)]
151 mod test {
152     use super::*;
153     use date::{CFDate, CFAbsoluteTime};
154     use std::mem;
155     use std::os::raw::c_void;
156     use std::sync::mpsc;
157 
158     #[test]
wait_200_milliseconds()159     fn wait_200_milliseconds() {
160         let run_loop = CFRunLoop::get_current();
161 
162         let now = CFDate::now().abs_time();
163         let (elapsed_tx, elapsed_rx) = mpsc::channel();
164         let mut info = Info {
165             start_time: now,
166             elapsed_tx,
167         };
168         let mut context = CFRunLoopTimerContext {
169             version: 0,
170             info: &mut info as *mut _ as *mut c_void,
171             retain: None,
172             release: None,
173             copyDescription: None,
174         };
175 
176         let run_loop_timer = CFRunLoopTimer::new(now + 0.20f64, 0f64, 0, 0, timer_popped, &mut context);
177         unsafe {
178             run_loop.add_timer(&run_loop_timer, kCFRunLoopDefaultMode);
179         }
180         CFRunLoop::run_current();
181         let elapsed = elapsed_rx.try_recv().unwrap();
182         println!("wait_200_milliseconds, elapsed: {}", elapsed);
183         assert!(elapsed > 0.19 && elapsed < 0.30);
184     }
185 
186     struct Info {
187         start_time: CFAbsoluteTime,
188         elapsed_tx: mpsc::Sender<f64>,
189     }
190 
timer_popped(_timer: CFRunLoopTimerRef, raw_info: *mut c_void)191     extern "C" fn timer_popped(_timer: CFRunLoopTimerRef, raw_info: *mut c_void) {
192         let info: *mut Info = unsafe { mem::transmute(raw_info) };
193         let now = CFDate::now().abs_time();
194         let elapsed = now - unsafe { (*info).start_time };
195         let _ = unsafe { (*info).elapsed_tx.send(elapsed) };
196         CFRunLoop::get_current().stop();
197     }
198 }
199