1mod 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)]
32pub struct ObsDisplayRef {
37 display: Sendable<*mut libobs::obs_display_t>,
38 id: usize,
39
40 _guard: Arc<RwLock<_DisplayDropGuard>>,
42
43 manager: Arc<RwLock<DisplayWindowManager>>,
45 _fixed_in_heap: PhantomPinned,
47
48 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 obs_render_main_texture();
75
76 gs_projection_pop();
77 gs_viewport_pop();
78}
79
80impl ObsDisplayRef {
81 #[cfg(target_family = "windows")]
82 #[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}