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<$crate::utils::async_sync::RwLock<std::collections::HashMap<$crate::unsafe_send::SendableComp<$ptr>, tokio::sync::broadcast::Sender<$gen_type>>>> = std::sync::Arc::new($crate::utils::async_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<$crate::utils::async_sync::RwLock<std::collections::HashMap<$crate::unsafe_send::SendableComp<$ptr>, tokio::sync::broadcast::Sender<()>>>> = std::sync::Arc::new($crate::utils::async_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<$crate::utils::async_sync::RwLock<std::collections::HashMap<$crate::unsafe_send::SendableComp<$ptr>, tokio::sync::broadcast::Sender<$name>>>> = std::sync::Arc::new($crate::utils::async_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        $($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                #[cfg(feature="blocking")]
210                let senders = [<$signal_name:snake:upper _SENDERS>].read();
211                #[cfg(not(feature="blocking"))]
212                let senders = futures::executor::block_on([<$signal_name:snake:upper _SENDERS>].read());
213                let senders = senders.get(&$crate::unsafe_send::SendableComp(obj_ptr as $ptr));
214                if senders.is_none() {
215                    log::warn!("No sender found for signal {}", stringify!($signal_name));
216                    return;
217                }
218
219                let senders = senders.unwrap();
220                let _ = senders.send(res);
221            })*
222
223            #[derive(Debug)]
224            pub struct $name {
225                pointer: $crate::unsafe_send::SendableComp<$ptr>,
226                runtime: $crate::runtime::ObsRuntime
227            }
228
229            impl $name {
230                #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
231                pub(crate) async fn new(ptr: &Sendable<$ptr>, runtime: $crate::runtime::ObsRuntime) -> Result<Self, crate::utils::ObsError> {
232                    use crate::{utils::ObsString, unsafe_send::SendableComp};
233                    let pointer =  SendableComp(ptr.0);
234
235                    $(
236                        let senders = [<$signal_name:snake:upper _SENDERS>].clone();
237                        let mut senders = senders.write().await;
238                        let (tx, [<_ $signal_name:snake _rx>]) = tokio::sync::broadcast::channel(16);
239                        senders.insert(pointer.clone(), tx);
240                    )*
241
242                    crate::run_with_obs!(runtime, (pointer), move || unsafe {
243                            let handler = ($handler_getter)(pointer);
244                            $(
245                                let signal = ObsString::new($signal_name);
246                                libobs::signal_handler_connect(
247                                    handler,
248                                    signal.as_ptr().0,
249                                    Some([< $signal_name:snake _handler>]),
250                                    pointer as *mut std::ffi::c_void,
251                                );
252                            )*
253                    }).await?;
254
255                    Ok(Self {
256                        pointer,
257                        runtime
258                    })
259                }
260
261                $(
262                    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
263                    pub async fn [<on_ $signal_name:snake>](&self) -> Result<tokio::sync::broadcast::Receiver<[<__Private $signal_name:camel Type >]>, crate::utils::ObsError> {
264                        let handlers = [<$signal_name:snake:upper _SENDERS>].read().await;
265                        let rx = handlers.get(&self.pointer)
266                            .ok_or_else(|| crate::utils::ObsError::NoSenderError)?
267                            .subscribe();
268
269                        Ok(rx)
270                    }
271                )*
272            }
273
274            impl Drop for $name {
275                fn drop(&mut self) {
276                    #[allow(unused_variables)]
277                    let ptr = self.pointer.clone();
278                    #[allow(unused_variables)]
279                    let runtime = self.runtime.clone();
280
281                    let future = crate::run_with_obs!(runtime, (ptr), move || unsafe {
282                        #[allow(unused_variables)]
283                        let handler = ($handler_getter)(ptr);
284                        $(
285                            let signal = crate::utils::ObsString::new($signal_name);
286                            libobs::signal_handler_disconnect(
287                                handler,
288                                signal.as_ptr().0,
289                                Some([< $signal_name:snake _handler>]),
290                                ptr as *mut std::ffi::c_void,
291                            );
292                        )*
293                    });
294
295                    #[allow(unused_variables)]
296                    let tmp_ptr = self.pointer.clone();
297                    #[cfg(not(feature="blocking"))]
298                    let r = futures::executor::block_on(async move {
299                        $(
300                            let mut handlers = [<$signal_name:snake:upper _SENDERS>].write().await;
301                            handlers.remove(&tmp_ptr);
302                        )*
303
304                        future.await
305                    });
306
307                    #[cfg(feature="blocking")]
308                    let r = {
309                        $(
310                            let mut handlers = [<$signal_name:snake:upper _SENDERS>].write();
311                            handlers.remove(&self.pointer);
312                        )*
313
314                        future
315                    };
316
317                    if std::thread::panicking() {
318                        return;
319                    }
320
321                    r.unwrap();
322                }
323            }
324        }
325    };
326}