Skip to content

Commit

Permalink
Adopt windows-sys (#2057)
Browse files Browse the repository at this point in the history
  • Loading branch information
clemenswasser committed Mar 7, 2022
1 parent 78e5a39 commit b222dde
Show file tree
Hide file tree
Showing 17 changed files with 1,427 additions and 1,242 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -21,6 +21,8 @@ And please only add new entries to the top of this list, right below the `# Unre
- **Breaking:** Replaced `EventLoopExtMacOS` with `EventLoopBuilderExtMacOS` (which also has renamed methods).
- **Breaking:** Replaced `EventLoopExtWindows` with `EventLoopBuilderExtWindows` (which also has renamed methods).
- **Breaking:** Replaced `EventLoopExtUnix` with `EventLoopBuilderExtUnix` (which also has renamed methods).
- **Breaking:** The platform specific extensions for Windows `winit::platform::windows` have changed. All `HANDLE`-like types e.g. `HWND` and `HMENU` were converted from winapi types or `*mut c_void` to `isize`. This was done to be consistent with the type definitions in windows-sys and to not expose internal dependencies.
- The internal bindings to the [Windows API](https://docs.microsoft.com/en-us/windows/) were changed from the unofficial [winapi](https://github.com/retep998/winapi-rs) bindings to the official Microsoft [windows-sys](https://github.com/microsoft/windows-rs) bindings.
- On Wayland, fix resize and scale factor changes not being propagated properly.

# 0.26.1 (2022-01-05)
Expand Down
54 changes: 28 additions & 26 deletions Cargo.toml
Expand Up @@ -55,33 +55,35 @@ default_features = false
features = ["display_link"]

[target.'cfg(target_os = "windows")'.dependencies]
parking_lot = "0.11"
parking_lot = "0.12"

[target.'cfg(target_os = "windows")'.dependencies.winapi]
version = "0.3.9"
[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
version = "0.33"
features = [
"combaseapi",
"commctrl",
"dwmapi",
"errhandlingapi",
"imm",
"hidusage",
"libloaderapi",
"objbase",
"ole2",
"processthreadsapi",
"shellapi",
"shellscalingapi",
"shobjidl_core",
"unknwnbase",
"winbase",
"windowsx",
"winerror",
"wingdi",
"winnt",
"winuser",
"mmsystem",
"timeapi"
"Win32_Devices_HumanInterfaceDevice",
"Win32_Foundation",
"Win32_Globalization",
"Win32_Graphics_Dwm",
"Win32_Graphics_Gdi",
"Win32_Media",
"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 All @@ -91,7 +93,7 @@ sctk = { package = "smithay-client-toolkit", version = "0.15.1", default_feature
mio = { version = "0.8", features = ["os-ext"], optional = true }
x11-dl = { version = "2.18.5", optional = true }
percent-encoding = { version = "2.0", optional = true }
parking_lot = { version = "0.11.0", optional = true }
parking_lot = { version = "0.12.0", optional = true }
libc = "0.2.64"

[target.'cfg(target_arch = "wasm32")'.dependencies.web_sys]
Expand Down
39 changes: 23 additions & 16 deletions src/platform/windows.rs
@@ -1,11 +1,7 @@
#![cfg(target_os = "windows")]

use std::os::raw::c_void;
use std::path::Path;

use winapi::shared::minwindef::WORD;
use winapi::shared::windef::{HMENU, HWND};

use crate::{
dpi::PhysicalSize,
event::DeviceId,
Expand All @@ -15,6 +11,15 @@ use crate::{
window::{BadIcon, Icon, Theme, Window, WindowBuilder},
};

/// Window Handle type used by Win32 API
pub type HWND = isize;
/// Menu Handle type used by Win32 API
pub type HMENU = isize;
/// Monitor Handle type used by Win32 API
pub type HMONITOR = isize;
/// Instance Handle type used by Win32 API
pub type HINSTANCE = isize;

/// Additional methods on `EventLoop` that are specific to Windows.
pub trait EventLoopBuilderExtWindows {
/// Whether to allow the event loop to be created off of the main thread.
Expand Down Expand Up @@ -70,11 +75,11 @@ impl<T> EventLoopBuilderExtWindows for EventLoopBuilder<T> {
/// Additional methods on `Window` that are specific to Windows.
pub trait WindowExtWindows {
/// Returns the HINSTANCE of the window
fn hinstance(&self) -> *mut c_void;
fn hinstance(&self) -> HINSTANCE;
/// Returns the native handle that is used by this window.
///
/// The pointer will become invalid when the native window was destroyed.
fn hwnd(&self) -> *mut c_void;
fn hwnd(&self) -> HWND;

/// Enables or disables mouse and keyboard input to the specified window.
///
Expand All @@ -100,13 +105,13 @@ pub trait WindowExtWindows {

impl WindowExtWindows for Window {
#[inline]
fn hinstance(&self) -> *mut c_void {
self.window.hinstance() as *mut _
fn hinstance(&self) -> HINSTANCE {
self.window.hinstance()
}

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

#[inline]
Expand Down Expand Up @@ -150,10 +155,12 @@ pub trait WindowBuilderExtWindows {
///
/// Parent and menu are mutually exclusive; a child window cannot have a menu!
///
/// The menu must have been manually created beforehand with [`winapi::um::winuser::CreateMenu`] or similar.
/// The menu must have been manually created beforehand with [`CreateMenu`] or similar.
///
/// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how the menus look.
/// If you use this, it is recommended that you combine it with `with_theme(Some(Theme::Light))` to avoid a jarring effect.
///
/// [`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu
fn with_menu(self, menu: HMENU) -> WindowBuilder;

/// This sets `ICON_BIG`. A good ceiling here is 256x256.
Expand Down Expand Up @@ -224,7 +231,7 @@ pub trait MonitorHandleExtWindows {
fn native_id(&self) -> String;

/// Returns the handle of the monitor - `HMONITOR`.
fn hmonitor(&self) -> *mut c_void;
fn hmonitor(&self) -> HMONITOR;
}

impl MonitorHandleExtWindows for MonitorHandle {
Expand All @@ -234,8 +241,8 @@ impl MonitorHandleExtWindows for MonitorHandle {
}

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

Expand Down Expand Up @@ -273,7 +280,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 @@ -285,7 +292,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
88 changes: 40 additions & 48 deletions src/platform_impl/windows/dark_mode.rs
@@ -1,29 +1,35 @@
/// 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::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,
use std::{ffi::c_void, ptr};

use windows_sys::{
core::PCSTR,
Win32::{
Foundation::{BOOL, HWND, NTSTATUS, S_OK},
System::{
LibraryLoader::{GetProcAddress, LoadLibraryA},
SystemInformation::OSVERSIONINFOW,
},
UI::{
Accessibility::{HCF_HIGHCONTRASTON, HIGHCONTRASTA},
Controls::SetWindowTheme,
WindowsAndMessaging::{SystemParametersInfoA, SPI_GETHIGHCONTRAST},
},
},
um::{libloaderapi, uxtheme, winnt, winuser},
};

use crate::window::Theme;

use super::util;

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 @@ -32,9 +38,9 @@ lazy_static! {
szCSDVersion: [0; 128],
};

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

if NT_SUCCESS(status) && vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 {
if status >= 0 && vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 {
Some(vi.dwBuildNumber)
} else {
None
Expand All @@ -54,8 +60,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> = util::encode_wide("DarkMode_Explorer");
static ref LIGHT_THEME_NAME: Vec<u16> = util::encode_wide("");
}

/// Attempt to set a theme on a window, if necessary.
Expand All @@ -77,7 +83,7 @@ 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, theme_name, ptr::null()) };

if status == S_OK && set_dark_mode_for_window(hwnd, is_dark_mode) {
return theme;
Expand All @@ -103,8 +109,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,17 +121,17 @@ 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 = BOOL::from(is_dark_mode);

let mut data = WINDOWCOMPOSITIONATTRIBDATA {
Attrib: WCA_USEDARKMODECOLORS,
pvData: &mut is_dark_mode_bigbool as *mut _ as _,
cbData: std::mem::size_of_val(&is_dark_mode_bigbool) as _,
};

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

status != FALSE
status != false.into()
}
} else {
false
Expand All @@ -141,24 +147,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: PCSTR = 132 as PCSTR;

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

if module.is_null() {
if module == 0 {
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 +168,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(),
lpszDefaultScheme: 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,
)
};

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 != false.into() && util::has_flag(hc.dwFlags, HCF_HIGHCONTRASTON)
}

0 comments on commit b222dde

Please sign in to comment.