Skip to content

Commit

Permalink
Adopt windows-rs
Browse files Browse the repository at this point in the history
Change from using the unofficial winapi bindings, to using the official windows bindings from Microsoft.
  • Loading branch information
clemenswasser committed Nov 6, 2021
1 parent cfbe846 commit 2b93692
Show file tree
Hide file tree
Showing 15 changed files with 1,525 additions and 1,444 deletions.
51 changes: 27 additions & 24 deletions Cargo.toml
Expand Up @@ -58,31 +58,34 @@ features = ["display_link"]
[target.'cfg(target_os = "windows")'.dependencies]
parking_lot = "0.11"

[target.'cfg(target_os = "windows")'.dependencies.winapi]
version = "0.3.9"
[target.'cfg(target_os = "windows")'.dependencies.windows]
version = "0.25"
features = [
"combaseapi",
"commctrl",
"dwmapi",
"errhandlingapi",
"imm",
"hidusage",
"libloaderapi",
"objbase",
"ole2",
"processthreadsapi",
"shellapi",
"shellscalingapi",
"shobjidl_core",
"unknwnbase",
"winbase",
"windowsx",
"winerror",
"wingdi",
"winnt",
"winuser",
"mmsystem",
"timeapi"
"build",
"Win32_Devices_HumanInterfaceDevice",
"Win32_Foundation",
"Win32_Globalization",
"Win32_Graphics_Dwm",
"Win32_Graphics_Gdi",
"Win32_Media_Multimedia",
"Win32_System_Com_StructuredStorage",
"Win32_System_Com",
"Win32_System_LibraryLoader",
"Win32_System_Ole",
"Win32_System_SystemInformation",
"Win32_System_SystemServices",
"Win32_System_Threading",
"Win32_System_WindowsProgramming",
"Win32_UI_Accessibility",
"Win32_UI_Controls",
"Win32_UI_HiDpi",
"Win32_UI_Input_Ime",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_Input_Pointer",
"Win32_UI_Input_Touch",
"Win32_UI_Shell",
"Win32_UI_TextServices",
"Win32_UI_WindowsAndMessaging",
]

