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 libobs::obs_sceneitem_remove(scene_item_ptr);
145 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]);