Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adopt windows-sys #2057

Merged
merged 3 commits into from Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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);
clemenswasser marked this conversation as resolved.
Show resolved Hide resolved

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)
}