Skip to main content

libobs_simple\sources/
macro_helper.rs

1/// Generates builder and updater structs for OBS objects.
2///
3/// This macro creates two structures for managing OBS objects:
4/// - A `{StructName}Builder` struct for constructing new OBS objects
5/// - A `{StructName}Updater` struct for updating existing OBS objects
6///
7/// # Arguments
8///
9/// - `struct $struct_name`: The base name for the generated builder and updater structs
10/// - `$obs_id`: A string literal representing the OBS object type ID
11/// - `$underlying_ptr_type`: The underlying pointer type (e.g., `*mut libobs::obs_source`)
12/// - `$updatable_name`: The name of the updatable trait/type to implement
13/// - Field definitions: Custom fields with their types and optional doc attributes
14///
15/// # Example
16///
17/// ```ignore
18/// define_object_manager!(
19///     struct MySource("underlying_obs_source_id", *mut libobs::obs_source) for MySourceUpdatable {
20///         /// ALSA device ID (e.g., "default", "hw:0,0", or custom PCM device)
21///        #[obs_property(type_t = "string")]
22///        device_id: String,
23///
24///        /// Custom PCM device name (used when device_id is "__custom__")
25///        #[obs_property(type_t = "string")]
26///        custom_pcm: String,
27///     }
28/// );
29/// ```
30#[allow(unused)]
31#[macro_export]
32macro_rules! define_object_manager {
33    ($(#[$parent_meta:meta])* struct $struct_name:ident($obs_id:literal, $underlying_ptr_type: ty) for $updatable_name:ident {
34        $(
35            $(#[$meta:meta])*
36            $field:ident: $ty:ty,
37        )*
38    }) => {
39        paste::paste! {
40            #[libobs_simple_macro::obs_object_builder($obs_id)]
41            $(#[$parent_meta])*
42            pub struct [<$struct_name Builder>] {
43                $(
44                    $(#[$meta])*
45                    $field: $ty,
46                )*
47            }
48
49            #[libobs_simple_macro::obs_object_updater($obs_id, $updatable_name, $underlying_ptr_type)]
50            /// Used to update the source this updater was created from. For more details look
51            /// at docs for the corresponding builder.
52            pub struct [<$struct_name Updater>] {
53                $(
54                    $(#[$meta])*
55                    $field: $ty,
56                )*
57            }
58        }
59    };
60}
61
62/// Implements custom source functionality with optional signal management.
63///
64/// This macro provides multiple overloads:
65/// 1. Simple version: Takes only the custom source struct name
66/// 2. Advanced version: Takes the struct name and signal definitions
67///
68/// The macro generates:
69/// - A custom source struct wrapping `ObsSourceRef` with signal management
70/// - Methods for creating updaters and accessing source-specific signals
71/// - Automatic forwarding of OBS object and source trait implementations
72///
73/// # Arguments
74///
75/// - `$new_source_struct`: The name of the custom source struct to implement
76/// - `[$signal_name: { ... }]`: Optional signal definitions with attributes and implementations
77///
78/// # Example
79///
80/// ```ignore
81/// impl_custom_source!(MyCustomSource);
82///
83/// // Or with signals:
84/// impl_custom_source!(MyCustomSource, [
85///     "signal_name": { /* signal definition */ },
86/// ]);
87///
88/// // Or with a custom signal struct name:
89/// impl_custom_source!(MyCustomSource, MyCustomSourceSignals);
90/// ```
91#[allow(unused)]
92macro_rules! impl_custom_source {
93    ($new_source_struct: ident) => {
94        impl_custom_source!($new_source_struct, []);
95    };
96    ($new_source_struct: ident, [
97        $($(#[$attr:meta])* $signal_name: literal: { $($inner_def:tt)* }),* $(,)*
98    ]) => {
99        paste::paste! {
100            libobs_wrapper::impl_signal_manager!(|ptr: libobs_wrapper::unsafe_send::SmartPointerSendable<*mut libobs::obs_source>| unsafe {
101                    // Safety: This is a smart pointer, so it is fine
102                    libobs::obs_source_get_signal_handler(ptr.get_ptr())
103                }, [<$new_source_struct Signals>] for *mut libobs::obs_source, [
104            $($(#[$attr])* $signal_name: { $($inner_def)* }),*
105            ]);
106
107            impl_custom_source!($new_source_struct, [<$new_source_struct Signals>]);
108        }
109    };
110    ($new_source_struct: ident, $signal_struct_name: ident) => {
111        impl_custom_source!($new_source_struct, $signal_struct_name, NO_SPECIFIC_SIGNALS_FUNCTION);
112
113        impl $new_source_struct {
114            pub fn source_specific_signals(&self) -> std::sync::Arc<$signal_struct_name> {
115                self.source_specific_signals.clone()
116            }
117        }
118    };
119    ($new_source_struct: ident, $signal_struct_name: ident, NO_SPECIFIC_SIGNALS_FUNCTION) => {
120        paste::paste!{
121            #[derive(Debug, Clone)]
122            /// This struct is essentially a wrapper around an OBS source with
123            /// additional functionality specific to the custom source.
124            ///
125            /// It provides methods to create an updater and access source-specific signals.
126            pub struct $new_source_struct {
127                source: ObsSourceRef,
128                source_specific_signals: std::sync::Arc<$signal_struct_name>,
129            }
130
131            impl $new_source_struct {
132                fn new(source: ObsSourceRef) -> Result<Self, libobs_wrapper::utils::ObsError> {
133                    use libobs_wrapper::data::object::ObsObjectTrait;
134                    let source_specific_signals =
135                        $signal_struct_name::new(&source.as_ptr(), source.runtime().clone())?;
136
137                    Ok(Self {
138                        source,
139                        source_specific_signals: std::sync::Arc::new(source_specific_signals),
140                    })
141                }
142
143                pub fn create_updater<'a>(&'a mut self) -> Result<[<$new_source_struct Updater>]<'a>, libobs_wrapper::utils::ObsError> {
144                    use libobs_wrapper::data::ObsObjectUpdater;
145                    use libobs_wrapper::data::object::ObsObjectTrait;
146                    [<$new_source_struct Updater>]::create_update(
147                        self.runtime().clone(),
148                        self.inner_source_mut()
149                    )
150                }
151            }
152
153            libobs_wrapper::forward_obs_object_impl!($new_source_struct, source, *mut libobs::obs_source);
154            libobs_wrapper::forward_obs_source_impl!($new_source_struct, source);
155        }
156    };
157}
158
159/// Implements the `ObsSourceBuilder` trait for a builder struct.
160///
161/// This macro provides a default implementation of `ObsSourceBuilder` that:
162/// - Calls the struct's `object_build()` method to construct the underlying OBS object
163/// - Wraps the result in an `ObsSourceRef` for the final return type
164///
165/// # Arguments
166///
167/// - `$name`: The name of the builder struct implementing this trait
168///
169/// # Example
170///
171/// ```ignore
172/// impl_default_builder!(MySourceBuilder);
173/// ```
174///
175/// This assumes the struct has a `runtime` field and an `object_build()` method.
176#[allow(unused)]
177macro_rules! impl_default_builder {
178    ($name: ident) => {
179        impl libobs_wrapper::sources::ObsSourceBuilder for $name {
180            type T = libobs_wrapper::sources::ObsSourceRef;
181
182            fn build(self) -> Result<Self::T, libobs_wrapper::utils::ObsError>
183            where
184                Self: Sized,
185            {
186                use libobs_wrapper::data::ObsObjectBuilder;
187                let runtime = self.runtime.clone();
188                libobs_wrapper::sources::ObsSourceRef::new_from_info(self.object_build()?, runtime)
189            }
190        }
191    };
192}
193
194#[allow(unused)]
195pub(crate) use {define_object_manager, impl_custom_source, impl_default_builder};