1#![allow(clippy::missing_panics_doc, clippy::missing_errors_doc)]
4
5use super::base::impl_cf_type_wrapper;
42use super::{CFDictionary, CFString};
43use crate::ffi;
44use std::time::Duration;
45
46impl_cf_type_wrapper!(CFNotificationCenter, cf_notification_center_get_type_id);
47impl_cf_type_wrapper!(CFRunLoop, cf_run_loop_get_type_id);
48impl_cf_type_wrapper!(CFTimer, cf_run_loop_timer_get_type_id);
49impl_cf_type_wrapper!(CFMessagePort, cf_message_port_get_type_id);
50impl_cf_type_wrapper!(CFReadStream, cf_read_stream_get_type_id);
51impl_cf_type_wrapper!(CFWriteStream, cf_write_stream_get_type_id);
52impl_cf_type_wrapper!(CFSocket, cf_socket_get_type_id);
53impl_cf_type_wrapper!(CFFileDescriptor, cf_file_descriptor_get_type_id);
54
55fn duration_to_seconds(duration: Duration) -> f64 {
56 duration.as_secs_f64()
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
61#[repr(i32)]
62pub enum CFRunLoopRunResult {
63 Finished = 1,
64 Stopped = 2,
65 TimedOut = 3,
66 HandledSource = 4,
67}
68
69impl CFNotificationCenter {
70 #[must_use]
72 pub fn local() -> Self {
73 let ptr = unsafe { ffi::cf_notification_center_get_local() };
74 unsafe { Self::from_raw(ptr) }.expect("CFNotificationCenterGetLocalCenter returned NULL")
75 }
76
77 #[must_use]
79 pub fn distributed() -> Self {
80 let ptr = unsafe { ffi::cf_notification_center_get_distributed() };
81 unsafe { Self::from_raw(ptr) }
82 .expect("CFNotificationCenterGetDistributedCenter returned NULL")
83 }
84
85 #[must_use]
87 pub fn darwin() -> Self {
88 let ptr = unsafe { ffi::cf_notification_center_get_darwin() };
89 unsafe { Self::from_raw(ptr) }
90 .expect("CFNotificationCenterGetDarwinNotifyCenter returned NULL")
91 }
92
93 pub fn post(
95 &self,
96 name: &CFString,
97 user_info: Option<&CFDictionary>,
98 deliver_immediately: bool,
99 ) {
100 unsafe {
101 ffi::cf_notification_center_post_notification(
102 self.as_ptr(),
103 name.as_ptr(),
104 user_info.map_or(std::ptr::null_mut(), CFDictionary::as_ptr),
105 deliver_immediately,
106 );
107 }
108 }
109}
110
111impl CFRunLoop {
112 #[must_use]
114 pub fn current() -> Self {
115 let ptr = unsafe { ffi::cf_run_loop_get_current() };
116 unsafe { Self::from_raw(ptr) }.expect("CFRunLoopGetCurrent returned NULL")
117 }
118
119 #[must_use]
121 pub fn main() -> Self {
122 let ptr = unsafe { ffi::cf_run_loop_get_main() };
123 unsafe { Self::from_raw(ptr) }.expect("CFRunLoopGetMain returned NULL")
124 }
125
126 #[must_use]
128 pub fn run_in_default_mode(
129 &self,
130 duration: Duration,
131 return_after_source_handled: bool,
132 ) -> CFRunLoopRunResult {
133 let code = unsafe {
134 ffi::cf_run_loop_run_in_default_mode(
135 duration_to_seconds(duration),
136 return_after_source_handled,
137 )
138 };
139 match code {
140 1 => CFRunLoopRunResult::Finished,
141 2 => CFRunLoopRunResult::Stopped,
142 4 => CFRunLoopRunResult::HandledSource,
143 _ => CFRunLoopRunResult::TimedOut,
144 }
145 }
146
147 pub fn wake_up(&self) {
149 unsafe { ffi::cf_run_loop_wake_up(self.as_ptr()) };
150 }
151
152 pub fn stop(&self) {
154 unsafe { ffi::cf_run_loop_stop(self.as_ptr()) };
155 }
156
157 pub fn add_timer(&self, timer: &CFTimer) {
159 unsafe { ffi::cf_run_loop_add_timer(self.as_ptr(), timer.as_ptr()) };
160 }
161}
162
163impl CFTimer {
164 #[must_use]
166 pub fn new(interval: Duration, repeats: bool) -> Self {
167 let ptr = unsafe { ffi::cf_run_loop_timer_create(duration_to_seconds(interval), repeats) };
168 unsafe { Self::from_raw(ptr) }.expect("CFRunLoopTimerCreate returned NULL")
169 }
170
171 #[must_use]
173 pub fn is_valid(&self) -> bool {
174 unsafe { ffi::cf_run_loop_timer_is_valid(self.as_ptr()) }
175 }
176
177 pub fn fire(&self) {
179 unsafe { ffi::cf_run_loop_timer_fire(self.as_ptr()) };
180 }
181
182 pub fn invalidate(&self) {
184 unsafe { ffi::cf_run_loop_timer_invalidate(self.as_ptr()) };
185 }
186}
187
188impl CFMessagePort {
189 #[must_use]
191 pub fn create_echo_local(name: &str) -> Self {
192 let name =
193 std::ffi::CString::new(name).expect("message-port name may not contain NUL bytes");
194 let ptr = unsafe { ffi::cf_message_port_create_echo_local(name.as_ptr()) };
195 unsafe { Self::from_raw(ptr) }.expect("CFMessagePortCreateLocal returned NULL")
196 }
197
198 #[must_use]
200 pub fn connect_remote(name: &str) -> Option<Self> {
201 let name =
202 std::ffi::CString::new(name).expect("message-port name may not contain NUL bytes");
203 let ptr = unsafe { ffi::cf_message_port_create_remote(name.as_ptr()) };
204 unsafe { Self::from_raw(ptr) }
205 }
206
207 pub fn send_request(&self, bytes: &[u8], timeout: Duration) -> Result<Vec<u8>, i32> {
209 let mut out_bytes = std::ptr::null_mut();
210 let mut out_len = 0_usize;
211 let status = unsafe {
212 ffi::cf_message_port_send_request(
213 self.as_ptr(),
214 bytes.as_ptr(),
215 bytes.len(),
216 duration_to_seconds(timeout),
217 &mut out_bytes,
218 &mut out_len,
219 )
220 };
221 if status != 0 {
222 return Err(status);
223 }
224 if out_bytes.is_null() {
225 return Ok(Vec::new());
226 }
227 let reply = unsafe { std::slice::from_raw_parts(out_bytes, out_len) }.to_vec();
228 unsafe { ffi::cf_message_port_free_bytes(out_bytes, out_len) };
229 Ok(reply)
230 }
231
232 pub fn invalidate(&self) {
234 unsafe { ffi::cf_message_port_invalidate(self.as_ptr()) };
235 }
236}
237
238#[derive(Debug, Clone)]
240pub struct CFStreamPair {
241 pub read: CFReadStream,
242 pub write: CFWriteStream,
243}
244
245impl CFStreamPair {
246 #[must_use]
248 pub fn new(transfer_buffer_size: usize) -> Self {
249 let mut read = std::ptr::null_mut();
250 let mut write = std::ptr::null_mut();
251 unsafe { ffi::cf_stream_create_bound_pair(transfer_buffer_size, &mut read, &mut write) };
252 Self {
253 read: unsafe { CFReadStream::from_raw(read) }
254 .expect("CFStreamCreateBoundPair read stream was NULL"),
255 write: unsafe { CFWriteStream::from_raw(write) }
256 .expect("CFStreamCreateBoundPair write stream was NULL"),
257 }
258 }
259}
260
261impl CFReadStream {
262 #[must_use]
264 pub fn open(&self) -> bool {
265 unsafe { ffi::cf_read_stream_open(self.as_ptr()) }
266 }
267
268 pub fn close(&self) {
270 unsafe { ffi::cf_read_stream_close(self.as_ptr()) };
271 }
272
273 pub fn read(&self, buffer: &mut [u8]) -> Result<usize, isize> {
275 let count =
276 unsafe { ffi::cf_read_stream_read(self.as_ptr(), buffer.as_mut_ptr(), buffer.len()) };
277 if count < 0 {
278 Err(count)
279 } else {
280 Ok(usize::try_from(count).expect("non-negative read count fits in usize"))
281 }
282 }
283}
284
285impl CFWriteStream {
286 #[must_use]
288 pub fn open(&self) -> bool {
289 unsafe { ffi::cf_write_stream_open(self.as_ptr()) }
290 }
291
292 pub fn close(&self) {
294 unsafe { ffi::cf_write_stream_close(self.as_ptr()) };
295 }
296
297 pub fn write(&self, buffer: &[u8]) -> Result<usize, isize> {
299 let count =
300 unsafe { ffi::cf_write_stream_write(self.as_ptr(), buffer.as_ptr(), buffer.len()) };
301 if count < 0 {
302 Err(count)
303 } else {
304 Ok(usize::try_from(count).expect("non-negative write count fits in usize"))
305 }
306 }
307}
308
309impl CFSocket {
310 #[must_use]
312 pub fn udp_ipv4() -> Option<Self> {
313 let ptr = unsafe { ffi::cf_socket_create_udp_ipv4() };
314 unsafe { Self::from_raw(ptr) }
315 }
316
317 #[must_use]
319 pub fn native(&self) -> i32 {
320 unsafe { ffi::cf_socket_get_native(self.as_ptr()) }
321 }
322
323 #[must_use]
325 pub fn is_valid(&self) -> bool {
326 unsafe { ffi::cf_socket_is_valid(self.as_ptr()) }
327 }
328
329 pub fn invalidate(&self) {
331 unsafe { ffi::cf_socket_invalidate(self.as_ptr()) };
332 }
333}
334
335impl CFFileDescriptor {
336 #[must_use]
338 pub fn from_raw_fd(native_fd: i32, close_on_invalidate: bool) -> Option<Self> {
339 let ptr = unsafe { ffi::cf_file_descriptor_create(native_fd, close_on_invalidate) };
340 unsafe { Self::from_raw(ptr) }
341 }
342
343 #[must_use]
345 pub fn native_descriptor(&self) -> i32 {
346 unsafe { ffi::cf_file_descriptor_get_native(self.as_ptr()) }
347 }
348
349 pub fn invalidate(&self) {
351 unsafe { ffi::cf_file_descriptor_invalidate(self.as_ptr()) };
352 }
353}