1use raw_window_handle::{HasWindowHandle, RawWindowHandle};
10use windows::Win32::Foundation::{HWND, POINT, RECT};
11use windows::Win32::Graphics::Gdi::{GetMonitorInfoW, MonitorFromPoint, MONITORINFO, MONITOR_DEFAULTTONEAREST};
12use windows::Win32::UI::WindowsAndMessaging::{
13 GetCursorPos, GetWindowRect, SetWindowPos, SWP_NOACTIVATE, SWP_NOOWNERZORDER, SWP_NOSIZE, SWP_NOZORDER,
14};
15
16fn hwnd_of(handle: &impl HasWindowHandle) -> Option<HWND> {
17 match handle.window_handle().ok()?.as_raw() {
18 RawWindowHandle::Win32(h) => Some(HWND(h.hwnd.get() as *mut std::ffi::c_void)),
19 _ => None,
20 }
21}
22
23fn work_area(p: POINT) -> Option<RECT> {
25 unsafe {
26 let mon = MonitorFromPoint(p, MONITOR_DEFAULTTONEAREST);
27 let mut info = MONITORINFO { cbSize: std::mem::size_of::<MONITORINFO>() as u32, ..Default::default() };
28 GetMonitorInfoW(mon, &mut info).as_bool().then_some(info.rcWork)
29 }
30}
31
32fn contains(r: RECT, p: POINT) -> bool {
33 p.x >= r.left && p.x < r.right && p.y >= r.top && p.y < r.bottom
34}
35
36fn place(area: RECT, w: i32, h: i32) -> (i32, i32) {
38 let (aw, ah) = (area.right - area.left, area.bottom - area.top);
39 let x = area.left + ((aw - w) / 2).max(0);
40 let y = area.top + ((ah - h) / 2).max(0);
41 (x, y)
42}
43
44pub fn place_on_cursor_monitor(handle: &impl HasWindowHandle) {
47 let Some(hwnd) = hwnd_of(handle) else { return };
48 unsafe {
49 let mut cursor = POINT::default();
50 if GetCursorPos(&mut cursor).is_err() {
51 return;
52 }
53 let Some(area) = work_area(cursor) else { return };
54 let mut rect = RECT::default();
55 if GetWindowRect(hwnd, &mut rect).is_err() {
56 return;
57 }
58 let centre = POINT { x: (rect.left + rect.right) / 2, y: (rect.top + rect.bottom) / 2 };
60 if contains(area, centre) {
61 return;
62 }
63 let (w, h) = (rect.right - rect.left, rect.bottom - rect.top);
64 let (x, y) = place(area, w, h);
65 let _ = SetWindowPos(hwnd, None, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 fn r(left: i32, top: i32, right: i32, bottom: i32) -> RECT {
74 RECT { left, top, right, bottom }
75 }
76
77 #[test]
78 fn places_inside_the_work_area() {
79 let area = r(1920, 0, 3840, 1080); let (x, y) = place(area, 1400, 860);
81 assert!(x >= area.left && x + 1400 <= area.right);
82 assert!(y >= area.top && y + 860 <= area.bottom);
83 let (x, y) = place(area, 4000, 2000);
85 assert_eq!((x, y), (area.left, area.top));
86 }
87
88 #[test]
89 fn contains_matches_win32_half_open_rects() {
90 let area = r(0, 0, 1920, 1080);
91 assert!(contains(area, POINT { x: 0, y: 0 }));
92 assert!(contains(area, POINT { x: 1919, y: 1079 }));
93 assert!(!contains(area, POINT { x: 1920, y: 500 }));
94 assert!(!contains(area, POINT { x: -1, y: 500 }));
95 }
96}