#![allow(non_snake_case)]
use core::ffi::c_void;
use super::{defaults, InputMetrics, TextRenderingHints, SubpixelType};
use crate::props::basic::color::{ColorU, OptionColorU};
extern "system" {
fn LoadLibraryA(name: *const u8) -> *mut c_void;
fn GetProcAddress(module: *mut c_void, name: *const u8) -> *mut c_void;
fn FreeLibrary(module: *mut c_void) -> i32;
}
const SM_CXDOUBLECLK: i32 = 36;
const SM_CYDOUBLECLK: i32 = 37;
const SM_CXDRAG: i32 = 68;
const SM_CXVSCROLL: i32 = 2;
const SPI_GETFONTSMOOTHING: u32 = 0x004A;
const SPI_GETFONTSMOOTHINGTYPE: u32 = 0x200A;
const SPI_GETWHEELSCROLLLINES: u32 = 0x0068;
const SPI_GETMOUSEHOVERTIME: u32 = 0x0066;
const SPI_GETCLIENTAREAANIMATION: u32 = 0x1042;
const SPI_GETKEYBOARDCUES: u32 = 0x100A;
const SPI_GETBEEP: u32 = 0x0001;
const SPI_GETCARETWIDTH: u32 = 0x2006;
const FE_FONTSMOOTHINGSTANDARD: u32 = 1;
const FE_FONTSMOOTHINGCLEARTYPE: u32 = 2;
type FnGetSystemMetrics = unsafe extern "system" fn(i32) -> i32;
type FnGetDoubleClickTime = unsafe extern "system" fn() -> u32;
type FnGetCaretBlinkTime = unsafe extern "system" fn() -> u32;
type FnSystemParametersInfoW = unsafe extern "system" fn(u32, u32, *mut c_void, u32) -> i32;
type FnGetSysColor = unsafe extern "system" fn(i32) -> u32;
type FnDwmGetColorizationColor = unsafe extern "system" fn(*mut u32, *mut i32) -> i32;
struct User32 {
GetSystemMetrics: FnGetSystemMetrics,
GetDoubleClickTime: FnGetDoubleClickTime,
GetCaretBlinkTime: FnGetCaretBlinkTime,
SystemParametersInfoW: FnSystemParametersInfoW,
GetSysColor: FnGetSysColor,
_handle: *mut c_void,
}
impl User32 {
fn load() -> Option<Self> {
unsafe {
let h = LoadLibraryA(b"User32.dll\0".as_ptr());
if h.is_null() { return None; }
macro_rules! sym {
($name:ident, $ty:ty) => {{
let p = GetProcAddress(h, concat!(stringify!($name), "\0").as_ptr());
if p.is_null() { FreeLibrary(h); return None; }
core::mem::transmute::<_, $ty>(p)
}};
}
Some(User32 {
GetSystemMetrics: sym!(GetSystemMetrics, FnGetSystemMetrics),
GetDoubleClickTime: sym!(GetDoubleClickTime, FnGetDoubleClickTime),
GetCaretBlinkTime: sym!(GetCaretBlinkTime, FnGetCaretBlinkTime),
SystemParametersInfoW: sym!(SystemParametersInfoW, FnSystemParametersInfoW),
GetSysColor: sym!(GetSysColor, FnGetSysColor),
_handle: h,
})
}
}
}
impl Drop for User32 {
fn drop(&mut self) { unsafe { FreeLibrary(self._handle); } }
}
struct Dwmapi {
DwmGetColorizationColor: FnDwmGetColorizationColor,
_handle: *mut c_void,
}
impl Dwmapi {
fn load() -> Option<Self> {
unsafe {
let h = LoadLibraryA(b"Dwmapi.dll\0".as_ptr());
if h.is_null() { return None; }
let p = GetProcAddress(h, b"DwmGetColorizationColor\0".as_ptr());
if p.is_null() { FreeLibrary(h); return None; }
Some(Dwmapi {
DwmGetColorizationColor: core::mem::transmute(p),
_handle: h,
})
}
}
}
impl Drop for Dwmapi {
fn drop(&mut self) { unsafe { FreeLibrary(self._handle); } }
}
fn color_from_sys(u32: &FnGetSysColor, index: i32) -> ColorU {
let c = unsafe { u32(index) };
let r = (c & 0xFF) as u8;
let g = ((c >> 8) & 0xFF) as u8;
let b = ((c >> 16) & 0xFF) as u8;
ColorU::new_rgb(r, g, b)
}
pub(super) fn discover() -> super::SystemStyle {
let u32_lib = match User32::load() {
Some(l) => l,
None => return defaults::windows_11_light(),
};
let mut style = defaults::windows_11_light();
unsafe {
style.input = InputMetrics {
double_click_time_ms: (u32_lib.GetDoubleClickTime)(),
double_click_distance_px: (u32_lib.GetSystemMetrics)(SM_CXDOUBLECLK) as f32,
drag_threshold_px: (u32_lib.GetSystemMetrics)(SM_CXDRAG) as f32,
caret_blink_rate_ms: (u32_lib.GetCaretBlinkTime)(),
caret_width_px: {
let mut w: u32 = 1;
(u32_lib.SystemParametersInfoW)(
SPI_GETCARETWIDTH, 0,
&mut w as *mut u32 as *mut c_void, 0,
);
w as f32
},
wheel_scroll_lines: {
let mut lines: u32 = 3;
(u32_lib.SystemParametersInfoW)(
SPI_GETWHEELSCROLLLINES, 0,
&mut lines as *mut u32 as *mut c_void, 0,
);
lines
},
hover_time_ms: {
let mut hover: u32 = 400;
(u32_lib.SystemParametersInfoW)(
SPI_GETMOUSEHOVERTIME, 0,
&mut hover as *mut u32 as *mut c_void, 0,
);
hover
},
};
style.colors.window_background = OptionColorU::Some(color_from_sys(&u32_lib.GetSysColor, 5));
style.colors.text = OptionColorU::Some(color_from_sys(&u32_lib.GetSysColor, 8));
style.colors.selection_background = OptionColorU::Some(color_from_sys(&u32_lib.GetSysColor, 13));
style.colors.selection_text = OptionColorU::Some(color_from_sys(&u32_lib.GetSysColor, 14));
style.colors.button_face = OptionColorU::Some(color_from_sys(&u32_lib.GetSysColor, 15));
style.colors.button_text = OptionColorU::Some(color_from_sys(&u32_lib.GetSysColor, 18));
style.colors.disabled_text = OptionColorU::Some(color_from_sys(&u32_lib.GetSysColor, 17));
{
let mut smoothing: i32 = 0;
(u32_lib.SystemParametersInfoW)(
SPI_GETFONTSMOOTHING, 0,
&mut smoothing as *mut i32 as *mut c_void, 0,
);
let mut smooth_type: u32 = 0;
(u32_lib.SystemParametersInfoW)(
SPI_GETFONTSMOOTHINGTYPE, 0,
&mut smooth_type as *mut u32 as *mut c_void, 0,
);
style.text_rendering = TextRenderingHints {
font_smoothing_enabled: smoothing != 0,
subpixel_type: if smooth_type == FE_FONTSMOOTHINGCLEARTYPE {
SubpixelType::Rgb } else {
SubpixelType::None
},
font_smoothing_gamma: 1000,
increased_contrast: false,
};
}
if let Some(dwm) = Dwmapi::load() {
let mut colorization: u32 = 0;
let mut opaque_blend: i32 = 0;
let hr = (dwm.DwmGetColorizationColor)(&mut colorization, &mut opaque_blend);
if hr >= 0 {
let a = ((colorization >> 24) & 0xFF) as u8;
let r = ((colorization >> 16) & 0xFF) as u8;
let g = ((colorization >> 8) & 0xFF) as u8;
let b = ( colorization & 0xFF) as u8;
style.colors.accent = OptionColorU::Some(ColorU::new(r, g, b, a));
}
}
if let Some(ref bg) = style.colors.window_background.as_option() {
let luma = (bg.r as u16 + bg.g as u16 + bg.b as u16) / 3;
if luma < 128 {
style.theme = super::Theme::Dark;
}
}
{
let mut anim: i32 = 1;
(u32_lib.SystemParametersInfoW)(
SPI_GETCLIENTAREAANIMATION, 0,
&mut anim as *mut i32 as *mut c_void, 0,
);
let mut kb_cues: i32 = 0;
(u32_lib.SystemParametersInfoW)(
SPI_GETKEYBOARDCUES, 0,
&mut kb_cues as *mut i32 as *mut c_void, 0,
);
style.animation = super::AnimationMetrics {
animations_enabled: anim != 0,
animation_duration_factor: 1.0,
focus_indicator_behavior: if kb_cues != 0 {
super::FocusBehavior::AlwaysVisible
} else {
super::FocusBehavior::KeyboardOnly
},
};
if anim == 0 {
style.prefers_reduced_motion =
crate::dynamic_selector::BoolCondition::True;
style.accessibility.prefers_reduced_motion = true;
}
}
{
let mut beep: i32 = 1;
(u32_lib.SystemParametersInfoW)(
SPI_GETBEEP, 0,
&mut beep as *mut i32 as *mut c_void, 0,
);
style.audio = super::AudioMetrics {
event_sounds_enabled: beep != 0,
input_feedback_sounds_enabled: false,
};
}
}
style.platform = super::Platform::Windows;
style
}