libobs_wrapper\display/
mod.rs

1//! For this display method to work, another preview window has to be created in order to create a swapchain
2//! This is because the main window renderer is already handled by other processes
3
4mod creation_data;
5mod enums;
6mod window_manager;
7
8pub use window_manager::{MiscDisplayTrait, ShowHideTrait, WindowPositionTrait};
9
10pub use creation_data::*;
11pub use enums::*;
12use libobs::obs_video_info;
13
14use crate::utils::ObsError;
15use crate::{impl_obs_drop, run_with_obs, runtime::ObsRuntime, unsafe_send::Sendable};
16use lazy_static::lazy_static;
17use libobs::obs_render_main_texture_src_color_only;
18use std::collections::HashMap;
19use std::mem::MaybeUninit;
20use std::{
21    ffi::c_void,
22    marker::PhantomPinned,
23    sync::{atomic::AtomicUsize, Arc, RwLock},
24};
25
26static ID_COUNTER: AtomicUsize = AtomicUsize::new(1);
27#[derive(Debug, Clone)]
28//TODO: This has to be checked again, I'm unsure with pinning and draw callbacks from OBS
29///
30/// This is a wrapper around the obs_display struct and contains direct memory references.
31/// You should ALWAYS use the context to get to this struct, and as said NEVER store it.
32pub struct ObsDisplayRef {
33    display: Sendable<*mut libobs::obs_display_t>,
34    id: usize,
35
36    _guard: Arc<_ObsDisplayDropGuard>,
37    _pos_remove_guard: Arc<PosRemoveGuard>,
38
39    /// Keep for window, manager is accessed by render thread as well so Arc and RwLock
40    ///
41    /// This is mostly used on windows to handle the size and position of the child window.
42    #[cfg(windows)]
43    #[allow(dead_code)]
44    child_window_handler:
45        Option<Arc<RwLock<window_manager::windows::WindowsPreviewChildWindowHandler>>>,
46
47    /// Stored so the obs context is not dropped while this is alive
48    pub(crate) runtime: ObsRuntime,
49}
50
51lazy_static! {
52    pub(super) static ref DISPLAY_POSITIONS: Arc<RwLock<HashMap<usize, (i32, i32)>>> =
53        Arc::new(RwLock::new(HashMap::new()));
54}
55
56#[derive(Debug)]
57struct PosRemoveGuard {
58    id: usize,
59}
60
61impl Drop for PosRemoveGuard {
62    fn drop(&mut self) {
63        let mut map = DISPLAY_POSITIONS.write().unwrap();
64        map.remove(&self.id);
65    }
66}
67
68unsafe extern "C" fn render_display(data: *mut c_void, width: u32, height: u32) {
69    let id = data as usize;
70    let pos = DISPLAY_POSITIONS
71        .read()
72        .unwrap()
73        .get(&id)
74        .cloned()
75        .unwrap_or((0, 0));
76
77    let mut ovi = MaybeUninit::<obs_video_info>::uninit();
78    libobs::obs_get_video_info(ovi.as_mut_ptr());
79
80    let ovi = unsafe { ovi.assume_init() };
81
82    libobs::gs_viewport_push();
83    libobs::gs_projection_push();
84
85    libobs::gs_ortho(
86        0.0f32,
87        ovi.base_width as f32,
88        0.0f32,
89        ovi.base_height as f32,
90        -100.0f32,
91        100.0f32,
92    );
93    libobs::gs_set_viewport(pos.0, pos.1, width as i32, height as i32);
94    //draw_backdrop(&s.buffers, ovi.base_width as f32, ovi.base_height as f32);
95
96    obs_render_main_texture_src_color_only();
97
98    libobs::gs_projection_pop();
99    libobs::gs_viewport_pop();
100}
101
102pub struct LockedPosition {
103    pub x: i32,
104    pub y: i32,
105    /// This must not be moved in memory as the draw callback is a raw pointer to this struct
106    _fixed_in_heap: PhantomPinned,
107}
108
109#[derive(Clone, Debug)]
110pub struct ObsWindowHandle {
111    pub(crate) window: Sendable<libobs::gs_window>,
112    #[allow(dead_code)]
113    pub(crate) is_wayland: bool,
114}
115
116impl ObsWindowHandle {
117    #[cfg(windows)]
118    pub fn new_from_handle(handle: *mut std::os::raw::c_void) -> Self {
119        Self {
120            window: Sendable(libobs::gs_window { hwnd: handle }),
121            is_wayland: false,
122        }
123    }
124
125    #[cfg(windows)]
126    pub fn get_hwnd(&self) -> windows::Win32::Foundation::HWND {
127        windows::Win32::Foundation::HWND(self.window.0.hwnd)
128    }
129
130    #[cfg(target_os = "linux")]
131    pub fn new_from_wayland(surface: *mut c_void) -> Self {
132        Self {
133            window: Sendable(libobs::gs_window {
134                display: surface,
135                id: 0,
136            }),
137            is_wayland: true,
138        }
139    }
140
141    #[cfg(target_os = "linux")]
142    pub fn new_from_x11(runtime: &ObsRuntime, id: u32) -> Result<Self, ObsError> {
143        let runtime = runtime.clone();
144        let display = run_with_obs!(runtime, (), move || unsafe {
145            Sendable(libobs::obs_get_nix_platform_display())
146        })?;
147
148        Ok(Self {
149            window: Sendable(libobs::gs_window {
150                display: display.0,
151                id,
152            }),
153            is_wayland: false,
154        })
155    }
156}
157
158impl ObsDisplayRef {
159    /// Call initialize to ObsDisplay#create the display
160    pub(crate) fn new(data: ObsDisplayCreationData, runtime: ObsRuntime) -> anyhow::Result<Self> {
161        use std::sync::atomic::Ordering;
162
163        use anyhow::bail;
164        use creation_data::ObsDisplayCreationData;
165
166        use crate::run_with_obs;
167
168        let ObsDisplayCreationData {
169            x,
170            y,
171            background_color,
172            create_child,
173            #[cfg(windows)]
174            height,
175            #[cfg(windows)]
176            width,
177            #[cfg(windows)]
178            window_handle,
179            ..
180        } = data.clone();
181
182        #[cfg(windows)]
183        let mut child_handler = if create_child {
184            Some(
185                window_manager::windows::WindowsPreviewChildWindowHandler::new_child(
186                    window_handle.clone(),
187                    x,
188                    y,
189                    width,
190                    height,
191                )?,
192            )
193        } else {
194            None
195        };
196
197        #[cfg(windows)]
198        let init_data = Sendable(data.build(child_handler.as_ref().map(|e| e.get_window_handle())));
199
200        #[cfg(not(windows))]
201        let init_data = Sendable(data.build(None));
202
203        log::trace!("Creating obs display...");
204        let display = run_with_obs!(runtime, (init_data), move || unsafe {
205            Sendable(libobs::obs_display_create(&init_data.0, background_color))
206        })?;
207
208        if display.0.is_null() {
209            bail!("OBS failed to create display");
210        }
211
212        #[cfg(windows)]
213        if let Some(handler) = &mut child_handler {
214            handler.set_display_handle(display.clone());
215        }
216
217        let initial_pos = if create_child && cfg!(windows) {
218            (0, 0)
219        } else {
220            (x, y)
221        };
222
223        let id = ID_COUNTER.fetch_add(1, Ordering::Relaxed);
224        DISPLAY_POSITIONS
225            .write()
226            .map_err(|e| ObsError::LockError(format!("{:?}", e)))?
227            .insert(id, initial_pos);
228
229        let instance = Self {
230            display: display.clone(),
231            _guard: Arc::new(_ObsDisplayDropGuard {
232                display,
233                runtime: runtime.clone(),
234            }),
235            id,
236            runtime: runtime.clone(),
237            _pos_remove_guard: Arc::new(PosRemoveGuard { id }),
238
239            #[cfg(windows)]
240            child_window_handler: child_handler.map(|e| Arc::new(RwLock::new(e))),
241        };
242
243        log::trace!("Adding draw callback with display {:?}", instance.display);
244
245        let display_ptr = instance.display.clone();
246        run_with_obs!(runtime, (display_ptr), move || unsafe {
247            libobs::obs_display_add_draw_callback(
248                display_ptr,
249                Some(render_display),
250                id as *mut c_void,
251            );
252        })?;
253
254        Ok(instance)
255    }
256
257    pub fn id(&self) -> usize {
258        self.id
259    }
260
261    pub fn update_color_space(&self) -> Result<(), ObsError> {
262        let display_ptr = self.display.clone();
263        run_with_obs!(self.runtime, (display_ptr), move || unsafe {
264            libobs::obs_display_update_color_space(display_ptr)
265        })
266    }
267}
268
269#[derive(Debug)]
270struct _ObsDisplayDropGuard {
271    display: Sendable<*mut libobs::obs_display_t>,
272    pub(crate) runtime: ObsRuntime,
273}
274
275impl_obs_drop!(_ObsDisplayDropGuard, (display), move || unsafe {
276    log::trace!("Removing callback of display {:?}...", display);
277    libobs::obs_display_remove_draw_callback(display, Some(render_display), std::ptr::null_mut());
278
279    libobs::obs_display_destroy(display);
280});