1mod 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::unsafe_send::SmartPointerSendable;
15use crate::utils::{ObsDropGuard, ObsError};
16use crate::{impl_obs_drop, run_with_obs, runtime::ObsRuntime, unsafe_send::Sendable};
17use std::mem::MaybeUninit;
18use std::{
19 ffi::c_void,
20 marker::PhantomPinned,
21 sync::{atomic::AtomicUsize, Arc, RwLock},
22};
23
24static ID_COUNTER: AtomicUsize = AtomicUsize::new(1);
25#[derive(Debug, Clone)]
26pub struct ObsDisplayRef {
30 id: usize,
31 render_state: Arc<DisplayRenderState>,
32
33 #[cfg(windows)]
37 #[allow(dead_code)]
38 child_window_handler:
39 Option<Arc<RwLock<window_manager::windows::WindowsPreviewChildWindowHandler>>>,
40
41 runtime: ObsRuntime,
43 display: SmartPointerSendable<*mut libobs::obs_display_t>,
44}
45
46#[derive(Debug)]
47pub(super) struct DisplayRenderState {
48 pub(super) position: RwLock<(i32, i32)>,
49}
50
51#[allow(unknown_lints)]
52#[allow(ensure_obs_call_in_runtime)]
53unsafe extern "C" fn render_display(data: *mut c_void, width: u32, height: u32) {
56 let Some(state) = (data as *const DisplayRenderState).as_ref() else {
57 log::error!("Display render callback received null userdata");
58 return;
59 };
60 let pos = *state
61 .position
62 .read()
63 .unwrap_or_else(|poisoned| poisoned.into_inner());
64
65 let mut ovi = MaybeUninit::<obs_video_info>::uninit();
66 let was_ok = libobs::obs_get_video_info(ovi.as_mut_ptr());
67 if !was_ok {
68 log::error!("Failed to get video info in display render callback");
69 return;
70 }
71
72 let ovi = unsafe {
73 ovi.assume_init()
75 };
76
77 libobs::gs_viewport_push();
78 libobs::gs_projection_push();
79
80 libobs::gs_ortho(
81 0.0f32,
82 ovi.base_width as f32,
83 0.0f32,
84 ovi.base_height as f32,
85 -100.0f32,
86 100.0f32,
87 );
88 libobs::gs_set_viewport(pos.0, pos.1, width as i32, height as i32);
89 libobs::obs_render_main_texture_src_color_only();
92
93 libobs::gs_projection_pop();
94 libobs::gs_viewport_pop();
95}
96
97pub struct LockedPosition {
98 pub x: i32,
99 pub y: i32,
100 _fixed_in_heap: PhantomPinned,
102}
103
104#[derive(Clone, Debug)]
105pub struct ObsWindowHandle {
106 pub(crate) window: Sendable<libobs::gs_window>,
107 #[allow(dead_code)]
108 pub(crate) is_wayland: bool,
109}
110
111impl ObsWindowHandle {
112 #[cfg(windows)]
118 pub unsafe 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")]
136 pub unsafe fn new_from_wayland(surface: *mut c_void) -> Self {
137 Self {
138 window: Sendable(libobs::gs_window {
139 display: surface,
140 id: 0,
141 }),
142 is_wayland: true,
143 }
144 }
145
146 #[cfg(target_os = "linux")]
147 pub fn new_from_x11(runtime: &ObsRuntime, id: u32) -> Result<Self, ObsError> {
148 let runtime = runtime.clone();
149 let display = run_with_obs!(runtime, (), move || unsafe {
150 Sendable(libobs::obs_get_nix_platform_display())
152 })?;
153
154 Ok(Self {
155 window: Sendable(libobs::gs_window {
156 display: display.0,
157 id,
158 }),
159 is_wayland: false,
160 })
161 }
162}
163
164impl ObsDisplayRef {
165 pub(crate) fn new(data: ObsDisplayCreationData, runtime: ObsRuntime) -> Result<Self, ObsError> {
167 use std::sync::atomic::Ordering;
168
169 use creation_data::ObsDisplayCreationData;
170
171 use crate::run_with_obs;
172
173 let ObsDisplayCreationData {
174 x,
175 y,
176 background_color,
177 create_child,
178 #[cfg(windows)]
179 height,
180 #[cfg(windows)]
181 width,
182 #[cfg(windows)]
183 window_handle,
184 ..
185 } = data.clone();
186
187 #[cfg(windows)]
188 let mut child_handler = if create_child {
189 Some(
190 window_manager::windows::WindowsPreviewChildWindowHandler::new_child(
191 window_handle.clone(),
192 x,
193 y,
194 width,
195 height,
196 )?,
197 )
198 } else {
199 None
200 };
201
202 #[cfg(windows)]
203 let init_data = Sendable(data.build(child_handler.as_ref().map(|e| e.get_window_handle())));
204
205 #[cfg(not(windows))]
206 let init_data = Sendable(data.build(None));
207
208 log::trace!("Creating obs display...");
209 let display = run_with_obs!(runtime, (init_data), move || {
210 let display_ptr = unsafe {
211 libobs::obs_display_create(&init_data.0 .0, background_color)
213 };
214
215 if display_ptr.is_null() {
216 Err(ObsError::NullPointer(None))
217 } else {
218 Ok(Sendable(display_ptr))
219 }
220 })??;
221
222 let initial_pos = if create_child && cfg!(windows) {
223 (0, 0)
224 } else {
225 (x, y)
226 };
227 let render_state = Arc::new(DisplayRenderState {
228 position: RwLock::new(initial_pos),
229 });
230
231 let display = SmartPointerSendable::new(
232 display.0,
233 Arc::new(_ObsDisplayDropGuard {
234 display,
235 render_state: render_state.clone(),
236 runtime: runtime.clone(),
237 }),
238 runtime.native_registry(),
239 );
240
241 #[cfg(windows)]
242 if let Some(handler) = &mut child_handler {
243 handler.set_display_handle(display.clone());
244 }
245
246 let id = ID_COUNTER.fetch_add(1, Ordering::Relaxed);
247 let instance = Self {
248 display: display.clone(),
249 id,
250 render_state: render_state.clone(),
251 runtime: runtime.clone(),
252
253 #[cfg(windows)]
254 child_window_handler: child_handler.map(|e| Arc::new(RwLock::new(e))),
255 };
256
257 log::trace!("Adding draw callback with display {:?}", instance.display);
258
259 let display_ptr = instance.as_ptr();
260 run_with_obs!(runtime, (display_ptr), move || {
261 unsafe {
262 libobs::obs_display_add_draw_callback(
264 display_ptr.get_ptr(),
265 Some(render_display),
266 Arc::as_ptr(&render_state) as *mut c_void,
267 );
268 }
269 })?;
270
271 Ok(instance)
272 }
273
274 pub fn id(&self) -> usize {
275 self.id
276 }
277
278 pub fn update_color_space(&self) -> Result<(), ObsError> {
279 let display_ptr = self.as_ptr();
280 run_with_obs!(self.runtime, (display_ptr), move || {
281 unsafe {
282 libobs::obs_display_update_color_space(display_ptr.get_ptr())
284 }
285 })
286 }
287
288 pub(crate) fn as_ptr(&self) -> SmartPointerSendable<*mut libobs::obs_display_t> {
289 self.display.clone()
290 }
291}
292
293#[derive(Debug)]
294struct _ObsDisplayDropGuard {
295 display: Sendable<*mut libobs::obs_display_t>,
296 render_state: Arc<DisplayRenderState>,
297 runtime: ObsRuntime,
298}
299
300impl ObsDropGuard for _ObsDisplayDropGuard {}
301
302impl_obs_drop!(
303 _ObsDisplayDropGuard,
304 (display, render_state),
305 move || unsafe {
306 log::trace!("Removing callback of display {:?}...", display);
309 libobs::obs_display_remove_draw_callback(
310 display.0,
311 Some(render_display),
312 Arc::as_ptr(&render_state) as *mut c_void,
313 );
314 libobs::obs_display_destroy(display.0);
315 }
316);