libobs_wrapper\scenes/
mod.rs

1//! This module is important, as it holds the scene items and scenes themselves.
2//! Scenes are essential, as they hold the sources which are then being rendered in the output.
3//! You'll need to add sources to the scenes if you want to have an output that is not black.
4//! You can also use the `libobs-simple` crate to simplify the creation of ObsSourceRefs.
5
6mod transform_info;
7pub use transform_info::*;
8
9mod scene_drop_guards;
10mod scene_item;
11
12mod filter_traits;
13pub use filter_traits::*;
14
15pub use scene_item::*;
16
17use std::collections::HashMap;
18use std::fmt::Debug;
19use std::sync::{Arc, RwLock};
20
21use libobs::{obs_scene_t, obs_source_t};
22
23use crate::macros::impl_eq_of_ptr;
24use crate::scenes::scene_drop_guards::_SceneDropGuard;
25use crate::sources::{ObsFilterGuardPair, ObsSourceTrait};
26use crate::unsafe_send::SmartPointerSendable;
27use crate::utils::{GeneralTraitHashMap, ObsDropGuard};
28use crate::{
29    impl_signal_manager, run_with_obs,
30    runtime::ObsRuntime,
31    unsafe_send::Sendable,
32    utils::{ObsError, ObsString},
33};
34
35#[derive(Debug)]
36struct _NoOpDropGuard;
37impl ObsDropGuard for _NoOpDropGuard {}
38
39#[derive(Debug, Clone)]
40/// This struct holds every ObsSourceRef that is attached to the scene by using `add_source`.
41pub struct ObsSceneRef {
42    name: ObsString,
43    attached_scene_items:
44        GeneralTraitHashMap<dyn ObsSourceTrait, Vec<Arc<Box<dyn SceneItemTrait + 'static>>>>,
45    attached_filters: Arc<RwLock<Vec<ObsFilterGuardPair>>>,
46    runtime: ObsRuntime,
47    signals: Arc<ObsSceneSignals>,
48    scene: SmartPointerSendable<*mut obs_scene_t>,
49}
50
51impl_eq_of_ptr!(ObsSceneRef);
52
53impl ObsSceneRef {
54    pub(crate) fn new(name: ObsString, runtime: ObsRuntime) -> Result<Self, ObsError> {
55        let scene = run_with_obs!(runtime, (name), move || {
56            let name_ptr = name.as_ptr();
57
58            let scene_ptr = unsafe {
59                // Safety: name_ptr is valid because we have the name variable in scope.
60                libobs::obs_scene_create(name_ptr.0)
61            };
62            if scene_ptr.is_null() {
63                return Err(ObsError::NullPointer(None));
64            }
65
66            let source_ptr = unsafe {
67                // Safety: scene_ptr is valid because we just created it and its not null.
68                libobs::obs_scene_get_source(scene_ptr)
69            };
70
71            if source_ptr.is_null() {
72                unsafe {
73                    // Safety: scene_ptr is valid because we just created it and its not null.
74                    libobs::obs_scene_release(scene_ptr);
75                }
76                return Err(ObsError::NullPointer(None));
77            }
78
79            Ok(Sendable(scene_ptr))
80        })??;
81
82        let drop_guard = Arc::new(_SceneDropGuard::new(scene.clone(), runtime.clone()));
83        let scene = SmartPointerSendable::new(scene.0, drop_guard);
84
85        let signals = Arc::new(ObsSceneSignals::new(&scene, runtime.clone())?);
86        Ok(Self {
87            name,
88            scene,
89            attached_scene_items: Arc::new(RwLock::new(HashMap::new())),
90            attached_filters: Arc::new(RwLock::new(Vec::new())),
91            runtime,
92            signals,
93        })
94    }
95
96    #[deprecated = "Use ObsSceneRef::set_to_channel instead"]
97    pub fn add_and_set(&self, channel: u32) -> Result<(), ObsError> {
98        self.set_to_channel(channel)
99    }
100
101    /// Sets this scene to a given output channel.
102    /// There are 64
103    /// channels that you can assign scenes to, which will draw on top of each
104    /// other in ascending index order.
105    pub fn set_to_channel(&self, channel: u32) -> Result<(), ObsError> {
106        if channel >= libobs::MAX_CHANNELS {
107            return Err(ObsError::InvalidOperation(format!(
108                "Channel {} is out of bounds (max {})",
109                channel,
110                libobs::MAX_CHANNELS - 1
111            )));
112        }
113
114        let scene_source_ptr = self.get_scene_source_ptr()?;
115        run_with_obs!(self.runtime, (scene_source_ptr), move || unsafe {
116            // Safety: We are in the runtime and the struct hasn't been dropped yet, therefore the scene source must be valid.
117            // Also we are removing that pointer from the output source if this scene is dropped in the Drop guard
118            libobs::obs_set_output_source(channel, scene_source_ptr.0);
119        })
120    }
121
122    /// Removes a scene from a given output channel, for more info about channels see `set_to_channel`.
123    pub fn remove_from_channel(&self, channel: u32) -> Result<(), ObsError> {
124        if channel >= libobs::MAX_CHANNELS {
125            return Err(ObsError::InvalidOperation(format!(
126                "Channel {} is out of bounds (max {})",
127                channel,
128                libobs::MAX_CHANNELS - 1
129            )));
130        }
131
132        run_with_obs!(self.runtime, (), move || unsafe {
133            // Safety: We are in the runtime
134            libobs::obs_set_output_source(channel, std::ptr::null_mut());
135        })
136    }
137
138    /// Gets the underlying source pointer of this scene, which is used internally when setting it to a channel.
139    pub fn get_scene_source_ptr(&self) -> Result<Sendable<*mut obs_source_t>, ObsError> {
140        let scene_ptr = self.scene.clone();
141        run_with_obs!(self.runtime, (scene_ptr), move || {
142            unsafe {
143                // Safety: We are in the runtime and the scene ptr must be valid because we are using a smart pointer
144                Sendable(libobs::obs_scene_get_source(scene_ptr.get_ptr()))
145            }
146        })
147    }
148
149    pub fn as_ptr(&self) -> SmartPointerSendable<*mut obs_scene_t> {
150        self.scene.clone()
151    }
152
153    pub fn name(&self) -> ObsString {
154        self.name.clone()
155    }
156
157    pub fn signals(&self) -> Arc<ObsSceneSignals> {
158        self.signals.clone()
159    }
160}
161
162impl_signal_manager!(|scene_ptr: SmartPointerSendable<*mut obs_scene_t>| unsafe {
163    // Safety: This is a smart pointer, so it is fine
164    let source_ptr = libobs::obs_scene_get_source(scene_ptr.get_ptr());
165
166    libobs::obs_source_get_signal_handler(source_ptr)
167}, ObsSceneSignals for *mut obs_scene_t, [
168    "item_add": {
169        struct ItemAddSignal {
170            POINTERS {
171                item: *mut libobs::obs_sceneitem_t,
172            }
173        }
174    },
175    "item_remove": {
176        struct ItemRemoveSignal {
177            POINTERS {
178                item: *mut libobs::obs_sceneitem_t,
179            }
180        }
181    },
182    "reorder": {},
183    "refresh": {},
184    "item_visible": {
185        struct ItemVisibleSignal {
186            visible: bool;
187            POINTERS {
188                item: *mut libobs::obs_sceneitem_t,
189            }
190        }
191    },
192    "item_locked": {
193        struct ItemLockedSignal {
194            locked: bool;
195            POINTERS {
196                item: *mut libobs::obs_sceneitem_t,
197            }
198        }
199    },
200    "item_select": {
201        struct ItemSelectSignal {
202            POINTERS {
203                item: *mut libobs::obs_sceneitem_t,
204            }
205        }
206    },
207    "item_deselect": {
208        struct ItemDeselectSignal {
209            POINTERS {
210                item: *mut libobs::obs_sceneitem_t,
211            }
212        }
213    },
214    "item_transform": {
215        struct ItemTransformSignal {
216            POINTERS {
217                item: *mut libobs::obs_sceneitem_t,
218            }
219        }
220    }
221]);