From 15c06e09935416b10332b5d5e74c2b95c1f8d992 Mon Sep 17 00:00:00 2001 From: Benjamin Klum Date: Sun, 16 Apr 2023 09:34:42 +0200 Subject: [PATCH] #145 Add support for Windows 7 and 8 --- Cargo.toml | 1 + src/win/dynamic_win_api.rs | 35 +++++++++++++++++++++++++++++++++++ src/win/mod.rs | 1 + src/win/window.rs | 32 ++++++++++++++++---------------- 4 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 src/win/dynamic_win_api.rs diff --git a/Cargo.toml b/Cargo.toml index eb23f4e5..d22e7669 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ nix = "0.22.0" [target.'cfg(target_os="windows")'.dependencies] winapi = { version = "0.3.8", features = ["libloaderapi", "winuser", "windef", "minwindef", "guiddef", "combaseapi", "wingdi", "errhandlingapi"] } uuid = { version = "0.8", features = ["v4"], optional = true } +libloading = "0.8.0" [target.'cfg(target_os="macos")'.dependencies] cocoa = "0.24.0" diff --git a/src/win/dynamic_win_api.rs b/src/win/dynamic_win_api.rs new file mode 100644 index 00000000..daa3c80c --- /dev/null +++ b/src/win/dynamic_win_api.rs @@ -0,0 +1,35 @@ +use libloading::{Library, Symbol}; +use winapi::shared::minwindef::{BOOL, UINT}; +use winapi::shared::windef::{DPI_AWARENESS_CONTEXT, HWND}; + +/// Provides access to some Win32 API functions that are not available in older Windows versions. +/// +/// This is better than eagerly linking to these functions because then the resulting binary +/// wouldn't work *at all* in the older Windows versions, whereas with this approach, we can +/// fall back to alternative logic or alternative values on a case-by-case basis. +pub struct DynamicWinApi { + user32_library: Library, +} + +impl DynamicWinApi { + /// Loads the dynamic windows API, in particular "user32.dll". + pub fn load() -> Self { + unsafe { Self { user32_library: Library::new("user32.dll").unwrap() } } + } + + /// Should be available from Windows 10 onwards. + pub fn set_process_dpi_awareness_context( + &self, + ) -> Option> { + unsafe { self.user32_library.get(b"SetProcessDpiAwarenessContext").ok() } + } + + /// Should be available from Windows 10 onwards. + pub fn get_dpi_for_window(&self) -> Option> { + unsafe { self.user32_library.get(b"GetDpiForWindow").ok() } + } +} + +type SetProcessDpiAwarenessContext = extern "stdcall" fn(value: DPI_AWARENESS_CONTEXT) -> BOOL; + +type GetDpiForWindow = extern "stdcall" fn(hwnd: HWND) -> UINT; diff --git a/src/win/mod.rs b/src/win/mod.rs index 1d76d144..b09e5280 100644 --- a/src/win/mod.rs +++ b/src/win/mod.rs @@ -1,3 +1,4 @@ +mod dynamic_win_api; mod keyboard; mod window; diff --git a/src/win/window.rs b/src/win/window.rs index 2af1abed..021e238e 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -4,16 +4,15 @@ use winapi::shared::windef::{HWND, RECT}; use winapi::um::combaseapi::CoCreateGuid; use winapi::um::winuser::{ AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, - GetDpiForWindow, GetMessageW, GetWindowLongPtrW, LoadCursorW, PostMessageW, RegisterClassW, - ReleaseCapture, SetCapture, SetProcessDpiAwarenessContext, SetTimer, SetWindowLongPtrW, - SetWindowPos, TranslateMessage, UnregisterClassW, CS_OWNDC, GET_XBUTTON_WPARAM, GWLP_USERDATA, - IDC_ARROW, MSG, SWP_NOMOVE, SWP_NOZORDER, WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_CREATE, - WM_DPICHANGED, WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, - WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCDESTROY, - WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SHOWWINDOW, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, - WM_TIMER, WM_USER, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_CAPTION, WS_CHILD, - WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE, - XBUTTON1, XBUTTON2, + GetMessageW, GetWindowLongPtrW, LoadCursorW, PostMessageW, RegisterClassW, ReleaseCapture, + SetCapture, SetTimer, SetWindowLongPtrW, SetWindowPos, TranslateMessage, UnregisterClassW, + CS_OWNDC, GET_XBUTTON_WPARAM, GWLP_USERDATA, IDC_ARROW, MSG, SWP_NOMOVE, SWP_NOZORDER, + WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_CREATE, WM_DPICHANGED, WM_INPUTLANGCHANGE, WM_KEYDOWN, + WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEHWHEEL, + WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCDESTROY, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SHOWWINDOW, + WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TIMER, WM_USER, WM_XBUTTONDOWN, + WM_XBUTTONUP, WNDCLASSW, WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, + WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE, XBUTTON1, XBUTTON2, }; use std::cell::{Cell, RefCell}; @@ -35,6 +34,7 @@ use crate::{ use super::keyboard::KeyboardState; +use crate::win::dynamic_win_api::DynamicWinApi; #[cfg(feature = "opengl")] use crate::{gl::GlContext, window::RawWindowHandleWrapper}; @@ -681,15 +681,15 @@ impl Window<'_> { }; *window_state.handler.borrow_mut() = Some(Box::new(handler)); - // Only works on Windows 10 unfortunately. - SetProcessDpiAwarenessContext( - winapi::shared::windef::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, - ); + let dynamic_win_api = DynamicWinApi::load(); + if let Some(f) = dynamic_win_api.set_process_dpi_awareness_context() { + f(winapi::shared::windef::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); + } // Now we can get the actual dpi of the window. let new_rect = if let WindowScalePolicy::SystemScaleFactor = options.scale { - // Only works on Windows 10 unfortunately. - let dpi = GetDpiForWindow(hwnd); + let dpi = + if let Some(f) = dynamic_win_api.get_dpi_for_window() { f(hwnd) } else { 96 }; let scale_factor = dpi as f64 / 96.0; let mut window_info = window_state.window_info.borrow_mut();