1mod transform_info;
19pub use transform_info::*;
20
21mod group;
22mod scene_drop_guards;
23mod scene_item;
24
25mod filter_traits;
26pub use filter_traits::*;
27
28pub use group::*;
29pub use scene_item::*;
30
31use std::collections::HashMap;
32use std::fmt::Debug;
33use std::sync::{Arc, RwLock};
34
35use libobs::{obs_scene_t, obs_source_t};
36
37use crate::macros::impl_eq_of_ptr;
38use crate::scenes::scene_drop_guards::_SceneDropGuard;
39use crate::sources::{ObsFilterGuardPair, ObsSourceTrait};
40use crate::unsafe_send::{NativeObjectId, SmartPointerSendable};
41use crate::utils::ObsDropGuard;
42use crate::{
43 impl_signal_manager, run_with_obs,
44 runtime::ObsRuntime,
45 unsafe_send::Sendable,
46 utils::{ObsError, ObsString},
47};
48
49#[derive(Debug)]
50struct _NoOpDropGuard;
51impl ObsDropGuard for _NoOpDropGuard {}
52
53pub(super) type SceneItemsBySource = Arc<
54 RwLock<
55 HashMap<
56 NativeObjectId,
57 (
58 Arc<dyn ObsSourceTrait>,
59 Vec<Arc<dyn SceneItemTrait + 'static>>,
60 ),
61 >,
62 >,
63>;
64
65#[derive(Debug, Clone)]
66pub struct ObsSceneRef {
68 name: ObsString,
69 attached_scene_items: SceneItemsBySource,
70 attached_groups: group::SceneGroups,
71 attached_filters: Arc<RwLock<Vec<ObsFilterGuardPair>>>,
72 runtime: ObsRuntime,
73 signals: Arc<ObsSceneSignals>,
74 scene: SmartPointerSendable<*mut obs_scene_t>,
75}
76
77impl_eq_of_ptr!(ObsSceneRef);
78
79impl ObsSceneRef {
80 pub(crate) fn new(name: ObsString, runtime: ObsRuntime) -> Result<Self, ObsError> {
81 let scene = run_with_obs!(runtime, (name), move || {
82 let name_ptr = name.as_ptr();
83
84 let scene_ptr = unsafe {
85 libobs::obs_scene_create(name_ptr.0)
87 };
88 if scene_ptr.is_null() {
89 return Err(ObsError::NullPointer(None));
90 }
91
92 let source_ptr = unsafe {
93 libobs::obs_scene_get_source(scene_ptr)
95 };
96
97 if source_ptr.is_null() {
98 unsafe {
99 libobs::obs_scene_release(scene_ptr);
101 }
102 return Err(ObsError::NullPointer(None));
103 }
104
105 Ok(Sendable(scene_ptr))
106 })??;
107
108 let drop_guard = Arc::new(_SceneDropGuard::new(scene.clone(), runtime.clone()));
109 let scene = SmartPointerSendable::new(scene.0, drop_guard, runtime.native_registry());
110
111 let signals = Arc::new(ObsSceneSignals::new(&scene, runtime.clone())?);
112 Ok(Self {
113 name,
114 scene,
115 attached_scene_items: Arc::new(RwLock::new(HashMap::new())),
116 attached_groups: Arc::new(RwLock::new(Vec::new())),
117 attached_filters: Arc::new(RwLock::new(Vec::new())),
118 runtime,
119 signals,
120 })
121 }
122
123 #[deprecated = "Use ObsSceneRef::set_to_channel instead"]
124 pub fn add_and_set(&self, channel: u32) -> Result<(), ObsError> {
125 self.set_to_channel(channel)
126 }
127
128 pub fn set_to_channel(&self, channel: u32) -> Result<(), ObsError> {
133 if channel >= libobs::MAX_CHANNELS {
134 return Err(ObsError::InvalidOperation(format!(
135 "Channel {} is out of bounds (max {})",
136 channel,
137 libobs::MAX_CHANNELS - 1
138 )));
139 }
140
141 let scene_source_ptr = self.get_scene_source_ptr()?;
142 run_with_obs!(self.runtime, (scene_source_ptr), move || unsafe {
143 libobs::obs_set_output_source(channel, scene_source_ptr.0);
146 })
147 }
148
149 pub fn remove_from_channel(&self, channel: u32) -> Result<(), ObsError> {
151 if channel >= libobs::MAX_CHANNELS {
152 return Err(ObsError::InvalidOperation(format!(
153 "Channel {} is out of bounds (max {})",
154 channel,
155 libobs::MAX_CHANNELS - 1
156 )));
157 }
158
159 run_with_obs!(self.runtime, (), move || unsafe {
160 libobs::obs_set_output_source(channel, std::ptr::null_mut());
162 })
163 }
164
165 pub(crate) fn get_scene_source_ptr(&self) -> Result<Sendable<*mut obs_source_t>, ObsError> {
167 let scene_ptr = self.scene.clone();
168 run_with_obs!(self.runtime, (scene_ptr), move || {
169 unsafe {
170 Sendable(libobs::obs_scene_get_source(scene_ptr.get_ptr()))
172 }
173 })
174 }
175
176 pub(crate) fn as_ptr(&self) -> SmartPointerSendable<*mut obs_scene_t> {
177 self.scene.clone()
178 }
179
180 pub fn object_id(&self) -> NativeObjectId {
181 self.scene.native_id()
182 }
183
184 pub fn name(&self) -> ObsString {
185 self.name.clone()
186 }
187
188 pub fn runtime(&self) -> &ObsRuntime {
189 &self.runtime
190 }
191
192 pub fn signals(&self) -> Arc<ObsSceneSignals> {
193 self.signals.clone()
194 }
195
196 pub fn add<T>(&mut self, source: T) -> Result<ObsSceneItemRef<T>, ObsError>
198 where
199 T: ObsSourceTrait + Clone + 'static,
200 {
201 SceneItemExtSceneTrait::add_source(self, source)
202 }
203
204 pub fn add_new_source(
206 &mut self,
207 info: crate::utils::SourceInfo,
208 ) -> Result<ObsSceneItemRef<crate::sources::ObsSourceRef>, ObsError> {
209 SceneItemExtSceneTrait::add_and_create_source(self, info)
210 }
211
212 pub fn remove_item<T: SceneItemTrait + ?Sized>(&mut self, item: &T) -> Result<(), ObsError> {
214 SceneItemExtSceneTrait::remove_item(self, item)
215 }
216
217 pub fn items_for_source<T>(&self, source: &T) -> Result<Vec<Arc<dyn SceneItemTrait>>, ObsError>
219 where
220 T: ObsSourceTrait + Clone,
221 {
222 SceneItemExtSceneTrait::items_for_source(self, source)
223 }
224
225 pub fn clear(&mut self) -> Result<(), ObsError> {
227 SceneItemExtSceneTrait::remove_all_sources(self)?;
228 self.clear_groups()
229 }
230}
231
232impl_signal_manager!(|scene_ptr: SmartPointerSendable<*mut obs_scene_t>| unsafe {
233 let source_ptr = libobs::obs_scene_get_source(scene_ptr.get_ptr());
235
236 libobs::obs_source_get_signal_handler(source_ptr)
237}, ObsSceneSignals for *mut obs_scene_t, [
238 "item_add": {
239 struct ItemAddSignal {
240 POINTERS {
241 item: *mut libobs::obs_sceneitem_t,
242 }
243 }
244 },
245 "item_remove": {
246 struct ItemRemoveSignal {
247 POINTERS {
248 item: *mut libobs::obs_sceneitem_t,
249 }
250 }
251 },
252 "reorder": {},
253 "refresh": {},
254 "item_visible": {
255 struct ItemVisibleSignal {
256 visible: bool;
257 POINTERS {
258 item: *mut libobs::obs_sceneitem_t,
259 }
260 }
261 },
262 "item_locked": {
263 struct ItemLockedSignal {
264 locked: bool;
265 POINTERS {
266 item: *mut libobs::obs_sceneitem_t,
267 }
268 }
269 },
270 "item_select": {
271 struct ItemSelectSignal {
272 POINTERS {
273 item: *mut libobs::obs_sceneitem_t,
274 }
275 }
276 },
277 "item_deselect": {
278 struct ItemDeselectSignal {
279 POINTERS {
280 item: *mut libobs::obs_sceneitem_t,
281 }
282 }
283 },
284 "item_transform": {
285 struct ItemTransformSignal {
286 POINTERS {
287 item: *mut libobs::obs_sceneitem_t,
288 }
289 }
290 }
291]);