use super::api::*;
use crate::monitor::Size;
use std::{mem, ptr, slice};
pub const BASE_DPI: UINT = 96;
pub static WSTR_EMPTY: &[WCHAR] = &[0x00];
#[inline]
pub fn this_hinstance() -> HINSTANCE {
extern "system" {
static __ImageBase: [u8; 64];
}
(unsafe { &__ImageBase }) as *const [u8; 64] as HINSTANCE
}
pub unsafe fn error_string_repr(err: DWORD) -> String {
let mut buffer: *mut WCHAR = ptr::null_mut();
let buf_ptr = (&mut buffer as *mut LPWSTR) as LPWSTR;
let char_count = FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
ptr::null(),
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT).into(),
buf_ptr,
0,
ptr::null_mut(),
);
debug_assert_ne!(char_count, 0);
let mut message = Vec::new();
lpcwstr_to_str(buffer, &mut message);
let _ = LocalFree(buffer.cast());
String::from_utf8_lossy(&message).into_owned()
}
pub fn str_to_wide_null(src: &str, buffer: &mut Vec<WCHAR>) -> LPCWSTR {
if src.is_empty() || src.len() > c_int::max_value() as usize {
return WSTR_EMPTY.as_ptr()
}
unsafe {
let str_ptr: LPCSTR = src.as_ptr().cast();
let str_len = src.len() as c_int;
let req_buffer_size = MultiByteToWideChar(
CP_UTF8, 0,
str_ptr, str_len,
ptr::null_mut(), 0, ) as usize + 1;
buffer.clear();
buffer.reserve(req_buffer_size);
let chars_written = MultiByteToWideChar(
CP_UTF8, 0,
str_ptr, str_len,
buffer.as_mut_ptr(), req_buffer_size as c_int,
) as usize;
for x in slice::from_raw_parts_mut(buffer.as_mut_ptr(), chars_written) {
if *x == 0x00 {
*x = b' ' as WCHAR; }
}
*buffer.as_mut_ptr().add(chars_written) = 0x00;
buffer.set_len(req_buffer_size);
buffer.as_ptr()
}
}
pub fn lpcwstr_to_str(src: LPCWSTR, buffer: &mut Vec<u8>) {
buffer.clear();
unsafe {
if *src == 0x00 {
return
}
let count = WideCharToMultiByte(
CP_UTF8, 0,
src, -1, ptr::null_mut(),
0, ptr::null(), ptr::null_mut(),
);
buffer.reserve(count as usize);
let _ = WideCharToMultiByte(
CP_UTF8, 0,
src, -1, buffer.as_mut_ptr(),
count, ptr::null(), ptr::null_mut(),
);
buffer.set_len(count as usize - 1); }
}
pub unsafe fn is_windows_ver_or_greater(dl: &Win32DL, major: WORD, minor: WORD, sp_major: WORD) -> bool {
let mut osvi: OSVERSIONINFOEXW = mem::zeroed();
osvi.dwOSVersionInfoSize = mem::size_of_val(&osvi) as DWORD;
osvi.dwMajorVersion = major.into();
osvi.dwMinorVersion = minor.into();
osvi.wServicePackMajor = sp_major.into();
let mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR;
let mut cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL);
cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL);
cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
dl.RtlVerifyVersionInfo(&mut osvi, mask, cond) == Some(0)
}
pub unsafe fn is_win10_ver_or_greater(dl: &Win32DL, build: WORD) -> bool {
let mut osvi: OSVERSIONINFOEXW = mem::zeroed();
osvi.dwOSVersionInfoSize = mem::size_of_val(&osvi) as DWORD;
osvi.dwMajorVersion = 10;
osvi.dwMinorVersion = 0;
osvi.dwBuildNumber = build.into();
let mask = VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER;
let mut cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL);
cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL);
cond = VerSetConditionMask(cond, VER_BUILDNUMBER, VER_GREATER_EQUAL);
dl.RtlVerifyVersionInfo(&mut osvi, mask, cond) == Some(0)
}
pub unsafe fn set_close_button(hwnd: HWND, enabled: bool) {
let menu: HMENU = GetSystemMenu(hwnd, FALSE);
let flag = if enabled {
MF_BYCOMMAND | MF_ENABLED
} else {
MF_BYCOMMAND | MF_DISABLED | MF_GRAYED
};
let _ = EnableMenuItem(menu, SC_CLOSE as UINT, flag);
}
#[inline]
pub unsafe fn ping_window_frame(hwnd: HWND) {
const MASK: UINT = SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_FRAMECHANGED;
let _ = SetWindowPos(hwnd, ptr::null_mut(), 0, 0, 0, 0, MASK);
}
#[inline]
pub fn rect_to_size2d(rect: &RECT) -> (LONG, LONG) {
(rect.right - rect.left, rect.bottom - rect.top)
}
pub enum DpiMode {
Unsupported,
System,
PerMonitorV1,
PerMonitorV2,
}
pub struct Win32 {
pub dl: Win32DL,
pub dpi_mode: DpiMode,
pub at_least_vista: bool,
pub at_least_8_point_1: bool,
pub at_least_anniversary_update: bool,
pub at_least_creators_update: bool,
}
impl Win32 {
pub fn new() -> Self {
const VISTA_MAJ: WORD = (_WIN32_WINNT_VISTA >> 8) & 0xFF;
const VISTA_MIN: WORD = _WIN32_WINNT_VISTA & 0xFF;
const W81_MAJ: WORD = (_WIN32_WINNT_WINBLUE >> 8) & 0xFF;
const W81_MIN: WORD = _WIN32_WINNT_WINBLUE & 0xFF;
unsafe {
let dl = Win32DL::link();
let at_least_vista = is_windows_ver_or_greater(&dl, VISTA_MAJ, VISTA_MIN, 0);
let at_least_8_point_1 = is_windows_ver_or_greater(&dl, W81_MAJ, W81_MIN, 0);
let at_least_anniversary_update = is_win10_ver_or_greater(&dl, 14393);
let at_least_creators_update = is_win10_ver_or_greater(&dl, 15063);
let dpi_mode = if at_least_creators_update {
assert_eq!(dl.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2), Some(TRUE));
DpiMode::PerMonitorV2
} else if at_least_8_point_1 {
assert_eq!(dl.SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE), Some(S_OK));
DpiMode::PerMonitorV1
} else if at_least_vista {
assert_ne!(dl.SetProcessDPIAware().unwrap_or(FALSE), FALSE);
DpiMode::System
} else {
DpiMode::Unsupported
};
Self {
dl,
dpi_mode,
at_least_vista,
at_least_8_point_1,
at_least_anniversary_update,
at_least_creators_update,
}
}
}
pub unsafe fn adjust_window_for_dpi(&self, size: Size, style: DWORD, style_ex: DWORD, dpi: UINT) -> (LONG, LONG) {
let (width, height) = size.scale_if_logical(dpi as f64 / BASE_DPI as f64);
let mut window = RECT { left: 0, top: 0, bottom: height as LONG, right: width as LONG };
if match self.dpi_mode {
DpiMode::PerMonitorV1 if self.at_least_anniversary_update => true,
DpiMode::PerMonitorV2 => true,
_ => false,
} {
let _ = self.dl.AdjustWindowRectExForDpi(&mut window, style, FALSE, style_ex, dpi);
} else {
let _ = AdjustWindowRectEx(&mut window, style, FALSE, style_ex);
}
rect_to_size2d(&window)
}
}
impl Default for Win32 {
#[inline]
fn default() -> Self {
Self::new()
}
}