apple_cf/dispatch_queue.rs
1//! Dispatch Queue wrapper for custom queue management
2//!
3//! This module provides a safe Rust wrapper around GCD (Grand Central Dispatch) queues
4//! that can be used with `ScreenCaptureKit` streams.
5//!
6//! ## When to Use Custom Queues
7//!
8//! By default, stream output handlers are called on a system-managed queue. Use a custom
9//! queue when you need:
10//!
11//! - **Priority control** - Use `UserInteractive` `QoS` for low-latency UI updates
12//! - **Thread isolation** - Ensure handlers run on a specific queue
13//! - **Performance tuning** - Adjust queue priority based on your app's needs
14//!
15//! ## Example
16//!
17//! ```rust,no_run
18//! use apple_cf::dispatch_queue::{DispatchQueue, DispatchQoS};
19//!
20//! // Create a high-priority queue for frame processing
21//! let queue = DispatchQueue::new("com.myapp.capture", DispatchQoS::UserInteractive);
22//! // Pass `queue.as_ptr()` into any framework that accepts a `dispatch_queue_t`.
23//! ```
24
25use std::ffi::{c_void, CString};
26use std::fmt;
27
28/// Quality of Service levels for dispatch queues
29///
30/// These `QoS` levels help the system prioritize work appropriately.
31///
32/// # Examples
33///
34/// ```
35/// use apple_cf::dispatch_queue::{DispatchQueue, DispatchQoS};
36///
37/// // High priority for UI-affecting work
38/// let queue = DispatchQueue::new("com.myapp.ui", DispatchQoS::UserInteractive);
39///
40/// // Lower priority for background tasks
41/// let bg_queue = DispatchQueue::new("com.myapp.background", DispatchQoS::Background);
42/// ```
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
44pub enum DispatchQoS {
45 /// Background `QoS` - for maintenance or cleanup tasks
46 Background = 0,
47 /// Utility `QoS` - for tasks that may take some time
48 Utility = 1,
49 /// Default `QoS` - standard priority
50 #[default]
51 Default = 2,
52 /// User Initiated `QoS` - for tasks initiated by the user
53 UserInitiated = 3,
54 /// User Interactive `QoS` - for tasks that affect the UI
55 UserInteractive = 4,
56}
57
58/// A wrapper around GCD `DispatchQueue`
59///
60/// This allows you to provide a custom dispatch queue for stream output handling
61/// instead of using the default queue.
62///
63/// # Example
64///
65/// ```no_run
66/// use apple_cf::dispatch_queue::{DispatchQueue, DispatchQoS};
67///
68/// let queue = DispatchQueue::new("com.myapp.capture", DispatchQoS::UserInteractive);
69/// ```
70pub struct DispatchQueue {
71 ptr: *const c_void,
72}
73
74unsafe impl Send for DispatchQueue {}
75unsafe impl Sync for DispatchQueue {}
76
77impl DispatchQueue {
78 /// Creates a new dispatch queue with the specified label and `QoS`
79 ///
80 /// # Arguments
81 ///
82 /// * `label` - A string label for the queue (e.g., "com.myapp.capture")
83 /// * `qos` - The quality of service level for the queue
84 ///
85 /// # Examples
86 ///
87 /// ```
88 /// use apple_cf::dispatch_queue::{DispatchQueue, DispatchQoS};
89 ///
90 /// let queue = DispatchQueue::new("com.myapp.capture", DispatchQoS::UserInteractive);
91 /// // Use the queue with SCStream's add_output_handler_with_queue
92 /// ```
93 ///
94 /// # Panics
95 ///
96 /// Panics if the label contains null bytes or if queue creation fails
97 #[must_use]
98 pub fn new(label: &str, qos: DispatchQoS) -> Self {
99 let c_label = CString::new(label).expect("Label contains null byte");
100 let ptr = unsafe { crate::ffi::dispatch_queue_create(c_label.as_ptr(), qos as i32) };
101 assert!(!ptr.is_null(), "Failed to create dispatch queue");
102 Self { ptr }
103 }
104
105 /// Returns the raw pointer to the dispatch queue
106 ///
107 /// This is used internally for FFI calls (and for testing)
108 #[must_use]
109 pub const fn as_ptr(&self) -> *const c_void {
110 self.ptr
111 }
112}
113
114impl Clone for DispatchQueue {
115 fn clone(&self) -> Self {
116 unsafe {
117 Self {
118 ptr: crate::ffi::dispatch_queue_retain(self.ptr),
119 }
120 }
121 }
122}
123
124impl Drop for DispatchQueue {
125 fn drop(&mut self) {
126 unsafe {
127 crate::ffi::dispatch_queue_release(self.ptr);
128 }
129 }
130}
131
132impl fmt::Debug for DispatchQueue {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 f.debug_struct("DispatchQueue")
135 .field("ptr", &self.ptr)
136 .finish()
137 }
138}
139
140impl fmt::Display for DispatchQueue {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 write!(f, "DispatchQueue")
143 }
144}