libobs_wrapper\scenes/
mod.rs

1use std::sync::Arc;
2
3use getters0::Getters;
4use libobs::{obs_scene_t, obs_set_output_source, obs_source_t};
5
6use crate::{
7    impl_obs_drop, impl_signal_manager, run_with_obs, runtime::ObsRuntime, sources::ObsSourceRef, unsafe_send::Sendable, utils::{async_sync::RwLock, ObsError, ObsString, SourceInfo}
8};
9
10#[derive(Debug)]
11struct _SceneDropGuard {
12    scene: Sendable<*mut obs_scene_t>,
13    runtime: ObsRuntime,
14}
15
16impl_obs_drop!(_SceneDropGuard, (scene), move || unsafe {
17    libobs::obs_scene_release(scene);
18});
19
20#[derive(Debug, Clone, Getters)]
21#[skip_new]
22pub struct ObsSceneRef {
23    #[skip_getter]
24    scene: Arc<Sendable<*mut obs_scene_t>>,
25    name: ObsString,
26    #[get_mut]
27    pub(crate) sources: Arc<RwLock<Vec<ObsSourceRef>>>,
28    #[skip_getter]
29    pub(crate) active_scene: Arc<RwLock<Option<Sendable<*mut obs_scene_t>>>>,
30
31    #[skip_getter]
32    _guard: Arc<_SceneDropGuard>,
33
34    #[skip_getter]
35    runtime: ObsRuntime,
36
37    pub(crate) signals: Arc<ObsSceneSignals>,
38}
39
40impl ObsSceneRef {
41    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
42    pub(crate) async fn new(
43        name: ObsString,
44        active_scene: Arc<RwLock<Option<Sendable<*mut obs_scene_t>>>>,
45        runtime: ObsRuntime,
46    ) -> Result<Self, ObsError> {
47        let name_ptr = name.as_ptr();
48        let scene = run_with_obs!(runtime, (name_ptr), move || unsafe {
49            Sendable(libobs::obs_scene_create(name_ptr))
50        }).await?;
51
52        let signals = Arc::new(ObsSceneSignals::new(&scene, runtime.clone()).await?);
53        Ok(Self {
54            name,
55            scene: Arc::new(scene.clone()),
56            sources: Arc::new(RwLock::new(vec![])),
57            active_scene: active_scene.clone(),
58            _guard: Arc::new(_SceneDropGuard {
59                scene,
60                runtime: runtime.clone(),
61            }),
62            runtime,
63            signals,
64        })
65    }
66
67    #[deprecated = "Use ObsSceneRef::set_to_channel instead"]
68    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
69    pub async fn add_and_set(&self, channel: u32) -> Result<(), ObsError> {
70        self.set_to_channel(channel).await
71    }
72
73    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
74    pub async fn set_to_channel(&self, channel: u32) -> Result<(), ObsError> {
75        let mut s = self.active_scene.write().await;
76        *s = Some(self.as_ptr());
77
78        let scene_source_ptr = self.get_scene_source_ptr().await?;
79        run_with_obs!(self.runtime, (scene_source_ptr), move || unsafe {
80            obs_set_output_source(channel, scene_source_ptr);
81        }).await
82    }
83
84    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
85    pub async fn get_scene_source_ptr(&self) -> Result<Sendable<*mut obs_source_t>, ObsError> {
86        let scene_ptr = self.scene.clone();
87        run_with_obs!(self.runtime, (scene_ptr), move || unsafe {
88            Sendable(libobs::obs_scene_get_source(scene_ptr))
89        }).await
90    }
91
92    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
93    pub async fn add_source(&mut self, info: SourceInfo) -> Result<ObsSourceRef, ObsError> {
94        let mut source = ObsSourceRef::new(
95            info.id,
96            info.name,
97            info.settings,
98            info.hotkey_data,
99            self.runtime.clone(),
100        )
101        .await?;
102
103        let scene_ptr = self.scene.clone();
104        let source_ptr = source.source.clone();
105
106        let ptr = run_with_obs!(self.runtime, (scene_ptr, source_ptr), move || unsafe {
107            Sendable(libobs::obs_scene_add(scene_ptr, source_ptr))
108        }).await?;
109
110        if ptr.0.is_null() {
111            return Err(ObsError::NullPointer);
112        }
113
114        source.scene_item = Some(ptr.clone());
115        self.sources.write().await.push(source.clone());
116        Ok(source)
117    }
118
119    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
120    pub async fn get_source_by_index(&self, index: usize) -> Option<ObsSourceRef> {
121        self.sources.read().await.get(index).map(|x| x.clone())
122    }
123
124    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
125    pub async fn get_source_mut(&self, name: &str) -> Option<ObsSourceRef> {
126        self.sources
127            .read()
128            .await
129            .iter()
130            .find(|x| x.name() == name)
131            .map(|x| x.clone())
132    }
133
134    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
135    pub async fn remove_source(&mut self, source: &ObsSourceRef) -> Result<(), ObsError> {
136        let scene_item_ptr = source.scene_item.clone();
137        if scene_item_ptr.is_none() {
138            return Err(ObsError::SourceNotFound);
139        }
140
141        let scene_item_ptr = scene_item_ptr.unwrap();
142        run_with_obs!(self.runtime, (scene_item_ptr), move || unsafe {
143            // Remove the scene item
144            libobs::obs_sceneitem_remove(scene_item_ptr);
145            // Release the scene item reference
146            libobs::obs_sceneitem_release(scene_item_ptr);
147        }).await?;
148
149        Ok(())
150    }
151
152    pub fn as_ptr(&self) -> Sendable<*mut obs_scene_t> {
153        Sendable(self.scene.0)
154    }
155}
156
157impl_signal_manager!(|scene_ptr| {
158    let source_ptr = libobs::obs_scene_get_source(scene_ptr);
159
160    libobs::obs_source_get_signal_handler(source_ptr)
161}, ObsSceneSignals for ObsSceneRef<*mut libobs::obs_scene_t>, [
162    "item_add": {
163        struct ItemAddSignal {
164            POINTERS {
165                item: *mut libobs::obs_sceneitem_t,
166            }
167        }
168    },
169    "item_remove": {
170        struct ItemRemoveSignal {
171            POINTERS {
172                item: *mut libobs::obs_sceneitem_t,
173            }
174        }
175    },
176    "reorder": {},
177    "refresh": {},
178    "item_visible": {
179        struct ItemVisibleSignal {
180            visible: bool;
181            POINTERS {
182                item: *mut libobs::obs_sceneitem_t,
183            }
184        }
185    },
186    "item_locked": {
187        struct ItemLockedSignal {
188            locked: bool;
189            POINTERS {
190                item: *mut libobs::obs_sceneitem_t,
191            }
192        }
193    },
194    "item_select": {
195        struct ItemSelectSignal {
196            POINTERS {
197                item: *mut libobs::obs_sceneitem_t,
198            }
199        }
200    },
201    "item_deselect": {
202        struct ItemDeselectSignal {
203            POINTERS {
204                item: *mut libobs::obs_sceneitem_t,
205            }
206        }
207    },
208    "item_transform": {
209        struct ItemTransformSignal {
210            POINTERS {
211                item: *mut libobs::obs_sceneitem_t,
212            }
213        }
214    }
215]);