1#[macro_export]
2#[doc(hidden)]
3macro_rules! __signals_impl_primitive_handler {
4 () => {move || {
5 Ok(())
6 }};
7
8 ($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 ($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}