1use std::sync::Mutex;
4use std::{
5 io,
6 ptr,
7};
8
9use window::WindowHandle;
10use windows::Win32::Foundation::{
11 POINT,
12 RECT,
13};
14use windows::Win32::Graphics::Gdi::{
15 CombineRgn,
16 CreateRectRgn,
17 GDI_REGION_TYPE,
18 HRGN,
19 RGN_COMBINE_MODE,
20 RGN_COPY,
21 RGN_DIFF,
22 RGN_ERROR,
23};
24use windows::Win32::System::Console::{
25 AllocConsole,
26 FreeConsole,
27};
28use windows::Win32::System::Shutdown::LockWorkStation;
29use windows::Win32::UI::HiDpi::{
30 DPI_AWARENESS_CONTEXT,
31 SetProcessDpiAwarenessContext,
32 SetThreadDpiAwarenessContext,
33};
34pub use windows::Win32::UI::HiDpi::{
35 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE,
36 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2,
37 DPI_AWARENESS_CONTEXT_SYSTEM_AWARE,
38 DPI_AWARENESS_CONTEXT_UNAWARE,
39 DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED,
40};
41use windows::Win32::UI::Magnification::{
42 MagInitialize,
43 MagSetFullscreenTransform,
44 MagShowSystemCursor,
45};
46use windows::Win32::UI::WindowsAndMessaging::{
47 ClipCursor,
48 GetCursorPos,
49 GetSystemMetrics,
50 SM_CXVIRTUALSCREEN,
51 SM_CYVIRTUALSCREEN,
52 SM_XVIRTUALSCREEN,
53 SM_YVIRTUALSCREEN,
54 SetCursorPos,
55};
56use windows::core::{
57 BOOL,
58 Free,
59};
60
61use crate::internal::{
62 ResultExt,
63 ReturnValue,
64};
65
66pub mod desktop;
67pub mod menu;
68pub mod message_box;
69pub mod messaging;
70pub mod resource;
71pub mod taskbar;
72pub mod window;
73
74pub type Point = POINT;
76pub type Rectangle = RECT;
78
79trait RectTransform {
80 #[expect(dead_code)]
81 fn as_point_array(&self) -> &[POINT];
82 fn as_point_array_mut(&mut self) -> &mut [POINT];
83}
84impl RectTransform for RECT {
85 fn as_point_array(&self) -> &[POINT] {
86 let data = ptr::from_ref(self).cast::<POINT>();
87 unsafe { std::slice::from_raw_parts(data, 2) }
88 }
89
90 fn as_point_array_mut(&mut self) -> &mut [POINT] {
91 let data = ptr::from_mut(self).cast::<POINT>();
92 unsafe { std::slice::from_raw_parts_mut(data, 2) }
93 }
94}
95
96impl ReturnValue for GDI_REGION_TYPE {
97 const NULL_VALUE: Self = RGN_ERROR;
98}
99
100impl ReturnValue for HRGN {
101 const NULL_VALUE: Self = HRGN(ptr::null_mut());
102}
103
104#[derive(Eq, PartialEq, Debug)]
106pub struct Region {
107 raw_handle: HRGN,
108 forget_handle: bool,
109}
110
111impl Region {
112 pub fn from_rect(rect: Rectangle) -> io::Result<Self> {
113 let raw_handle = unsafe { CreateRectRgn(rect.left, rect.top, rect.right, rect.bottom) }
114 .if_null_to_error(|| io::ErrorKind::Other.into())?;
115 Ok(Self::from_non_null(raw_handle))
116 }
117
118 pub(crate) fn from_non_null(handle: HRGN) -> Self {
119 Self {
120 raw_handle: handle,
121 forget_handle: false,
122 }
123 }
124
125 pub fn duplicate(&self) -> io::Result<Self> {
126 self.combine(None, RGN_COPY)
127 }
128
129 pub fn and_not_in(&self, other: &Region) -> io::Result<Self> {
130 self.combine(Some(other), RGN_DIFF)
131 }
132
133 fn combine(&self, other: Option<&Region>, mode: RGN_COMBINE_MODE) -> io::Result<Self> {
134 let dest = Self::from_rect(Default::default())?;
135 unsafe {
136 CombineRgn(
137 Some(dest.raw_handle),
138 Some(self.raw_handle),
139 other.map(|x| x.raw_handle),
140 mode,
141 )
142 .if_null_get_last_error_else_drop()?;
143 }
144 Ok(dest)
145 }
146
147 fn into_raw(mut self) -> HRGN {
148 self.forget_handle = true;
149 self.raw_handle
150 }
151}
152
153impl Drop for Region {
154 fn drop(&mut self) {
155 if !self.forget_handle {
156 unsafe {
157 self.raw_handle.free();
158 }
159 }
160 }
161}
162
163impl From<Region> for HRGN {
164 fn from(value: Region) -> Self {
165 value.into_raw()
166 }
167}
168
169impl From<&Region> for HRGN {
170 fn from(value: &Region) -> Self {
171 value.raw_handle
172 }
173}
174
175#[derive(Debug)]
176#[must_use]
177pub struct CursorConfinement(Rectangle);
178
179impl CursorConfinement {
180 pub fn new(bounding_area: Rectangle) -> io::Result<Self> {
184 Self::apply(bounding_area)?;
185 Ok(Self(bounding_area))
186 }
187
188 pub fn reapply(&self) -> io::Result<()> {
192 Self::apply(self.0)
193 }
194
195 fn apply(bounding_area: Rectangle) -> io::Result<()> {
196 unsafe {
197 ClipCursor(Some(&raw const bounding_area))?;
198 }
199 Ok(())
200 }
201
202 pub fn remove() -> io::Result<()> {
203 unsafe {
204 ClipCursor(None)?;
205 }
206 Ok(())
207 }
208}
209
210impl Drop for CursorConfinement {
211 fn drop(&mut self) {
212 Self::remove().unwrap_or_default_and_print_error();
213 }
214}
215
216#[derive(Debug)]
217#[must_use]
218pub struct UnmagnifiedCursorConcealment(());
219
220impl UnmagnifiedCursorConcealment {
221 pub fn new() -> io::Result<Self> {
225 init_magnifier()?;
226 unsafe {
227 MagShowSystemCursor(false).if_null_get_last_error_else_drop()?;
228 }
229 Ok(Self(()))
230 }
231
232 pub fn remove() -> io::Result<()> {
233 unsafe {
234 MagShowSystemCursor(true).if_null_get_last_error_else_drop()?;
235 }
236 Ok(())
237 }
238}
239
240impl Drop for UnmagnifiedCursorConcealment {
241 fn drop(&mut self) {
242 Self::remove().unwrap_or_default_and_print_error();
243 }
244}
245
246pub fn get_cursor_pos() -> io::Result<Point> {
247 let mut point = Point::default();
248 unsafe { GetCursorPos(&raw mut point)? }
249 Ok(point)
250}
251
252pub fn set_cursor_pos(coords: Point) -> io::Result<()> {
253 unsafe { SetCursorPos(coords.x, coords.y)? }
254 Ok(())
255}
256
257pub fn get_virtual_screen_rect() -> Rectangle {
258 let left = unsafe { GetSystemMetrics(SM_XVIRTUALSCREEN) };
259 let top = unsafe { GetSystemMetrics(SM_YVIRTUALSCREEN) };
260 let width = unsafe { GetSystemMetrics(SM_CXVIRTUALSCREEN) };
261 let height = unsafe { GetSystemMetrics(SM_CYVIRTUALSCREEN) };
262 Rectangle {
263 left,
264 top,
265 right: left + width,
266 bottom: top + height,
267 }
268}
269
270fn init_magnifier() -> io::Result<()> {
271 static MAGNIFIER_INITIALIZED: Mutex<bool> = const { Mutex::new(false) };
272
273 let mut initialized = MAGNIFIER_INITIALIZED.lock().unwrap();
274 if *initialized {
275 Ok(())
276 } else {
277 let result = unsafe { MagInitialize().if_null_get_last_error_else_drop() };
278 *initialized = true;
279 result
280 }
281}
282
283pub fn set_fullscreen_magnification(mag_factor: f32, offset: Point) -> io::Result<()> {
284 init_magnifier()?;
285 unsafe {
286 MagSetFullscreenTransform(mag_factor, offset.x, offset.y).if_null_get_last_error_else_drop()
287 }
288}
289
290pub fn remove_fullscreen_magnification() -> io::Result<()> {
291 set_fullscreen_magnification(1.0, Point { x: 0, y: 0 })
292}
293
294pub fn set_fullscreen_magnification_use_bitmap_smoothing(use_smoothing: bool) -> io::Result<()> {
295 #[link(
296 name = "magnification.dll",
297 kind = "raw-dylib",
298 modifiers = "+verbatim"
299 )]
300 unsafe extern "system" {
301 fn MagSetFullscreenUseBitmapSmoothing(use_smoothing: BOOL) -> BOOL;
302 }
303
304 init_magnifier()?;
305 unsafe {
306 MagSetFullscreenUseBitmapSmoothing(use_smoothing.into()).if_null_get_last_error_else_drop()
307 }
308}
309
310pub fn set_process_dpi_awareness_context(context: DPI_AWARENESS_CONTEXT) -> io::Result<()> {
311 unsafe {
312 SetProcessDpiAwarenessContext(context)?;
313 }
314 Ok(())
315}
316
317pub fn set_thread_dpi_awareness_context(context: DPI_AWARENESS_CONTEXT) -> io::Result<()> {
318 unsafe {
319 SetThreadDpiAwarenessContext(context)
320 .0
321 .if_null_get_last_error_else_drop()?;
322 }
323 Ok(())
324}
325
326pub fn allocate_console() -> io::Result<()> {
328 unsafe {
329 AllocConsole()?;
330 }
331 Ok(())
332}
333
334pub fn detach_console() -> io::Result<()> {
338 unsafe {
339 FreeConsole()?;
340 }
341 Ok(())
342}
343
344pub fn lock_workstation() -> io::Result<()> {
346 unsafe { LockWorkStation()? };
349 Ok(())
350}