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};