libobs_wrapper\data/
mod.rs

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/// Contains `obs_data` and its related strings. Note that
35/// this struct prevents string pointers from being freed
36/// by keeping them owned.
37/// Update: The strings are actually copied by obs itself, we don't need to store them
38#[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    /// Creates a new empty `ObsData` wrapper for the
47    /// libobs `obs_data` data structure.
48    ///
49    /// `ObsData` can then be populated using the set
50    /// functions, which take ownership of the
51    /// `ObsString` types to prevent them from being
52    /// dropped prematurely. This makes it safer than
53    /// using `obs_data` directly from libobs.
54    #[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    /// Returns a pointer to the raw `obs_data`
74    /// represented by `ObsData`.
75    pub fn as_ptr(&self) -> Sendable<*mut obs_data> {
76        self.obs_data.clone()
77    }
78
79    /// Sets a string in `obs_data` and stores it so
80    /// it in `ObsData` does not get freed.
81    #[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    /// Sets an int in `obs_data` and stores the key
105    /// in `ObsData` so it does not get freed.
106    #[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    /// Sets a bool in `obs_data` and stores the key
125    /// in `ObsData` so it does not get freed.
126    #[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    /// Sets a double in `obs_data` and stores the key
144    /// in `ObsData` so it does not get freed.
145    #[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}