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