[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
Expand Down
18 changes: 10 additions & 8 deletions src/platform/windows.rs
Expand Up @@ -3,8 +3,10 @@
use std::os::raw::c_void;
use std::path::Path;

use winapi::shared::minwindef::WORD;
use winapi::shared::windef::{HMENU, HWND};
use windows::Win32::{
Foundation::HWND,
UI::{Input::KeyboardAndMouse::EnableWindow, WindowsAndMessaging::HMENU},
};

use crate::{
dpi::PhysicalSize,
Expand Down Expand Up @@ -102,18 +104,18 @@ pub trait WindowExtWindows {
impl WindowExtWindows for Window {
#[inline]
fn hinstance(&self) -> *mut c_void {
self.window.hinstance() as *mut _
self.window.hinstance().0 as *mut c_void
}

#[inline]
fn hwnd(&self) -> *mut c_void {
self.window.hwnd() as *mut _
self.window.hwnd().0 as *mut c_void
}

#[inline]
fn set_enable(&self, enabled: bool) {
unsafe {
winapi::um::winuser::EnableWindow(self.hwnd() as _, enabled as _);
EnableWindow(HWND(self.hwnd() as isize), enabled);
}
}

Expand Down Expand Up @@ -238,7 +240,7 @@ impl MonitorHandleExtWindows for MonitorHandle {

#[inline]
fn hmonitor(&self) -> *mut c_void {
self.inner.hmonitor() as *mut _
self.inner.hmonitor().0 as *mut c_void
}
}

Expand Down Expand Up @@ -276,7 +278,7 @@ pub trait IconExtWindows: Sized {
///
/// In cases where the specified size does not exist in the file, Windows may perform scaling
/// to get an icon of the desired size.
fn from_resource(ordinal: WORD, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon>;
fn from_resource(ordinal: u16, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon>;
}

impl IconExtWindows for Icon {
Expand All @@ -288,7 +290,7 @@ impl IconExtWindows for Icon {
Ok(Icon { inner: win_icon })
}

fn from_resource(ordinal: WORD, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> {
fn from_resource(ordinal: u16, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> {
let win_icon = WinIcon::from_resource(ordinal, size)?;
Ok(Icon { inner: win_icon })
}
Expand Down
82 changes: 34 additions & 48 deletions src/platform_impl/windows/dark_mode.rs
@@ -1,29 +1,29 @@
/// This is a simple implementation of support for Windows Dark Mode,
/// which is inspired by the solution in https://github.com/ysc3839/win32-darkmode
use std::ffi::OsStr;
use std::ffi::{c_void, OsStr};
use std::iter::once;
use std::os::windows::ffi::OsStrExt;

use winapi::{
shared::{
basetsd::SIZE_T,
minwindef::{BOOL, DWORD, FALSE, WORD},
ntdef::{NTSTATUS, NT_SUCCESS, PVOID},
windef::HWND,
winerror::S_OK,
},
um::{libloaderapi, uxtheme, winnt, winuser},
use windows::runtime::Handle;
use windows::Win32::Foundation::{BOOL, HWND, NTSTATUS, PSTR, PWSTR};
use windows::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA};
use windows::Win32::System::SystemInformation::OSVERSIONINFOW;
use windows::Win32::UI::Accessibility::{HCF_HIGHCONTRASTON, HIGHCONTRASTA, HIGHCONTRASTW_FLAGS};
use windows::Win32::UI::Controls::SetWindowTheme;
use windows::Win32::UI::WindowsAndMessaging::{
SystemParametersInfoA, SPI_GETHIGHCONTRAST, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS,
};

use crate::window::Theme;

lazy_static! {
static ref WIN10_BUILD_VERSION: Option<DWORD> = {
type RtlGetVersion = unsafe extern "system" fn (*mut winnt::OSVERSIONINFOW) -> NTSTATUS;
static ref WIN10_BUILD_VERSION: Option<u32> = {
type RtlGetVersion = unsafe extern "system" fn (*mut OSVERSIONINFOW) -> NTSTATUS;
let handle = get_function!("ntdll.dll", RtlGetVersion);

if let Some(rtl_get_version) = handle {
unsafe {
let mut vi = winnt::OSVERSIONINFOW {
let mut vi = OSVERSIONINFOW {
dwOSVersionInfoSize: 0,
dwMajorVersion: 0,
dwMinorVersion: 0,
Expand All @@ -34,7 +34,7 @@ lazy_static! {

let status = (rtl_get_version)(&mut vi as _);

if NT_SUCCESS(status) && vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 {
if (status.0 as i32) >= 0 && vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 {
Some(vi.dwBuildNumber)
} else {
None
Expand All @@ -54,8 +54,8 @@ lazy_static! {
}
};

static ref DARK_THEME_NAME: Vec<u16> = widestring("DarkMode_Explorer");
static ref LIGHT_THEME_NAME: Vec<u16> = widestring("");
static ref DARK_THEME_NAME: Vec<u16> = OsStr::new("DarkMode_Explorer").encode_wide().chain(once(0)).collect();
static ref LIGHT_THEME_NAME: Vec<u16> = OsStr::new("").encode_wide().chain(once(0)).collect();
}

/// Attempt to set a theme on a window, if necessary.
Expand All @@ -77,9 +77,9 @@ pub fn try_theme(hwnd: HWND, preferred_theme: Option<Theme>) -> Theme {
Theme::Light => LIGHT_THEME_NAME.as_ptr(),
};

let status = unsafe { uxtheme::SetWindowTheme(hwnd, theme_name as _, std::ptr::null()) };
let status = unsafe { SetWindowTheme(hwnd, PWSTR(theme_name as *mut u16), None) };

if status == S_OK && set_dark_mode_for_window(hwnd, is_dark_mode) {
if status.is_ok() && set_dark_mode_for_window(hwnd, is_dark_mode) {
return theme;
}
}
Expand All @@ -103,8 +103,8 @@ fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool {
#[repr(C)]
struct WINDOWCOMPOSITIONATTRIBDATA {
Attrib: WINDOWCOMPOSITIONATTRIB,
pvData: PVOID,
cbData: SIZE_T,
pvData: *mut c_void,
cbData: usize,
}

lazy_static! {
Expand All @@ -115,7 +115,7 @@ fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool {
if let Some(set_window_composition_attribute) = *SET_WINDOW_COMPOSITION_ATTRIBUTE {
unsafe {
// SetWindowCompositionAttribute needs a bigbool (i32), not bool.
let mut is_dark_mode_bigbool = is_dark_mode as BOOL;
let mut is_dark_mode_bigbool = is_dark_mode;

let mut data = WINDOWCOMPOSITIONATTRIBDATA {
Attrib: WCA_USEDARKMODECOLORS,
Expand All @@ -125,7 +125,7 @@ fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool {

let status = set_window_composition_attribute(hwnd, &mut data as *mut _);

status != FALSE
status.as_bool()
}
} else {
false
Expand All @@ -141,24 +141,17 @@ fn should_apps_use_dark_mode() -> bool {
lazy_static! {
static ref SHOULD_APPS_USE_DARK_MODE: Option<ShouldAppsUseDarkMode> = {
unsafe {
const UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL: WORD = 132;
const UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL: PSTR = PSTR(132 as *mut u8);

let module = libloaderapi::LoadLibraryA("uxtheme.dll\0".as_ptr() as _);
let module = LoadLibraryA(PSTR("uxtheme.dll\0".as_ptr() as *mut u8));

if module.is_null() {
if module.is_invalid() {
return None;
}

let handle = libloaderapi::GetProcAddress(
module,
winuser::MAKEINTRESOURCEA(UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL),
);
let handle = GetProcAddress(module, UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL);

if handle.is_null() {
None
} else {
Some(std::mem::transmute(handle))
}
handle.map(|handle| std::mem::transmute(handle))
}
};
}
Expand All @@ -169,27 +162,20 @@ fn should_apps_use_dark_mode() -> bool {
}

fn is_high_contrast() -> bool {
let mut hc = winuser::HIGHCONTRASTA {
let mut hc = HIGHCONTRASTA {
cbSize: 0,
dwFlags: 0,
lpszDefaultScheme: std::ptr::null_mut(),
dwFlags: HIGHCONTRASTW_FLAGS(0),
lpszDefaultScheme: PSTR(std::ptr::null_mut()),
};

let ok = unsafe {
winuser::SystemParametersInfoA(
winuser::SPI_GETHIGHCONTRAST,
SystemParametersInfoA(
SPI_GETHIGHCONTRAST,
std::mem::size_of_val(&hc) as _,
&mut hc as *mut _ as _,
0,
SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS(0),
)
};

ok != FALSE && (winuser::HCF_HIGHCONTRASTON & hc.dwFlags) == 1
}

fn widestring(src: &'static str) -> Vec<u16> {
OsStr::new(src)
.encode_wide()
.chain(Some(0).into_iter())
.collect()
ok.as_bool() && (hc.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON
}
47 changes: 26 additions & 21 deletions src/platform_impl/windows/dpi.rs
Expand Up @@ -2,41 +2,46 @@

use std::sync::Once;

use windows::{
runtime::Handle,
Win32::{
Foundation::{HWND, S_OK},
Graphics::Gdi::{
GetDC, GetDeviceCaps, MonitorFromWindow, HMONITOR, LOGPIXELSX, MONITOR_DEFAULTTONEAREST,
},
UI::{
HiDpi::{
DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, MDT_EFFECTIVE_DPI,
PROCESS_PER_MONITOR_DPI_AWARE,
},
WindowsAndMessaging::IsProcessDPIAware,
},
},
};

use crate::platform_impl::platform::util::{
ENABLE_NON_CLIENT_DPI_SCALING, GET_DPI_FOR_MONITOR, GET_DPI_FOR_WINDOW, SET_PROCESS_DPI_AWARE,
SET_PROCESS_DPI_AWARENESS, SET_PROCESS_DPI_AWARENESS_CONTEXT,
};
use winapi::{
shared::{
minwindef::FALSE,
windef::{DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, HMONITOR, HWND},
winerror::S_OK,
},
um::{
shellscalingapi::{MDT_EFFECTIVE_DPI, PROCESS_PER_MONITOR_DPI_AWARE},
wingdi::{GetDeviceCaps, LOGPIXELSX},
winuser::{self, MONITOR_DEFAULTTONEAREST},
},
};

const DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2: DPI_AWARENESS_CONTEXT = -4isize as _;
const DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2: DPI_AWARENESS_CONTEXT = DPI_AWARENESS_CONTEXT(-4);

pub fn become_dpi_aware() {
static ENABLE_DPI_AWARENESS: Once = Once::new();
ENABLE_DPI_AWARENESS.call_once(|| {
unsafe {
if let Some(SetProcessDpiAwarenessContext) = *SET_PROCESS_DPI_AWARENESS_CONTEXT {
// We are on Windows 10 Anniversary Update (1607) or later.
if SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
== FALSE
if !SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
.as_bool()
{
// V2 only works with Windows 10 Creators Update (1703). Try using the older
// V1 if we can't set V2.
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
}
} else if let Some(SetProcessDpiAwareness) = *SET_PROCESS_DPI_AWARENESS {
// We are on Windows 8.1 or later.
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
let _ = SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
} else if let Some(SetProcessDPIAware) = *SET_PROCESS_DPI_AWARE {
// We are on Vista or later.
SetProcessDPIAware();
Expand Down Expand Up @@ -76,8 +81,8 @@ pub fn dpi_to_scale_factor(dpi: u32) -> f64 {
}

pub unsafe fn hwnd_dpi(hwnd: HWND) -> u32 {
let hdc = winuser::GetDC(hwnd);
if hdc.is_null() {
let hdc = GetDC(hwnd);
if hdc.is_invalid() {
panic!("[winit] `GetDC` returned null!");
}
if let Some(GetDpiForWindow) = *GET_DPI_FOR_WINDOW {
Expand All @@ -88,8 +93,8 @@ pub unsafe fn hwnd_dpi(hwnd: HWND) -> u32 {
}
} else if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR {
// We are on Windows 8.1 or later.
let monitor = winuser::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if monitor.is_null() {
let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if monitor.is_invalid() {
return BASE_DPI;
}

Expand All @@ -102,7 +107,7 @@ pub unsafe fn hwnd_dpi(hwnd: HWND) -> u32 {
}
} else {
// We are on Vista or later.
if winuser::IsProcessDPIAware() != FALSE {
if IsProcessDPIAware().as_bool() {
// If the process is DPI aware, then scaling must be handled by the application using
// this DPI value.
GetDeviceCaps(hdc, LOGPIXELSX) as u32
Expand Down

0 comments on commit 2b93692

Please sign in to comment.