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 lazy_static::lazy_static;
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)]
28pub struct ObsDisplayRef {
35 id: usize,
36
37 _pos_remove_guard: Arc<PosRemoveGuard>,
38
39 #[cfg(windows)]
43 #[allow(dead_code)]
44 child_window_handler:
45 Option<Arc<RwLock<window_manager::windows::WindowsPreviewChildWindowHandler>>>,
46
47 runtime: ObsRuntime,
49 display: SmartPointerSendable<*mut libobs::obs_display_t>,
50}
51
52lazy_static! {
53 pub(super) static ref DISPLAY_POSITIONS: Arc<RwLock<HashMap<usize, (i32, i32)>>> =
54 Arc::new(RwLock::new(HashMap::new()));
55}
56
57#[derive(Debug)]
58struct PosRemoveGuard {
59 id: usize,
60}
61
62impl Drop for PosRemoveGuard {
63 fn drop(&mut self) {
64 let mut map = DISPLAY_POSITIONS.write().unwrap();
65 map.remove(&self.id);
66 }
67}
68
69#[allow(unknown_lints)]
70#[allow(ensure_obs_call_in_runtime)]
71unsafe extern "C" fn render_display(data: *mut c_void, width: u32, height: u32) {
74 let id = data as usize;
75 let pos = DISPLAY_POSITIONS
76 .read()
77 .unwrap()
78 .get(&id)
79 .cloned()
80 .unwrap_or((0, 0));
81
82 let mut ovi = MaybeUninit::<obs_video_info>::uninit();
83 let was_ok = libobs::obs_get_video_info(ovi.as_mut_ptr());
84 if !was_ok {
85 log::error!("Failed to get video info in display render callback");
86 return;
87 }
88
89 let ovi = unsafe {
90 ovi.assume_init()
92 };
93
94 libobs::gs_viewport_push();
95 libobs::gs_projection_push();
96
97 libobs::gs_ortho(
98 0.0f32,
99 ovi.base_width as f32,
100 0.0f32,
101 ovi.base_height as f32,
102 -100.0f32,
103 100.0f32,
104 );
105 libobs::gs_set_viewport(pos.0, pos.1, width as i32, height as i32);
106 libobs::obs_render_main_texture_src_color_only();
109
110 libobs::gs_projection_pop();
111 libobs::gs_viewport_pop();
112}
113
114pub struct LockedPosition {
115 pub x: i32,
116 pub y: i32,
117 _fixed_in_heap: PhantomPinned,
119}
120
121#[derive(Clone, Debug)]
122pub struct ObsWindowHandle {
123 pub(crate) window: Sendable<libobs::gs_window>,
124 #[allow(dead_code)]
125 pub(crate) is_wayland: bool,
126}
127
128impl ObsWindowHandle {
129 #[cfg(windows)]
130 pub fn new_from_handle(handle: *mut std::os::raw::c_void) -> Self {
131 Self {
132 window: Sendable(libobs::gs_window { hwnd: handle }),
133 is_wayland: false,
134 }
135 }
136
137 #[cfg(windows)]
138 pub fn get_hwnd(&self) -> windows::Win32::Foundation::HWND {
139 windows::Win32::Foundation::HWND(self.window.0.hwnd)
140 }
141
142 #[cfg(target_os = "linux")]
143 pub fn new_from_wayland(surface: *mut c_void) -> Self {
144 Self {
145 window: Sendable(libobs::gs_window {
146 display: surface,
147 id: 0,
148 }),
149 is_wayland: true,
150 }
151 }
152
153 #[cfg(target_os = "linux")]
154 pub fn new_from_x11(runtime: &ObsRuntime, id: u32) -> Result<Self, ObsError> {
155 let runtime = runtime.clone();
156 let display = run_with_obs!(runtime, (), move || unsafe {
157 Sendable(libobs::obs_get_nix_platform_display())
159 })?;
160
161 Ok(Self {
162 window: Sendable(libobs::gs_window {
163 display: display.0,
164 id,
165 }),
166 is_wayland: false,
167 })
168 }
169}
170
171impl ObsDisplayRef {
172 pub(crate) fn new(data: ObsDisplayCreationData, runtime: ObsRuntime) -> Result<Self, ObsError> {
174 use std::sync::atomic::Ordering;
175
176 use creation_data::ObsDisplayCreationData;
177
178 use crate::run_with_obs;
179
180 let ObsDisplayCreationData {
181 x,
182 y,
183 background_color,
184 create_child,
185 #[cfg(windows)]
186 height,
187 #[cfg(windows)]
188 width,
189 #[cfg(windows)]
190 window_handle,
191 ..
192 } = data.clone();
193
194 #[cfg(windows)]
195 let mut child_handler = if create_child {
196 Some(
197 window_manager::windows::WindowsPreviewChildWindowHandler::new_child(
198 window_handle.clone(),
199 x,
200 y,
201 width,
202 height,
203 )?,
204 )
205 } else {
206 None
207 };
208
209 #[cfg(windows)]
210 let init_data = Sendable(data.build(child_handler.as_ref().map(|e| e.get_window_handle())));
211
212 #[cfg(not(windows))]
213 let init_data = Sendable(data.build(None));
214
215 log::trace!("Creating obs display...");
216 let display = run_with_obs!(runtime, (init_data), move || {
217 let display_ptr = unsafe {
218 libobs::obs_display_create(&init_data.0 .0, background_color)
220 };
221
222 if display_ptr.is_null() {
223 Err(ObsError::NullPointer(None))
224 } else {
225 Ok(Sendable(display_ptr))
226 }
227 })??;
228
229 let display = SmartPointerSendable::new(
230 display.0,
231 Arc::new(_ObsDisplayDropGuard {
232 display,
233 runtime: runtime.clone(),
234 }),
235 );
236
237 #[cfg(windows)]
238 if let Some(handler) = &mut child_handler {
239 handler.set_display_handle(display.clone());
240 }
241
242 let initial_pos = if create_child && cfg!(windows) {
243 (0, 0)
244 } else {
245 (x, y)
246 };
247
248 let id = ID_COUNTER.fetch_add(1, Ordering::Relaxed);
249 DISPLAY_POSITIONS
250 .write()
251 .map_err(|e| ObsError::LockError(format!("{:?}", e)))?
252 .insert(id, initial_pos);
253
254 let instance = Self {
255 display: display.clone(),
256 id,
257 runtime: runtime.clone(),
258 _pos_remove_guard: Arc::new(PosRemoveGuard { id }),
259
260 #[cfg(windows)]
261 child_window_handler: child_handler.map(|e| Arc::new(RwLock::new(e))),
262 };
263
264 log::trace!("Adding draw callback with display {:?}", instance.display);
265
266 let display_ptr = instance.as_ptr();
267 run_with_obs!(runtime, (display_ptr), move || {
268 unsafe {
269 libobs::obs_display_add_draw_callback(
271 display_ptr.get_ptr(),
272 Some(render_display),
273 id as *mut c_void,
274 );
275 }
276 })?;
277
278 Ok(instance)
279 }
280
281 pub fn id(&self) -> usize {
282 self.id
283 }
284
285 pub fn update_color_space(&self) -> Result<(), ObsError> {
286 let display_ptr = self.as_ptr();
287 run_with_obs!(self.runtime, (display_ptr), move || {
288 unsafe {
289 libobs::obs_display_update_color_space(display_ptr.get_ptr())
291 }
292 })
293 }
294
295 pub fn as_ptr(&self) -> SmartPointerSendable<*mut libobs::obs_display_t> {
296 self.display.clone()
297 }
298}
299
300#[derive(Debug)]
301struct _ObsDisplayDropGuard {
302 display: Sendable<*mut libobs::obs_display_t>,
303 runtime: ObsRuntime,
304}
305
306impl ObsDropGuard for _ObsDisplayDropGuard {}
307
308impl_obs_drop!(_ObsDisplayDropGuard, (display), move || unsafe {
309 log::trace!("Removing callback of display {:?}...", display);
311 libobs::obs_display_remove_draw_callback(display.0, Some(render_display), std::ptr::null_mut());
312
313 libobs::obs_display_destroy(display.0);
314});