#![allow(clippy::missing_panics_doc, clippy::missing_errors_doc)]
use super::base::impl_cf_type_wrapper;
use super::{CFDictionary, CFString};
use crate::ffi;
use std::time::Duration;
impl_cf_type_wrapper!(CFNotificationCenter, cf_notification_center_get_type_id);
impl_cf_type_wrapper!(CFRunLoop, cf_run_loop_get_type_id);
impl_cf_type_wrapper!(CFTimer, cf_run_loop_timer_get_type_id);
impl_cf_type_wrapper!(CFMessagePort, cf_message_port_get_type_id);
impl_cf_type_wrapper!(CFReadStream, cf_read_stream_get_type_id);
impl_cf_type_wrapper!(CFWriteStream, cf_write_stream_get_type_id);
impl_cf_type_wrapper!(CFSocket, cf_socket_get_type_id);
impl_cf_type_wrapper!(CFFileDescriptor, cf_file_descriptor_get_type_id);
fn duration_to_seconds(duration: Duration) -> f64 {
duration.as_secs_f64()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum CFRunLoopRunResult {
Finished = 1,
Stopped = 2,
TimedOut = 3,
HandledSource = 4,
}
impl CFNotificationCenter {
#[must_use]
pub fn local() -> Self {
let ptr = unsafe { ffi::cf_notification_center_get_local() };
Self::from_raw(ptr).expect("CFNotificationCenterGetLocalCenter returned NULL")
}
#[must_use]
pub fn distributed() -> Self {
let ptr = unsafe { ffi::cf_notification_center_get_distributed() };
Self::from_raw(ptr).expect("CFNotificationCenterGetDistributedCenter returned NULL")
}
#[must_use]
pub fn darwin() -> Self {
let ptr = unsafe { ffi::cf_notification_center_get_darwin() };
Self::from_raw(ptr).expect("CFNotificationCenterGetDarwinNotifyCenter returned NULL")
}
pub fn post(
&self,
name: &CFString,
user_info: Option<&CFDictionary>,
deliver_immediately: bool,
) {
unsafe {
ffi::cf_notification_center_post_notification(
self.as_ptr(),
name.as_ptr(),
user_info.map_or(std::ptr::null_mut(), CFDictionary::as_ptr),
deliver_immediately,
);
}
}
}
impl CFRunLoop {
#[must_use]
pub fn current() -> Self {
let ptr = unsafe { ffi::cf_run_loop_get_current() };
Self::from_raw(ptr).expect("CFRunLoopGetCurrent returned NULL")
}
#[must_use]
pub fn main() -> Self {
let ptr = unsafe { ffi::cf_run_loop_get_main() };
Self::from_raw(ptr).expect("CFRunLoopGetMain returned NULL")
}
#[must_use]
pub fn run_in_default_mode(
&self,
duration: Duration,
return_after_source_handled: bool,
) -> CFRunLoopRunResult {
let code = unsafe {
ffi::cf_run_loop_run_in_default_mode(
duration_to_seconds(duration),
return_after_source_handled,
)
};
match code {
1 => CFRunLoopRunResult::Finished,
2 => CFRunLoopRunResult::Stopped,
4 => CFRunLoopRunResult::HandledSource,
_ => CFRunLoopRunResult::TimedOut,
}
}
pub fn wake_up(&self) {
unsafe { ffi::cf_run_loop_wake_up(self.as_ptr()) };
}
pub fn stop(&self) {
unsafe { ffi::cf_run_loop_stop(self.as_ptr()) };
}
pub fn add_timer(&self, timer: &CFTimer) {
unsafe { ffi::cf_run_loop_add_timer(self.as_ptr(), timer.as_ptr()) };
}
}
impl CFTimer {
#[must_use]
pub fn new(interval: Duration, repeats: bool) -> Self {
let ptr = unsafe { ffi::cf_run_loop_timer_create(duration_to_seconds(interval), repeats) };
Self::from_raw(ptr).expect("CFRunLoopTimerCreate returned NULL")
}
#[must_use]
pub fn is_valid(&self) -> bool {
unsafe { ffi::cf_run_loop_timer_is_valid(self.as_ptr()) }
}
pub fn fire(&self) {
unsafe { ffi::cf_run_loop_timer_fire(self.as_ptr()) };
}
pub fn invalidate(&self) {
unsafe { ffi::cf_run_loop_timer_invalidate(self.as_ptr()) };
}
}
impl CFMessagePort {
#[must_use]
pub fn create_echo_local(name: &str) -> Self {
let name =
std::ffi::CString::new(name).expect("message-port name may not contain NUL bytes");
let ptr = unsafe { ffi::cf_message_port_create_echo_local(name.as_ptr()) };
Self::from_raw(ptr).expect("CFMessagePortCreateLocal returned NULL")
}
#[must_use]
pub fn connect_remote(name: &str) -> Option<Self> {
let name =
std::ffi::CString::new(name).expect("message-port name may not contain NUL bytes");
let ptr = unsafe { ffi::cf_message_port_create_remote(name.as_ptr()) };
Self::from_raw(ptr)
}
pub fn send_request(&self, bytes: &[u8], timeout: Duration) -> Result<Vec<u8>, i32> {
let mut out_bytes = std::ptr::null_mut();
let mut out_len = 0_usize;
let status = unsafe {
ffi::cf_message_port_send_request(
self.as_ptr(),
bytes.as_ptr(),
bytes.len(),
duration_to_seconds(timeout),
&mut out_bytes,
&mut out_len,
)
};
if status != 0 {
return Err(status);
}
if out_bytes.is_null() {
return Ok(Vec::new());
}
let reply = unsafe { std::slice::from_raw_parts(out_bytes, out_len) }.to_vec();
unsafe { ffi::cf_message_port_free_bytes(out_bytes, out_len) };
Ok(reply)
}
pub fn invalidate(&self) {
unsafe { ffi::cf_message_port_invalidate(self.as_ptr()) };
}
}
#[derive(Debug, Clone)]
pub struct CFStreamPair {
pub read: CFReadStream,
pub write: CFWriteStream,
}
impl CFStreamPair {
#[must_use]
pub fn new(transfer_buffer_size: usize) -> Self {
let mut read = std::ptr::null_mut();
let mut write = std::ptr::null_mut();
unsafe { ffi::cf_stream_create_bound_pair(transfer_buffer_size, &mut read, &mut write) };
Self {
read: CFReadStream::from_raw(read)
.expect("CFStreamCreateBoundPair read stream was NULL"),
write: CFWriteStream::from_raw(write)
.expect("CFStreamCreateBoundPair write stream was NULL"),
}
}
}
impl CFReadStream {
#[must_use]
pub fn open(&self) -> bool {
unsafe { ffi::cf_read_stream_open(self.as_ptr()) }
}
pub fn close(&self) {
unsafe { ffi::cf_read_stream_close(self.as_ptr()) };
}
pub fn read(&self, buffer: &mut [u8]) -> Result<usize, isize> {
let count =
unsafe { ffi::cf_read_stream_read(self.as_ptr(), buffer.as_mut_ptr(), buffer.len()) };
if count < 0 {
Err(count)
} else {
Ok(usize::try_from(count).expect("non-negative read count fits in usize"))
}
}
}
impl CFWriteStream {
#[must_use]
pub fn open(&self) -> bool {
unsafe { ffi::cf_write_stream_open(self.as_ptr()) }
}
pub fn close(&self) {
unsafe { ffi::cf_write_stream_close(self.as_ptr()) };
}
pub fn write(&self, buffer: &[u8]) -> Result<usize, isize> {
let count =
unsafe { ffi::cf_write_stream_write(self.as_ptr(), buffer.as_ptr(), buffer.len()) };
if count < 0 {
Err(count)
} else {
Ok(usize::try_from(count).expect("non-negative write count fits in usize"))
}
}
}
impl CFSocket {
#[must_use]
pub fn udp_ipv4() -> Option<Self> {
let ptr = unsafe { ffi::cf_socket_create_udp_ipv4() };
Self::from_raw(ptr)
}
#[must_use]
pub fn native(&self) -> i32 {
unsafe { ffi::cf_socket_get_native(self.as_ptr()) }
}
#[must_use]
pub fn is_valid(&self) -> bool {
unsafe { ffi::cf_socket_is_valid(self.as_ptr()) }
}
pub fn invalidate(&self) {
unsafe { ffi::cf_socket_invalidate(self.as_ptr()) };
}
}
impl CFFileDescriptor {
#[must_use]
pub fn from_raw_fd(native_fd: i32, close_on_invalidate: bool) -> Option<Self> {
let ptr = unsafe { ffi::cf_file_descriptor_create(native_fd, close_on_invalidate) };
Self::from_raw(ptr)
}
#[must_use]
pub fn native_descriptor(&self) -> i32 {
unsafe { ffi::cf_file_descriptor_get_native(self.as_ptr()) }
}
pub fn invalidate(&self) {
unsafe { ffi::cf_file_descriptor_invalidate(self.as_ptr()) };
}
}