Files
galaxy/crates/computer_use/src/windows/dpi.rs
T

48 lines
2.0 KiB
Rust

//! Thread-local DPI-awareness helper for Windows computer_use actions.
//!
//! Several Win32 APIs this crate calls (`SetCursorPos`, `GetCursorPos`,
//! `GetSystemMetrics(SM_*VIRTUALSCREEN)`, `BitBlt`, …) return *logical* coordinates if the
//! calling thread is not DPI-aware, which causes mis-located clicks and scaled/cropped screenshots
//! on HiDPI monitors.
//!
//! Rather than relying on the host process manifest, we opt every computer_use operation into
//! per-monitor-v2 awareness for the duration of the call via [`DpiAwarenessGuard`].
use windows::Win32::UI::HiDpi::{
DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, SetThreadDpiAwarenessContext,
};
/// RAII guard that requests per-monitor-v2 DPI awareness for the current thread and restores the
/// previous context when dropped.
///
/// Requires Windows 10 version 1703 or newer (when `PER_MONITOR_AWARE_V2` shipped — 1607 only
/// had V1). On older systems (or when the process awareness cannot be overridden)
/// `SetThreadDpiAwarenessContext` returns a null context; in that case this guard is a no-op.
pub(super) struct DpiAwarenessGuard {
previous: Option<DPI_AWARENESS_CONTEXT>,
}
impl DpiAwarenessGuard {
/// Enters per-monitor-v2 DPI awareness for the calling thread.
pub(super) fn enter_per_monitor_v2() -> Self {
// SAFETY: `SetThreadDpiAwarenessContext` has no preconditions and mutates only
// thread-local state.
let prev =
unsafe { SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) };
let previous = if prev.0.is_null() { None } else { Some(prev) };
Self { previous }
}
}
impl Drop for DpiAwarenessGuard {
fn drop(&mut self) {
if let Some(prev) = self.previous {
// SAFETY: `prev` was returned by a prior successful call to
// `SetThreadDpiAwarenessContext` on this same thread.
unsafe {
let _ = SetThreadDpiAwarenessContext(prev);
}
}
}
}