libobs_wrapper\display\window_manager/
position_trait.rs

1#[cfg(windows)]
2use windows::Win32::{
3    Foundation::HWND,
4    Graphics::Gdi::{RedrawWindow, RDW_ERASE, RDW_INVALIDATE},
5    UI::WindowsAndMessaging::{
6        SetWindowPos, HWND_BOTTOM, SWP_NOACTIVATE, SWP_NOCOPYBITS, SWP_NOSIZE, SWP_NOZORDER,
7        SWP_SHOWWINDOW,
8    },
9};
10
11use crate::display::window_manager::WindowPositionTrait;
12use crate::display::DISPLAY_POSITIONS;
13use crate::utils::ObsError;
14use crate::{display::ObsDisplayRef, run_with_obs};
15
16impl WindowPositionTrait for ObsDisplayRef {
17    fn set_render_at_bottom(&self, _render_at_bottom: bool) -> Result<(), ObsError> {
18        log::trace!("Set render bottom");
19        #[cfg(windows)]
20        if let Some(m) = self.child_window_handler.as_ref() {
21            let mut m = m
22                .write()
23                .map_err(|e| ObsError::LockError(format!("{:?}", e)))?;
24
25            m.render_at_bottom = _render_at_bottom;
26        }
27        Ok(())
28    }
29
30    fn get_render_at_bottom(&self) -> Result<bool, ObsError> {
31        #[cfg(windows)]
32        if let Some(m) = self.child_window_handler.as_ref() {
33            let m = m
34                .read()
35                .map_err(|e| ObsError::LockError(format!("{:?}", e)))?;
36            return Ok(m.render_at_bottom);
37        }
38
39        Ok(false)
40    }
41
42    fn set_pos(&self, x: i32, y: i32) -> Result<(), ObsError> {
43        log::trace!("Set pos {x} {y}");
44
45        #[cfg(windows)]
46        if let Some(m) = self.child_window_handler.as_ref() {
47            let mut m = m
48                .write()
49                .map_err(|e| ObsError::LockError(format!("{:?}", e)))?;
50
51            assert!(
52                m.obs_display.is_some(),
53                "Invalid state. The display should have been created and set, but it wasn't."
54            );
55
56            let insert_after = if m.render_at_bottom {
57                HWND_BOTTOM
58            } else {
59                HWND::default()
60            };
61
62            m.x = x;
63            m.y = y;
64
65            unsafe {
66                // SAFETY: The window handle is valid because it was created and stored in the struct during initialization.
67                // SetWindowPos is a safe Windows API call when provided with a valid window handle and valid parameters.
68                let flags = SWP_NOCOPYBITS | SWP_NOSIZE | SWP_NOACTIVATE;
69                // Just use dummy values as size is not changed
70                SetWindowPos(
71                    m.window_handle.get_hwnd(),
72                    Some(insert_after),
73                    x,
74                    y,
75                    1_i32,
76                    1_i32,
77                    flags,
78                )
79                .map_err(|e| ObsError::DisplayCreationError(format!("{:?}", e)))?;
80            }
81
82            // Update color space when window position changes
83            drop(m); // Release the lock before calling run_with_obs
84            self.update_color_space()?;
85            return Ok(());
86        }
87
88        *DISPLAY_POSITIONS
89            .write()
90            .map_err(|e| ObsError::LockError(format!("{:?}", e)))?
91            .get_mut(&self.id)
92            .ok_or_else(|| ObsError::LockError("Position not found".to_string()))? = (x, y);
93
94        self.update_color_space()?;
95        Ok(())
96    }
97
98    fn set_size(&self, width: u32, height: u32) -> Result<(), ObsError> {
99        log::trace!("Set size {width} {height}");
100
101        #[cfg(windows)]
102        if let Some(m) = self.child_window_handler.as_ref() {
103            let mut m = m
104                .write()
105                .map_err(|e| ObsError::LockError(format!("{:?}", e)))?;
106
107            assert!(
108                m.obs_display.is_some(),
109                "Invalid state. The display should have been created and set, but it wasn't."
110            );
111
112            m.width = width;
113            m.height = height;
114
115            unsafe {
116                // SAFETY: The window handle is valid because it was created and stored in the struct during initialization.
117                // SetWindowPos is a Windows API call that is safe when provided with a valid window handle and valid parameters.
118                SetWindowPos(
119                    m.window_handle.get_hwnd(),
120                    None,
121                    m.x,
122                    m.y,
123                    width as i32,
124                    height as i32,
125                    SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER | SWP_SHOWWINDOW,
126                )
127                .map_err(|e| ObsError::DisplayCreationError(format!("{:?}", e)))?;
128
129                let _ = RedrawWindow(
130                    Some(m.window_handle.get_hwnd()),
131                    None,
132                    None,
133                    RDW_ERASE | RDW_INVALIDATE,
134                );
135            }
136        }
137
138        let pointer = self.as_ptr();
139        run_with_obs!(self.runtime, (pointer), move || {
140            unsafe {
141                // Safety: The pointer is valid because we are using a smart pointer
142
143                libobs::obs_display_resize(pointer.get_ptr(), width, height);
144                // Update color space when window size changes
145                libobs::obs_display_update_color_space(pointer.get_ptr());
146            }
147        })
148        .map_err(|e| ObsError::InvocationError(format!("{:?}", e)))?;
149        Ok(())
150    }
151
152    fn get_pos(&self) -> Result<(i32, i32), ObsError> {
153        #[cfg(windows)]
154        if let Some(m) = self.child_window_handler.as_ref() {
155            let m = m
156                .read()
157                .map_err(|e| ObsError::LockError(format!("{:?}", e)))?;
158            return Ok((m.x, m.y));
159        }
160
161        let pos = DISPLAY_POSITIONS
162            .read()
163            .map_err(|e| ObsError::LockError(format!("{:?}", e)))?;
164        let pos = pos
165            .get(&self.id)
166            .ok_or_else(|| ObsError::LockError("Position not found".to_string()))?;
167
168        Ok(*pos)
169    }
170
171    fn get_size(&self) -> Result<(u32, u32), ObsError> {
172        #[cfg(windows)]
173        if let Some(m) = self.child_window_handler.as_ref() {
174            let m = m
175                .read()
176                .map_err(|e| ObsError::LockError(format!("{:?}", e)))?;
177
178            return Ok((m.width, m.height));
179        }
180
181        let pointer = self.as_ptr();
182        let (width, height) = run_with_obs!(self.runtime, (pointer), move || {
183            let mut w: u32 = 0;
184            let mut h: u32 = 0;
185            unsafe {
186                // Safety: The pointer is valid because we are using a smart pointer and because `w` and `h` are valid mutable references.
187                libobs::obs_display_size(pointer.get_ptr(), &mut w, &mut h);
188            };
189
190            (w, h)
191        })
192        .map_err(|e| ObsError::InvocationError(format!("{:?}", e)))?;
193
194        Ok((width, height))
195    }
196}