libobs_wrapper/
signals.rs

1#[macro_export]
2#[doc(hidden)]
3macro_rules! __signals_impl_primitive_handler {
4    () => {move || {
5        Ok(())
6    }};
7
8    // Match against all primitive types
9    ($field_name: ident, i8) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, i8) };
10    ($field_name: ident, i16) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, i16) };
11    ($field_name: ident, i32) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, i32) };
12    ($field_name: ident, i64) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, i64) };
13    ($field_name: ident, i128) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, i128) };
14    ($field_name: ident, isize) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, isize) };
15
16    ($field_name: ident, u8) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, u8) };
17    ($field_name: ident, u16) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, u16) };
18    ($field_name: ident, u32) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, u32) };
19    ($field_name: ident, u64) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, u64) };
20    ($field_name: ident, u128) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, u128) };
21    ($field_name: ident, usize) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, usize) };
22
23    ($field_name: ident, f32) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, f32) };
24    ($field_name: ident, f64) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, f64) };
25
26    ($field_name: ident, bool) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, bool) };
27    ($field_name: ident, char) => { $crate::__signals_impl_primitive_handler!(__inner, $field_name, char) };
28
29    ($field_name: ident, String) => {
30        move |__internal_calldata|  {
31            let mut $field_name = std::ptr::null_mut();
32            let obs_str = $crate::utils::ObsString::new(stringify!($field_name));
33            let success = libobs::calldata_get_string(
34                __internal_calldata,
35                obs_str.as_ptr().0,
36                &mut $field_name as *const _ as _,
37            );
38
39            if !success {
40                return Err(anyhow::anyhow!(
41                    "Failed to get {} from calldata",
42                    stringify!($field_name)
43                ));
44            }
45
46            let $field_name = std::ffi::CStr::from_ptr($field_name).to_str()?;
47
48            Result::<_, anyhow::Error>::Ok($field_name.to_owned())
49        }
50    };
51
52    // For any other type, return false
53    ($field_name: ident, $other:ty) => { $crate::__signals_impl_primitive_handler!(__enum $field_name, $other) };
54
55    (__inner, $field_name: ident, $field_type: ty) => {
56        move |__internal_calldata| {
57            let mut $field_name = std::mem::zeroed::<$field_type>();
58            let obs_str = $crate::utils::ObsString::new(stringify!($field_name));
59            let success = libobs::calldata_get_data(
60                __internal_calldata,
61                obs_str.as_ptr().0,
62                &mut $field_name as *const _ as *mut std::ffi::c_void,
63                std::mem::size_of::<$field_type>(),
64            );
65
66            if !success {
67                return Err(anyhow::anyhow!(
68                    "Failed to get {} from calldata",
69                    stringify!($field_name)
70                ));
71            }
72
73            Result::<_, anyhow::Error>::Ok($field_name)
74        }
75    };
76    (__ptr, $field_name: ident, $field_type: ty) => {
77        move |__internal_calldata| {
78            let mut $field_name = std::mem::zeroed::<$field_type>();
79            let obs_str = $crate::utils::ObsString::new(stringify!($field_name));
80            let success = libobs::calldata_get_data(
81                __internal_calldata,
82                obs_str.as_ptr().0,
83                &mut $field_name as *const _ as *mut std::ffi::c_void,
84                std::mem::size_of::<$field_type>(),
85            );
86
87            if !success {
88                return Err(anyhow::anyhow!(
89                    "Failed to get {} from calldata",
90                    stringify!($field_name)
91                ));
92            }
93
94            Result::<_, anyhow::Error>::Ok($crate::unsafe_send::Sendable($field_name))
95        }
96    };
97    (__enum $field_name: ident, $enum_type: ty) => {
98        move |__internal_calldata| {
99            let code = $crate::__signals_impl_primitive_handler!(__inner, $field_name, i64)(__internal_calldata)?;
100            let en = <$enum_type>::try_from(code as i32);
101            if let Err(e) = en {
102                anyhow::bail!("Failed to convert code to {}: {}", stringify!($field_name), e);
103            }
104
105            Result::<_, anyhow::Error>::Ok(en.unwrap())
106        }
107    }
108}
109
110#[macro_export]
111#[doc(hidden)]
112macro_rules! __signals_impl_signal {
113    ($ptr: ty, $signal_name: literal, $field_name: ident: $gen_type:ty) => {
114        paste::paste! {
115            type [<__Private $signal_name:camel Type >] = $gen_type;
116            lazy_static::lazy_static! {
117                static ref [<$signal_name:snake:upper _SENDERS>]: std::sync::Arc<std::sync::RwLock<std::collections::HashMap<$crate::unsafe_send::SendableComp<$ptr>, tokio::sync::broadcast::Sender<$gen_type>>>> = std::sync::Arc::new(std::sync::RwLock::new(std::collections::HashMap::new()));
118            }
119
120            unsafe fn [< $signal_name:snake _handler_inner>](cd: *mut libobs::calldata_t) -> anyhow::Result<$gen_type> {
121                let e = $crate::__signals_impl_primitive_handler!($field_name, $gen_type)(cd);
122
123                e
124            }
125        }
126
127    };
128    ($ptr: ty, $signal_name: literal, ) => {
129        paste::paste! {
130            type [<__Private $signal_name:camel Type >] = ();
131            lazy_static::lazy_static! {
132                static ref [<$signal_name:snake:upper _SENDERS>]: std::sync::Arc<std::sync::RwLock<std::collections::HashMap<$crate::unsafe_send::SendableComp<$ptr>, tokio::sync::broadcast::Sender<()>>>> = std::sync::Arc::new(std::sync::RwLock::new(std::collections::HashMap::new()));
133            }
134
135            unsafe fn [< $signal_name:snake _handler_inner>](_cd: *mut libobs::calldata_t) -> anyhow::Result<()> {
136                Ok(())
137            }
138        }
139
140    };
141    ($ptr: ty, $signal_name: literal, struct $name: ident {
142        $($field_name: ident: $field_type: ty),* $(,)*
143    }) => {
144        $crate::__signals_impl_signal!($ptr, $signal_name, struct $name {
145            $($field_name: $field_type),*;
146            POINTERS {}
147        });
148    };
149    ($ptr: ty, $signal_name: literal, struct $name: ident {
150        POINTERS
151        {$($ptr_field_name: ident: $ptr_field_type: ty),* $(,)*}
152    }) => {
153        $crate::__signals_impl_signal!($ptr, $signal_name, struct $name {
154            ;POINTERS { $($ptr_field_name: $ptr_field_type),* }
155        });
156    };
157    ($ptr: ty, $signal_name: literal, struct $name: ident {
158        $($field_name: ident: $field_type: ty),* $(,)*;
159        POINTERS
160        {$($ptr_field_name: ident: $ptr_field_type: ty),* $(,)*}
161    }) => {
162        paste::paste! {
163            type [<__Private $signal_name:camel Type >] = $name;
164            lazy_static::lazy_static! {
165                static ref [<$signal_name:snake:upper _SENDERS>]: std::sync::Arc<std::sync::RwLock<std::collections::HashMap<$crate::unsafe_send::SendableComp<$ptr>, tokio::sync::broadcast::Sender<$name>>>> = std::sync::Arc::new(std::sync::RwLock::new(std::collections::HashMap::new()));
166            }
167
168            #[derive(Debug, Clone)]
169            pub struct $name {
170                $(pub $field_name: $field_type,)*
171                $(pub $ptr_field_name: $crate::unsafe_send::Sendable<$ptr_field_type>,)*
172            }
173
174            unsafe fn [< $signal_name:snake _handler_inner>](cd: *mut libobs::calldata_t) -> anyhow::Result<$name> {
175                $(
176                    let $field_name = $crate::__signals_impl_primitive_handler!($field_name, $field_type)(cd)?;
177                )*
178                $(
179                    let $ptr_field_name = $crate::__signals_impl_primitive_handler!(__ptr, $ptr_field_name, $ptr_field_type)(cd)?;
180                )*
181
182                Ok($name {
183                    $($field_name,)*
184                    $($ptr_field_name,)*
185                })
186            }
187        }
188    }
189}
190
191#[macro_export]
192macro_rules! impl_signal_manager {
193    ($handler_getter: expr, $name: ident for $ref: ident<$ptr: ty>, [
194        $($(#[$attr:meta])* $signal_name: literal: { $($inner_def:tt)* }),* $(,)*
195    ]) => {
196        paste::paste! {
197            $($crate::__signals_impl_signal!($ptr, $signal_name, $($inner_def)*);)*
198
199            $(
200            extern "C" fn [< $signal_name:snake _handler>](obj_ptr: *mut std::ffi::c_void, __internal_calldata: *mut libobs::calldata_t) {
201                #[allow(unused_unsafe)]
202                let res = unsafe { [< $signal_name:snake _handler_inner>](__internal_calldata) };
203                if res.is_err() {
204                    log::warn!("Error processing signal {}: {:?}", stringify!($signal_name), res.err());
205                    return;
206                }
207
208                let res = res.unwrap();
209                let senders = [<$signal_name:snake:upper _SENDERS>].read();
210                if let Err(e) = senders {
211                    log::warn!("Failed to acquire read lock for signal {}: {}", stringify!($signal_name), e);
212                    return;
213                }
214
215                let senders = senders.unwrap();
216                let senders = senders.get(&$crate::unsafe_send::SendableComp(obj_ptr as $ptr));
217                if senders.is_none() {
218                    log::warn!("No sender found for signal {}", stringify!($signal_name));
219                    return;
220                }
221
222                let senders = senders.unwrap();
223                let _ = senders.send(res);
224            })*
225
226            #[derive(Debug)]
227            pub struct $name {
228                pointer: $crate::unsafe_send::SendableComp<$ptr>,
229                runtime: $crate::runtime::ObsRuntime
230            }
231
232            impl $name {
233                pub(crate) fn new(ptr: &Sendable<$ptr>, runtime: $crate::runtime::ObsRuntime) -> Result<Self, $crate::utils::ObsError> {
234                    use $crate::{utils::ObsString, unsafe_send::SendableComp};
235                    let pointer =  SendableComp(ptr.0);
236
237                    $(
238                        let senders = [<$signal_name:snake:upper _SENDERS>].clone();
239                        let senders = senders.write();
240                        if senders.is_err() {
241                            return Err($crate::utils::ObsError::LockError("Failed to acquire write lock for signal senders".to_string()));
242                        }
243
244                        let (tx, [<_ $signal_name:snake _rx>]) = tokio::sync::broadcast::channel(16);
245                        let mut senders = senders.unwrap();
246                        senders.insert(pointer.clone(), tx);
247                    )*
248
249                    $crate::run_with_obs!(runtime, (pointer), move || {
250                            let handler = ($handler_getter)(pointer);
251                            $(
252                                let signal = ObsString::new($signal_name);
253                                unsafe {
254                                    libobs::signal_handler_connect(
255                                        handler,
256                                        signal.as_ptr().0,
257                                        Some([< $signal_name:snake _handler>]),
258                                        pointer as *mut std::ffi::c_void,
259                                    );
260                                };
261                            )*
262                    })?;
263
264                    Ok(Self {
265                        pointer,
266                        runtime
267                    })
268                }
269
270                $(
271                    $(#[$attr])*
272                    pub fn [<on_ $signal_name:snake>](&self) -> Result<tokio::sync::broadcast::Receiver<[<__Private $signal_name:camel Type >]>, $crate::utils::ObsError> {
273                        let handlers = [<$signal_name:snake:upper _SENDERS>].read();
274                        if handlers.is_err() {
275                            return Err($crate::utils::ObsError::LockError("Failed to acquire read lock for signal senders".to_string()));
276                        }
277
278                        let handlers = handlers.unwrap();
279                        let rx = handlers.get(&self.pointer)
280                            .ok_or_else(|| $crate::utils::ObsError::NoSenderError)?
281                            .subscribe();
282
283                        Ok(rx)
284                    }
285                )*
286            }
287
288            impl Drop for $name {
289                fn drop(&mut self) {
290                    log::trace!("Dropping signal manager {}...", stringify!($name));
291
292                    #[allow(unused_variables)]
293                    let ptr = self.pointer.clone();
294                    #[allow(unused_variables)]
295                    let runtime = self.runtime.clone();
296
297                    //TODO make this non blocking
298                    let future = $crate::run_with_obs!(runtime, (ptr), move || {
299                        #[allow(unused_variables)]
300                        let handler = ($handler_getter)(ptr);
301                        $(
302                            let signal = $crate::utils::ObsString::new($signal_name);
303                            unsafe {
304                                libobs::signal_handler_disconnect(
305                                    handler,
306                                    signal.as_ptr().0,
307                                    Some([< $signal_name:snake _handler>]),
308                                    ptr as *mut std::ffi::c_void,
309                                );
310                            }
311                        )*
312                    });
313
314                    let r = {
315                        $(
316                            let handlers = [<$signal_name:snake:upper _SENDERS>].write();
317                            if handlers.is_err() {
318                                log::warn!("Failed to acquire write lock for signal {} senders during drop", stringify!($signal_name));
319                                return;
320                            }
321
322                            let mut handlers = handlers.unwrap();
323                            handlers.remove(&self.pointer);
324                        )*
325
326                        future
327                    };
328
329                    if std::thread::panicking() {
330                        return;
331                    }
332
333                    r.unwrap();
334                }
335            }
336        }
337    };
338}