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 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 Self::from_raw(ptr).expect("CFNotificationCenterGetDistributedCenter returned NULL")
82 }
83
84 #[must_use]
86 pub fn darwin() -> Self {
87 let ptr = unsafe { ffi::cf_notification_center_get_darwin() };
88 Self::from_raw(ptr).expect("CFNotificationCenterGetDarwinNotifyCenter returned NULL")
89 }
90
91 pub fn post(
93 &self,
94 name: &CFString,
95 user_info: Option<&CFDictionary>,
96 deliver_immediately: bool,
97 ) {
98 unsafe {
99 ffi::cf_notification_center_post_notification(
100 self.as_ptr(),
101 name.as_ptr(),
102 user_info.map_or(std::ptr::null_mut(), CFDictionary::as_ptr),
103 deliver_immediately,
104 );
105 }
106 }
107}
108
109impl CFRunLoop {
110 #[must_use]
112 pub fn current() -> Self {
113 let ptr = unsafe { ffi::cf_run_loop_get_current() };
114 Self::from_raw(ptr).expect("CFRunLoopGetCurrent returned NULL")
115 }
116
117 #[must_use]
119 pub fn main() -> Self {
120 let ptr = unsafe { ffi::cf_run_loop_get_main() };
121 Self::from_raw(ptr).expect("CFRunLoopGetMain returned NULL")
122 }
123
124 #[must_use]
126 pub fn run_in_default_mode(
127 &self,
128 duration: Duration,
129 return_after_source_handled: bool,
130 ) -> CFRunLoopRunResult {
131 let code = unsafe {
132 ffi::cf_run_loop_run_in_default_mode(
133 duration_to_seconds(duration),
134 return_after_source_handled,
135 )
136 };
137 match code {
138 1 => CFRunLoopRunResult::Finished,
139 2 => CFRunLoopRunResult::Stopped,
140 4 => CFRunLoopRunResult::HandledSource,
141 _ => CFRunLoopRunResult::TimedOut,
142 }
143 }
144
145 pub fn wake_up(&self) {
147 unsafe { ffi::cf_run_loop_wake_up(self.as_ptr()) };
148 }
149
150 pub fn stop(&self) {
152 unsafe { ffi::cf_run_loop_stop(self.as_ptr()) };
153 }
154
155 pub fn add_timer(&self, timer: &CFTimer) {
157 unsafe { ffi::cf_run_loop_add_timer(self.as_ptr(), timer.as_ptr()) };
158 }
159}
160
161impl CFTimer {
162 #[must_use]
164 pub fn new(interval: Duration, repeats: bool) -> Self {
165 let ptr = unsafe { ffi::cf_run_loop_timer_create(duration_to_seconds(interval), repeats) };
166 Self::from_raw(ptr).expect("CFRunLoopTimerCreate returned NULL")
167 }
168
169 #[must_use]
171 pub fn is_valid(&self) -> bool {
172 unsafe { ffi::cf_run_loop_timer_is_valid(self.as_ptr()) }
173 }
174
175 pub fn fire(&self) {
177 unsafe { ffi::cf_run_loop_timer_fire(self.as_ptr()) };
178 }
179
180 pub fn invalidate(&self) {
182 unsafe { ffi::cf_run_loop_timer_invalidate(self.as_ptr()) };
183 }
184}
185
186impl CFMessagePort {
187 #[must_use]
189 pub fn create_echo_local(name: &str) -> Self {
190 let name =
191 std::ffi::CString::new(name).expect("message-port name may not contain NUL bytes");
192 let ptr = unsafe { ffi::cf_message_port_create_echo_local(name.as_ptr()) };
193 Self::from_raw(ptr).expect("CFMessagePortCreateLocal returned NULL")
194 }
195
196 #[must_use]
198 pub fn connect_remote(name: &str) -> Option<Self> {
199 let name =
200 std::ffi::CString::new(name).expect("message-port name may not contain NUL bytes");
201 let ptr = unsafe { ffi::cf_message_port_create_remote(name.as_ptr()) };
202 Self::from_raw(ptr)
203 }
204
205 pub fn send_request(&self, bytes: &[u8], timeout: Duration) -> Result<Vec<u8>, i32> {
207 let mut out_bytes = std::ptr::null_mut();
208 let mut out_len = 0_usize;
209 let status = unsafe {
210 ffi::cf_message_port_send_request(
211 self.as_ptr(),
212 bytes.as_ptr(),
213 bytes.len(),
214 duration_to_seconds(timeout),
215 &mut out_bytes,
216 &mut out_len,
217 )
218 };
219 if status != 0 {
220 return Err(status);
221 }
222 if out_bytes.is_null() {
223 return Ok(Vec::new());
224 }
225 let reply = unsafe { std::slice::from_raw_parts(out_bytes, out_len) }.to_vec();
226 unsafe { ffi::cf_message_port_free_bytes(out_bytes, out_len) };
227 Ok(reply)
228 }
229
230 pub fn invalidate(&self) {
232 unsafe { ffi::cf_message_port_invalidate(self.as_ptr()) };
233 }
234}
235
236#[derive(Debug, Clone)]
238pub struct CFStreamPair {
239 pub read: CFReadStream,
240 pub write: CFWriteStream,
241}
242
243impl CFStreamPair {
244 #[must_use]
246 pub fn new(transfer_buffer_size: usize) -> Self {
247 let mut read = std::ptr::null_mut();
248 let mut write = std::ptr::null_mut();
249 unsafe { ffi::cf_stream_create_bound_pair(transfer_buffer_size, &mut read, &mut write) };
250 Self {
251 read: CFReadStream::from_raw(read)
252 .expect("CFStreamCreateBoundPair read stream was NULL"),
253 write: CFWriteStream::from_raw(write)
254 .expect("CFStreamCreateBoundPair write stream was NULL"),
255 }
256 }
257}
258
259impl CFReadStream {
260 #[must_use]
262 pub fn open(&self) -> bool {
263 unsafe { ffi::cf_read_stream_open(self.as_ptr()) }
264 }
265
266 pub fn close(&self) {
268 unsafe { ffi::cf_read_stream_close(self.as_ptr()) };
269 }
270
271 pub fn read(&self, buffer: &mut [u8]) -> Result<usize, isize> {
273 let count =
274 unsafe { ffi::cf_read_stream_read(self.as_ptr(), buffer.as_mut_ptr(), buffer.len()) };
275 if count < 0 {
276 Err(count)
277 } else {
278 Ok(usize::try_from(count).expect("non-negative read count fits in usize"))
279 }
280 }
281}
282
283impl CFWriteStream {
284 #[must_use]
286 pub fn open(&self) -> bool {
287 unsafe { ffi::cf_write_stream_open(self.as_ptr()) }
288 }
289
290 pub fn close(&self) {
292 unsafe { ffi::cf_write_stream_close(self.as_ptr()) };
293 }
294
295 pub fn write(&self, buffer: &[u8]) -> Result<usize, isize> {
297 let count =
298 unsafe { ffi::cf_write_stream_write(self.as_ptr(), buffer.as_ptr(), buffer.len()) };
299 if count < 0 {
300 Err(count)
301 } else {
302 Ok(usize::try_from(count).expect("non-negative write count fits in usize"))
303 }
304 }
305}
306
307impl CFSocket {
308 #[must_use]
310 pub fn udp_ipv4() -> Option<Self> {
311 let ptr = unsafe { ffi::cf_socket_create_udp_ipv4() };
312 Self::from_raw(ptr)
313 }
314
315 #[must_use]
317 pub fn native(&self) -> i32 {
318 unsafe { ffi::cf_socket_get_native(self.as_ptr()) }
319 }
320
321 #[must_use]
323 pub fn is_valid(&self) -> bool {
324 unsafe { ffi::cf_socket_is_valid(self.as_ptr()) }
325 }
326
327 pub fn invalidate(&self) {
329 unsafe { ffi::cf_socket_invalidate(self.as_ptr()) };
330 }
331}
332
333impl CFFileDescriptor {
334 #[must_use]
336 pub fn from_raw_fd(native_fd: i32, close_on_invalidate: bool) -> Option<Self> {
337 let ptr = unsafe { ffi::cf_file_descriptor_create(native_fd, close_on_invalidate) };
338 Self::from_raw(ptr)
339 }
340
341 #[must_use]
343 pub fn native_descriptor(&self) -> i32 {
344 unsafe { ffi::cf_file_descriptor_get_native(self.as_ptr()) }
345 }
346
347 pub fn invalidate(&self) {
349 unsafe { ffi::cf_file_descriptor_invalidate(self.as_ptr()) };
350 }
351}