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},
},
},
};
pub fn encode_wide<S: AsRef<std::ffi::OsStr>>(string: S) -> Vec<u16> {
std::os::windows::prelude::OsStrExt::encode_wide(string.as_ref())
.chain(std::iter::once(0))
.collect()
}
#[allow(non_snake_case)]
pub fn LOWORD(dword: u32) -> u16 {
(dword & 0xFFFF) as u16
}
pub fn decode_wide(w_str: *mut u16) -> String {
let len = unsafe { windows_sys::Win32::Globalization::lstrlenW(w_str) } as usize;
let w_str_slice = unsafe { std::slice::from_raw_parts(w_str, len) };
String::from_utf16_lossy(w_str_slice)
}
#[derive(Clone)]
#[repr(transparent)]
pub struct Accel(pub ACCEL);
impl std::fmt::Debug for Accel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ACCEL")
.field("key", &self.0.key)
.field("cmd", &self.0.cmd)
.field("fVirt", &self.0.fVirt)
.finish()
}
}
impl Deref for Accel {
type Target = ACCEL;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Accel {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub fn get_instance_handle() -> windows_sys::Win32::Foundation::HMODULE {
extern "C" {
static __ImageBase: windows_sys::Win32::System::SystemServices::IMAGE_DOS_HEADER;
}
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'));
let module = unsafe { LoadLibraryW(library.as_ptr()) };
if module.is_null() {
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 GetDpiForWindow = unsafe extern "system" fn(hwnd: HWND) -> u32;
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_WINDOW: Lazy<Option<GetDpiForWindow>> =
Lazy::new(|| get_function!("user32.dll", GetDpiForWindow));
static GET_DPI_FOR_MONITOR: Lazy<Option<GetDpiForMonitor>> =
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(GetDpiForWindow) = *GET_DPI_FOR_WINDOW {
match GetDpiForWindow(hwnd) {
0 => BASE_DPI, #[allow(clippy::unnecessary_cast)]
dpi => dpi as u32,
}
} else if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR {
let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if monitor.is_null() {
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.is_null() {
return BASE_DPI;
}
if IsProcessDPIAware() == 1 {
GetDeviceCaps(hdc, LOGPIXELSX as _) as u32
} else {
BASE_DPI
}
}
}