diff --git a/.changes/windows-rect-dpi.md b/.changes/windows-rect-dpi.md new file mode 100644 index 0000000..f9271be --- /dev/null +++ b/.changes/windows-rect-dpi.md @@ -0,0 +1,5 @@ +--- +"tray-icon": "patch" +--- + +Fix tray icon rect scaled by dpi on Windows diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 8ddf1df..b143020 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -241,11 +241,7 @@ impl TrayIcon { } pub fn rect(&self) -> Option { - get_tray_rect(self.internal_id, self.hwnd).map(|rect| { - let dpi = unsafe { util::hwnd_dpi(self.hwnd) }; - let scale_factor = util::dpi_to_scale_factor(dpi); - Rect::from_win32(rect, scale_factor) - }) + get_tray_rect(self.internal_id, self.hwnd).map(Into::into) } } @@ -344,14 +340,11 @@ unsafe extern "system" fn tray_proc( return 0; } - let dpi = util::hwnd_dpi(hwnd); - let scale_factor = util::dpi_to_scale_factor(dpi); - let id = userdata.id.clone(); let position = PhysicalPosition::new(cursor.x as f64, cursor.y as f64); let rect = match get_tray_rect(userdata.internal_id, hwnd) { - Some(rect) => Rect::from_win32(rect, scale_factor), + Some(rect) => Rect::from(rect), None => return 0, }; @@ -445,14 +438,11 @@ unsafe extern "system" fn tray_proc( let in_y = (rect.top..rect.bottom).contains(&cursor.y); if !in_x || !in_y { - let dpi = util::hwnd_dpi(hwnd); - let scale_factor = util::dpi_to_scale_factor(dpi); - KillTimer(hwnd, WM_USER_LEAVE_TIMER_ID as _); TrayIconEvent::send(TrayIconEvent::Leave { id: userdata.id.clone(), - rect: Rect::from_win32(rect, scale_factor), + rect: rect.into(), position, }); } @@ -562,16 +552,14 @@ fn get_tray_rect(id: u32, hwnd: HWND) -> Option { } } -impl Rect { - fn from_win32(rect: RECT, scale_factor: f64) -> Self { +impl From for Rect { + fn from(rect: RECT) -> Self { Self { - position: crate::dpi::LogicalPosition::new(rect.left, rect.top) - .to_physical(scale_factor), - size: crate::dpi::LogicalSize::new( - rect.right.saturating_sub(rect.left), - rect.bottom.saturating_sub(rect.top), - ) - .to_physical(scale_factor), + position: crate::dpi::PhysicalPosition::new(rect.left.into(), rect.top.into()), + size: crate::dpi::PhysicalSize::new( + rect.right.saturating_sub(rect.left) as u32, + rect.bottom.saturating_sub(rect.top) as u32, + ), } } } diff --git a/src/platform_impl/windows/util.rs b/src/platform_impl/windows/util.rs index afd5cc6..045fa65 100644 --- a/src/platform_impl/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -4,20 +4,9 @@ use std::ops::{Deref, DerefMut}; -use once_cell::sync::Lazy; -use windows_sys::{ - core::HRESULT, - Win32::{ - Foundation::{FARPROC, HWND, S_OK}, - Graphics::Gdi::{ - GetDC, GetDeviceCaps, MonitorFromWindow, HMONITOR, LOGPIXELSX, MONITOR_DEFAULTTONEAREST, - }, - System::LibraryLoader::{GetProcAddress, LoadLibraryW}, - UI::{ - HiDpi::{MDT_EFFECTIVE_DPI, MONITOR_DPI_TYPE}, - WindowsAndMessaging::{IsProcessDPIAware, ACCEL, WINDOW_LONG_PTR_INDEX}, - }, - }, +use windows_sys::Win32::{ + Foundation::HWND, + UI::WindowsAndMessaging::{ACCEL, WINDOW_LONG_PTR_INDEX}, }; pub fn encode_wide>(string: S) -> Vec { @@ -72,81 +61,6 @@ pub fn get_instance_handle() -> windows_sys::Win32::Foundation::HMODULE { unsafe { &__ImageBase as *const _ as _ } } -fn get_function_impl(library: &str, function: &str) -> FARPROC { - let library = encode_wide(library); - assert_eq!(function.chars().last(), Some('\0')); - - // Library names we will use are ASCII so we can use the A version to avoid string conversion. - let module = unsafe { LoadLibraryW(library.as_ptr()) }; - if module == 0 { - return None; - } - - unsafe { GetProcAddress(module, function.as_ptr()) } -} - -macro_rules! get_function { - ($lib:expr, $func:ident) => { - crate::platform_impl::platform::util::get_function_impl( - $lib, - concat!(stringify!($func), '\0'), - ) - .map(|f| unsafe { std::mem::transmute::<_, $func>(f) }) - }; -} - -pub type GetDpiForMonitor = unsafe extern "system" fn( - hmonitor: HMONITOR, - dpi_type: MONITOR_DPI_TYPE, - dpi_x: *mut u32, - dpi_y: *mut u32, -) -> HRESULT; - -static GET_DPI_FOR_MONITOR: Lazy> = - Lazy::new(|| get_function!("shcore.dll", GetDpiForMonitor)); - -pub const BASE_DPI: u32 = 96; -pub fn dpi_to_scale_factor(dpi: u32) -> f64 { - dpi as f64 / BASE_DPI as f64 -} - -#[allow(non_snake_case)] -pub unsafe fn hwnd_dpi(hwnd: HWND) -> u32 { - if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR { - // We are on Windows 8.1 or later. - let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - if monitor == 0 { - return BASE_DPI; - } - - let mut dpi_x = 0; - let mut dpi_y = 0; - #[allow(clippy::unnecessary_cast)] - if GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) == S_OK { - dpi_x as u32 - } else { - BASE_DPI - } - } else { - let hdc = GetDC(hwnd); - if hdc == 0 { - return BASE_DPI; - } - - // We are on Vista or later. - if IsProcessDPIAware() == 1 { - // If the process is DPI aware, then scaling must be handled by the application using - // this DPI value. - GetDeviceCaps(hdc, LOGPIXELSX as _) as u32 - } else { - // If the process is DPI unaware, then scaling is performed by the OS; we thus return - // 96 (scale factor 1.0) to prevent the window from being re-scaled by both the - // application and the WM. - BASE_DPI - } - } -} - #[inline(always)] pub unsafe fn get_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX) -> isize { #[cfg(target_pointer_width = "64")]