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 creation_data::*;
9pub use enums::*;
10pub use window_manager::*;
11
12use std::{
13    ffi::c_void,
14    marker::PhantomPinned,
15    sync::{atomic::AtomicUsize, Arc},
16};
17
18use libobs::{
19    gs_ortho, gs_projection_pop, gs_projection_push, gs_set_viewport, gs_viewport_pop,
20    gs_viewport_push, obs_get_video_info, obs_render_main_texture, obs_video_info,
21};
22
23use crate::{
24    run_with_obs,
25    runtime::ObsRuntime,
26    unsafe_send::Sendable,
27    utils::{async_sync::RwLock, ObsError},
28};
29
30static ID_COUNTER: AtomicUsize = AtomicUsize::new(1);
31#[derive(Debug, Clone)]
32//TODO: This has to be checked again, I'm unsure with pinning and draw callbacks from OBS
33/// # NEVER STORE THIS REF DIRECTLY!!
34/// This is a wrapper around the obs_display struct and contains direct memory references.
35/// You should ALWAYS use the context to get to this struct, and as said NEVER store it.
36pub struct ObsDisplayRef {
37    display: Sendable<*mut libobs::obs_display_t>,
38    id: usize,
39
40    // The callbacks and obs display first
41    _guard: Arc<RwLock<_DisplayDropGuard>>,
42
43    // Keep for window, manager is accessed by render thread as well so Arc and RwLock
44    manager: Arc<RwLock<DisplayWindowManager>>,
45    /// This must not be moved in memory as the draw callback is a raw pointer to this struct
46    _fixed_in_heap: PhantomPinned,
47
48    /// Stored so the obs context is not dropped while this is alive
49    pub(crate) runtime: ObsRuntime,
50}
51
52unsafe extern "C" fn render_display(data: *mut c_void, _cx: u32, _cy: u32) {
53    let s = &*(data as *mut ObsDisplayRef);
54
55    let (width, height) = s.get_size_blocking();
56
57    let mut ovi: obs_video_info = std::mem::zeroed();
58    obs_get_video_info(&mut ovi);
59
60    gs_viewport_push();
61    gs_projection_push();
62
63    gs_ortho(
64        0.0f32,
65        ovi.base_width as f32,
66        0.0f32,
67        ovi.base_height as f32,
68        -100.0f32,
69        100.0f32,
70    );
71    gs_set_viewport(0, 0, width as i32, height as i32);
72    //draw_backdrop(&s.buffers, ovi.base_width as f32, ovi.base_height as f32);
73
74    obs_render_main_texture();
75
76    gs_projection_pop();
77    gs_viewport_pop();
78}
79
80impl ObsDisplayRef {
81    #[cfg(target_family = "windows")]
82    /// Call initialize to ObsDisplay#create the display
83    /// NOTE: This must be pinned to prevent the draw callbacks from having a invalid pointer. DO NOT UNPIN
84    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
85    pub(crate) async fn new(
86        data: creation_data::ObsDisplayCreationData,
87        runtime: ObsRuntime,
88    ) -> anyhow::Result<std::pin::Pin<Box<Self>>> {
89        use std::sync::atomic::Ordering;
90
91        use anyhow::bail;
92        use creation_data::ObsDisplayCreationData;
93        use libobs::gs_window;
94        use window_manager::DisplayWindowManager;
95
96        use crate::run_with_obs;
97
98        let ObsDisplayCreationData {
99            x,
100            y,
101            height,
102            width,
103            parent_window,
104            background_color,
105            ..
106        } = data.clone();
107
108        let mut manager =
109            DisplayWindowManager::new(parent_window.clone(), x as i32, y as i32, width, height)?;
110
111        let child_handle = Sendable(manager.get_child_handle());
112        let init_data = Sendable(data.build(gs_window {
113            hwnd: child_handle.0 .0,
114        }));
115
116        log::trace!("Creating obs display...");
117        let display = run_with_obs!(runtime, (init_data), move || unsafe {
118            Sendable(libobs::obs_display_create(&init_data, background_color))
119        }).await?;
120
121        if display.0.is_null() {
122            bail!("OBS failed to create display");
123        }
124
125        manager.obs_display = Some(display.clone());
126        let mut instance = Box::pin(Self {
127            display: display.clone(),
128            manager: Arc::new(RwLock::new(manager)),
129            id: ID_COUNTER.fetch_add(1, Ordering::Relaxed),
130            _guard: Arc::new(RwLock::new(_DisplayDropGuard {
131                display,
132                self_ptr: None,
133                runtime: runtime.clone(),
134            })),
135            _fixed_in_heap: PhantomPinned,
136            runtime: runtime.clone(),
137        });
138
139        let instance_ptr =
140            Sendable(unsafe { instance.as_mut().get_unchecked_mut() as *mut _ as *mut c_void });
141
142        instance._guard.write().await.self_ptr = Some(instance_ptr.clone());
143
144        let pos = instance.get_pos().await;
145        log::trace!(
146            "Adding draw callback with display {:?} and draw callback params at {:?} (pos is {:?})...",
147            instance.display,
148            instance_ptr,
149            pos
150        );
151        let display_ptr = instance.display.clone();
152        run_with_obs!(runtime, (display_ptr, instance_ptr), move || unsafe {
153            libobs::obs_display_add_draw_callback(display_ptr, Some(render_display), instance_ptr);
154        }).await?;
155
156        Ok(instance)
157    }
158
159    pub fn id(&self) -> usize {
160        self.id
161    }
162}
163
164#[derive(Debug)]
165struct _DisplayDropGuard {
166    display: Sendable<*mut libobs::obs_display_t>,
167    self_ptr: Option<Sendable<*mut c_void>>,
168    runtime: ObsRuntime,
169}
170
171impl _DisplayDropGuard {
172    #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
173    pub async fn inner_drop(
174        r: ObsRuntime,
175        display: Sendable<*mut libobs::obs_display_t>,
176        self_ptr: Option<Sendable<*mut c_void>>,
177    ) -> Result<(), ObsError> {
178        run_with_obs!(r, (display), move || unsafe {
179            if let Some(ptr) = &self_ptr {
180                log::trace!("Destroying display with callback at {:?}...", ptr.0);
181                libobs::obs_display_remove_draw_callback(display, Some(render_display), ptr.0);
182            }
183
184            libobs::obs_display_destroy(display);
185        }).await
186    }
187}
188
189impl Drop for _DisplayDropGuard {
190    fn drop(&mut self) {
191        let display = self.display.clone();
192        let self_ptr = self.self_ptr.clone();
193        let r = self.runtime.clone();
194        #[cfg(not(feature = "blocking"))]
195        let r = futures::executor::block_on(async {
196            _DisplayDropGuard::inner_drop(r, display, self_ptr).await
197        });
198        #[cfg(feature = "blocking")]
199        let r = _DisplayDropGuard::inner_drop(r, display, self_ptr);
200
201        if std::thread::panicking() {
202            return;
203        }
204
205        r.unwrap();
206    }
207}