1use std::{
2 ffi::{CStr, CString},
3 sync::Arc,
4};
5
6use libobs::{
7 obs_data, obs_data_create, obs_data_release, obs_data_set_bool, obs_data_set_double,
8 obs_data_set_int, obs_data_set_string,
9};
10
11use crate::{
12 impl_obs_drop, run_with_obs,
13 runtime::ObsRuntime,
14 unsafe_send::Sendable,
15 utils::{ObsError, ObsString},
16};
17
18pub mod audio;
19pub mod immutable;
20mod lib_support;
21pub mod output;
22pub mod properties;
23pub mod video;
24pub use lib_support::*;
25mod updater;
26pub use updater::*;
27
28#[derive(Debug)]
29pub(crate) struct _ObsDataDropGuard {
30 obs_data: Sendable<*mut obs_data>,
31 pub(crate) runtime: ObsRuntime,
32}
33
34#[derive(Debug)]
39pub struct ObsData {
40 obs_data: Sendable<*mut obs_data>,
41 pub(crate) runtime: ObsRuntime,
42 pub(crate) _drop_guard: Arc<_ObsDataDropGuard>,
43}
44
45impl ObsData {
46 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
55 pub async fn new(runtime: ObsRuntime) -> Result<Self, ObsError> {
56 let obs_data = run_with_obs!(runtime, move || unsafe { Sendable(obs_data_create()) }).await?;
57
58 Ok(ObsData {
59 obs_data: obs_data.clone(),
60 runtime: runtime.clone(),
61 _drop_guard: Arc::new(_ObsDataDropGuard { obs_data, runtime }),
62 })
63 }
64
65 pub fn bulk_update(&mut self) -> ObsDataUpdater {
66 ObsDataUpdater {
67 changes: Vec::new(),
68 obs_data: self.obs_data.clone(),
69 _drop_guard: self._drop_guard.clone(),
70 }
71 }
72
73 pub fn as_ptr(&self) -> Sendable<*mut obs_data> {
76 self.obs_data.clone()
77 }
78
79 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
82 pub async fn set_string<T: Into<ObsString> + Send + Sync, K: Into<ObsString> + Send + Sync>(
83 &mut self,
84 key: T,
85 value: K,
86 ) -> Result<&mut Self, ObsError> {
87 let key = key.into();
88 let value = value.into();
89
90 let key_ptr = key.as_ptr();
91 let value_ptr = value.as_ptr();
92 let data_ptr = self.obs_data.clone();
93
94 run_with_obs!(
95 self.runtime,
96 (data_ptr, key_ptr, value_ptr),
97 move || unsafe { obs_data_set_string(data_ptr, key_ptr, value_ptr) }
98 )
99 .await?;
100
101 Ok(self)
102 }
103
104 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
107 pub async fn set_int<T: Into<ObsString> + Sync + Send>(
108 &mut self,
109 key: T,
110 value: i64,
111 ) -> Result<&mut Self, ObsError> {
112 let key = key.into();
113
114 let key_ptr = key.as_ptr();
115 let data_ptr = self.obs_data.clone();
116
117 run_with_obs!(self.runtime, (key_ptr, data_ptr), move || unsafe {
118 obs_data_set_int(data_ptr, key_ptr, value.into());
119 }).await?;
120
121 Ok(self)
122 }
123
124 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
127 pub async fn set_bool<T: Into<ObsString> + Sync + Send>(
128 &mut self,
129 key: T,
130 value: bool,
131 ) -> Result<&mut Self, ObsError> {
132 let key = key.into();
133
134 let key_ptr = key.as_ptr();
135 let data_ptr = self.obs_data.clone();
136 run_with_obs!(self.runtime, (key_ptr, data_ptr), move || unsafe {
137 obs_data_set_bool(data_ptr, key_ptr, value.into());
138 }).await?;
139
140 Ok(self)
141 }
142
143 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
146 pub async fn set_double<T: Into<ObsString> + Sync + Send>(
147 &mut self,
148 key: T,
149 value: f64,
150 ) -> Result<&mut Self, ObsError> {
151 let key = key.into();
152
153 let key_ptr = key.as_ptr();
154 let data_ptr = self.obs_data.clone();
155
156 run_with_obs!(self.runtime, (key_ptr, data_ptr), move || unsafe {
157 obs_data_set_double(data_ptr, key_ptr, value.into());
158 }).await?;
159
160 Ok(self)
161 }
162
163 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
164 pub async fn from_json(json: &str, runtime: ObsRuntime) -> Result<Self, ObsError> {
165 let cstr = CString::new(json).map_err(|_| ObsError::JsonParseError)?;
166
167 let cstr_ptr = Sendable(cstr.as_ptr());
168 let result = run_with_obs!(runtime, (cstr_ptr), move || unsafe {
169 Sendable(libobs::obs_data_create_from_json(cstr_ptr))
170 }).await?;
171
172 if result.0.is_null() {
173 return Err(ObsError::JsonParseError);
174 }
175
176 Ok(ObsData {
177 obs_data: result.clone(),
178 runtime: runtime.clone(),
179 _drop_guard: Arc::new(_ObsDataDropGuard {
180 obs_data: result,
181 runtime,
182 }),
183 })
184 }
185
186 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
187 pub async fn get_json(&self) -> Result<String, ObsError> {
188 let data_ptr = self.obs_data.clone();
189 let ptr = run_with_obs!(self.runtime, (data_ptr), move || unsafe {
190 Sendable(libobs::obs_data_get_json(data_ptr))
191 }).await?;
192
193 if ptr.0.is_null() {
194 return Err(ObsError::NullPointer);
195 }
196
197 let ptr = unsafe { CStr::from_ptr(ptr.0) };
198 let ptr = ptr.to_str().map_err(|_| ObsError::JsonParseError)?;
199
200 Ok(ptr.to_string())
201 }
202}
203
204impl_obs_drop!(_ObsDataDropGuard, (obs_data), move || unsafe {
205 obs_data_release(obs_data)
206});
207
208impl ObsData {
209 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
210 pub async fn clone(&self) -> Result<Self, ObsError> {
211 let json = self.get_json().await?;
212
213 Self::from_json(json.as_str(), self.runtime.clone()).await
214 }
215}