libobs_wrapper\utils/
calldata.rs1use std::{
2 ffi::{c_char, CStr},
3 mem::MaybeUninit,
4 pin::Pin,
5 sync::Arc,
6};
7
8use libobs::{calldata_t, proc_handler_t};
9
10use crate::{
11 context::ObsContext,
12 impl_obs_drop, run_with_obs,
13 runtime::ObsRuntime,
14 unsafe_send::Sendable,
15 utils::{calldata_free, ObsError, ObsString},
16};
17
18pub struct CalldataWrapper {
20 _drop_guard: Arc<_CalldataWrapperDropGuard>,
22 data: Sendable<Pin<Box<calldata_t>>>, runtime: ObsRuntime,
26}
27
28impl CalldataWrapper {
29 pub unsafe fn as_mut_ptr(&mut self) -> Sendable<*mut calldata_t> {
36 let r: &mut calldata_t = Pin::as_mut(&mut self.data.0).get_unchecked_mut();
38 Sendable(r as *mut _)
39 }
40
41 pub fn get_string<T: Into<ObsString>>(&mut self, key: T) -> Result<String, ObsError> {
43 let key: ObsString = key.into();
44 let self_ptr = unsafe {
45 self.as_mut_ptr()
47 };
48
49 let _drop_guard = self._drop_guard.clone(); let value = run_with_obs!(
51 self.runtime.clone(),
52 (_drop_guard, self_ptr, key),
53 move || {
54 let key_ptr = key.as_ptr().0;
55
56 let mut data = MaybeUninit::<*const c_char>::uninit();
57 let ok = unsafe {
58 libobs::calldata_get_string(self_ptr.0, key_ptr, data.as_mut_ptr())
60 };
61 if !ok {
62 return Err(ObsError::Unexpected(format!(
63 "Calldata String {key} not found."
64 )));
65 }
66
67 let data_ptr = unsafe {
68 data.assume_init()
70 };
71 if data_ptr.is_null() {
72 return Err(ObsError::Unexpected(format!(
73 "Calldata String {key} is null."
74 )));
75 }
76
77 let data = unsafe {
78 CStr::from_ptr(data_ptr)
80 };
81 let data = data.to_str();
82 if let Err(_e) = data {
83 return Err(ObsError::Unexpected(format!(
84 "Calldata String {key} is not valid UTF-8."
85 )));
86 }
87
88 let data = data.unwrap();
89 Ok(data.to_string())
90 }
91 )??;
92
93 Ok(value)
94 }
95
96 }
98
99struct _CalldataWrapperDropGuard {
100 calldata_ptr: Sendable<*mut calldata_t>,
101 runtime: ObsRuntime,
102}
103
104impl_obs_drop!(_CalldataWrapperDropGuard, (calldata_ptr), move || unsafe {
105 calldata_free(calldata_ptr.0);
107});
108
109pub trait ObsCalldataExt {
111 unsafe fn call_proc_handler<T: Into<ObsString>>(
114 &self,
115 proc_handler: &Sendable<*mut proc_handler_t>,
116 name: T,
117 ) -> Result<CalldataWrapper, ObsError>;
118}
119
120impl ObsCalldataExt for ObsRuntime {
121 unsafe fn call_proc_handler<T: Into<ObsString>>(
122 &self,
123 proc_handler: &Sendable<*mut proc_handler_t>,
124 name: T,
125 ) -> Result<CalldataWrapper, ObsError> {
126 if proc_handler.0.is_null() {
127 return Err(ObsError::NullPointer(None));
128 }
129
130 let proc_handler = proc_handler.clone();
131 let name: ObsString = name.into();
132 let mut calldata = run_with_obs!(self.clone(), (proc_handler, name), move || {
133 let data: calldata_t = unsafe { std::mem::zeroed() };
135 let mut data = Box::pin(data);
136 let raw_ptr = unsafe { Pin::as_mut(&mut data).get_unchecked_mut() };
138
139 let ok = unsafe { libobs::proc_handler_call(proc_handler.0, name.as_ptr().0, raw_ptr) };
141 if !ok {
142 return Err(ObsError::Unexpected(
143 "Couldn't call proc handler".to_string(),
144 ));
145 }
146
147 Ok(Sendable(data))
148 })??;
149
150 let calldata_ptr = unsafe { Pin::as_mut(&mut calldata.0).get_unchecked_mut() };
152
153 let guard = Arc::new(_CalldataWrapperDropGuard {
155 calldata_ptr: Sendable(calldata_ptr),
156 runtime: self.clone(),
157 });
158
159 Ok(CalldataWrapper {
160 data: calldata,
161 runtime: self.clone(),
162 _drop_guard: guard,
163 })
164 }
165}
166
167impl ObsCalldataExt for ObsContext {
168 unsafe fn call_proc_handler<T: Into<ObsString>>(
169 &self,
170 proc_handler: &Sendable<*mut proc_handler_t>,
171 name: T,
172 ) -> Result<CalldataWrapper, ObsError> {
173 self.runtime().call_proc_handler(proc_handler, name)
174 }
175}