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::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)]
28pub 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 #[cfg(windows)]
43 #[allow(dead_code)]
44 child_window_handler:
45 Option<Arc<RwLock<window_manager::windows::WindowsPreviewChildWindowHandler>>>,
46
47 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 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 _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 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});