Skip to main content

libobs_wrapper\data/
mod.rs

1//! This module contains every struct related to storing data in OBS.
2//! There are two important structs:
3//! 1. `ObsData`
4//! - This struct holds a mutable reference to a ObsData, so you can set a string, ints and similar
5//! - You can convert this ObsData object to a immutable reference
6//! - Cloning this ObsData struct is very memory intensive, as the ObsData will completely clone every member of this data.
7//! 2. `ImmutableObsData`
8//! - This structs holds, as the name might suggest, an immutable reference to ObsData.
9//! - The data inside this struct can not be changed and is intended for read-only.
10//! - You can turn this ImmutableObsData into a writable `ObsData` struct again, but this will internally clone the data and not affect the `ImmutableObsData` itself.
11use std::{ffi::CString, sync::Arc};
12
13use crate::{
14    impl_obs_drop, run_with_obs,
15    runtime::ObsRuntime,
16    unsafe_send::{Sendable, SmartPointerSendable},
17    utils::{ObsDropGuard, ObsError},
18};
19pub use immutable::ImmutableObsData;
20
21pub mod audio;
22mod immutable;
23mod lib_support;
24pub mod object;
25pub mod output;
26pub mod properties;
27pub mod video;
28pub use lib_support::*;
29mod updater;
30pub use updater::*;
31mod traits;
32pub use traits::*;
33
34#[derive(Debug)]
35pub(super) struct _ObsDataDropGuard {
36    data_ptr: Sendable<*mut libobs::obs_data_t>,
37    runtime: ObsRuntime,
38}
39
40impl_obs_drop!(_ObsDataDropGuard, (data_ptr), move || unsafe {
41    // Safety: This is the drop guard, so the data_ptr must be valid here.
42    libobs::obs_data_release(data_ptr.0)
43});
44
45impl ObsDropGuard for _ObsDataDropGuard {}
46
47/// Contains `obs_data` and its related strings. Note that
48/// this struct prevents string pointers from being freed
49/// by keeping them owned.
50/// `ObsData` is intentionally not `Clone`: duplicating it requires a native round-trip
51/// and can fail. Use [`ObsData::try_clone`] when an independent mutable copy is needed.
52//NOTE: Update: The strings are actually copied by obs itself, we don't need to store them
53#[derive(Debug)]
54pub struct ObsData {
55    pub(crate) runtime: ObsRuntime,
56    ptr: SmartPointerSendable<*mut libobs::obs_data_t>,
57}
58
59impl ObsData {
60    /// Creates a new empty `ObsData` wrapper for the
61    /// libobs `obs_data` data structure.
62    ///
63    /// `ObsData` can then be populated using the set
64    /// functions, which take ownership of the
65    /// `ObsString` types to prevent them from being
66    /// dropped prematurely. This makes it safer than
67    /// using `obs_data` directly from libobs.
68    pub fn new(runtime: ObsRuntime) -> Result<Self, ObsError> {
69        let obs_data = run_with_obs!(runtime, move || unsafe {
70            // Safety: We are in the runtime, so creating new obs_data_t is safe.
71            Sendable(libobs::obs_data_create())
72        })?;
73
74        let drop_guard = Arc::new(_ObsDataDropGuard {
75            data_ptr: obs_data.clone(),
76            runtime: runtime.clone(),
77        });
78        let ptr =
79            SmartPointerSendable::new(obs_data.0, drop_guard.clone(), runtime.native_registry());
80        Ok(ObsData {
81            ptr,
82            runtime: runtime.clone(),
83        })
84    }
85
86    /// Wraps an owned native data pointer returned by libobs.
87    ///
88    /// The caller must only pass pointers for which this wrapper owns one reference.
89    /// Keeping this constructor crate-private makes that ownership contract an internal
90    /// FFI invariant rather than something downstream callers need to reason about.
91    pub(crate) fn from_raw_pointer(
92        data: Sendable<*mut libobs::obs_data_t>,
93        runtime: ObsRuntime,
94    ) -> Self {
95        let drop_guard = Arc::new(_ObsDataDropGuard {
96            data_ptr: data.clone(),
97            runtime: runtime.clone(),
98        });
99        Self {
100            ptr: SmartPointerSendable::new(data.0, drop_guard, runtime.native_registry()),
101            runtime,
102        }
103    }
104
105    pub fn bulk_update(&mut self) -> ObsDataUpdater {
106        ObsDataUpdater::new(self.as_ptr(), self.runtime.clone())
107    }
108
109    pub fn from_json(json: &str, runtime: ObsRuntime) -> Result<Self, ObsError> {
110        let cstr = CString::new(json).map_err(|_| ObsError::JsonParseError)?;
111
112        let raw_ptr = run_with_obs!(runtime, (cstr), move || unsafe {
113            // Safety: We made sure that the cstr pointer is valid during the call.
114            Sendable(libobs::obs_data_create_from_json(cstr.as_ptr()))
115        })?;
116
117        if raw_ptr.0.is_null() {
118            return Err(ObsError::JsonParseError);
119        }
120
121        let drop_guard = Arc::new(_ObsDataDropGuard {
122            data_ptr: raw_ptr.clone(),
123            runtime: runtime.clone(),
124        });
125
126        let ptr =
127            SmartPointerSendable::new(raw_ptr.0, drop_guard.clone(), runtime.native_registry());
128
129        Ok(ObsData {
130            ptr,
131            runtime: runtime.clone(),
132        })
133    }
134
135    /// Converts this `ObsData` into an `ImmutableObsData`.
136    /// Transfers the pointer without cloning.
137    pub fn into_immutable(self) -> ImmutableObsData {
138        ImmutableObsData::from(self)
139    }
140
141    /// Creates an independent mutable copy of this OBS data object.
142    ///
143    /// This is fallible by design; the previous `Clone` implementation performed the
144    /// same JSON round-trip but panicked when either native operation failed.
145    pub fn try_clone(&self) -> Result<Self, ObsError> {
146        let json = self.get_json()?;
147        Self::from_json(&json, self.runtime.clone())
148    }
149}
150
151impl ObsDataPointers for ObsData {
152    fn runtime(&self) -> &ObsRuntime {
153        &self.runtime
154    }
155
156    fn as_ptr(&self) -> SmartPointerSendable<*mut libobs::obs_data_t> {
157        self.ptr.clone()
158    }
159}
160
161impl ObsDataGetters for ObsData {}
162impl ObsDataSetters for ObsData {}