#![allow(non_snake_case)]
use once_cell::sync::Lazy;
use windows::{
core::{s, w, PCSTR, PSTR},
Win32::{
Foundation::{BOOL, HANDLE, HMODULE, HWND},
System::LibraryLoader::*,
UI::{Accessibility::*, WindowsAndMessaging::*},
},
};
use std::ffi::c_void;
use crate::window::Theme;
static HUXTHEME: Lazy<isize> =
Lazy::new(|| unsafe { LoadLibraryA(s!("uxtheme.dll")).unwrap_or_default().0 as _ });
static WIN10_BUILD_VERSION: Lazy<Option<u32>> = Lazy::new(|| {
let version = windows_version::OsVersion::current();
if version.major == 10 && version.minor == 0 {
Some(version.build)
} else {
None
}
});
static DARK_MODE_SUPPORTED: Lazy<bool> = Lazy::new(|| {
match *WIN10_BUILD_VERSION {
Some(v) => v >= 17763,
None => false,
}
});
pub fn allow_dark_mode_for_app(is_dark_mode: bool) {
const UXTHEME_ALLOWDARKMODEFORAPP_ORDINAL: u16 = 135;
type AllowDarkModeForApp = unsafe extern "system" fn(bool) -> bool;
static ALLOW_DARK_MODE_FOR_APP: Lazy<Option<AllowDarkModeForApp>> = Lazy::new(|| unsafe {
if HMODULE(*HUXTHEME as _).is_invalid() {
return None;
}
GetProcAddress(
HMODULE(*HUXTHEME as _),
PCSTR::from_raw(UXTHEME_ALLOWDARKMODEFORAPP_ORDINAL as usize as *mut _),
)
.map(|handle| std::mem::transmute(handle))
});
#[repr(C)]
enum PreferredAppMode {
Default,
AllowDark,
}
const UXTHEME_SETPREFERREDAPPMODE_ORDINAL: u16 = 135;
type SetPreferredAppMode = unsafe extern "system" fn(PreferredAppMode) -> PreferredAppMode;
static SET_PREFERRED_APP_MODE: Lazy<Option<SetPreferredAppMode>> = Lazy::new(|| unsafe {
if HMODULE(*HUXTHEME as _).is_invalid() {
return None;
}
GetProcAddress(
HMODULE(*HUXTHEME as _),
PCSTR::from_raw(UXTHEME_SETPREFERREDAPPMODE_ORDINAL as usize as *mut _),
)
.map(|handle| std::mem::transmute(handle))
});
if let Some(ver) = *WIN10_BUILD_VERSION {
if ver < 18362 {
if let Some(_allow_dark_mode_for_app) = *ALLOW_DARK_MODE_FOR_APP {
unsafe { _allow_dark_mode_for_app(is_dark_mode) };
}
} else if let Some(_set_preferred_app_mode) = *SET_PREFERRED_APP_MODE {
let mode = if is_dark_mode {
PreferredAppMode::AllowDark
} else {
PreferredAppMode::Default
};
unsafe { _set_preferred_app_mode(mode) };
}
}
refresh_immersive_color_policy_state();
}
fn refresh_immersive_color_policy_state() {
const UXTHEME_REFRESHIMMERSIVECOLORPOLICYSTATE_ORDINAL: u16 = 104;
type RefreshImmersiveColorPolicyState = unsafe extern "system" fn();
static REFRESH_IMMERSIVE_COLOR_POLICY_STATE: Lazy<Option<RefreshImmersiveColorPolicyState>> =
Lazy::new(|| unsafe {
if HMODULE(*HUXTHEME as _).is_invalid() {
return None;
}
GetProcAddress(
HMODULE(*HUXTHEME as _),
PCSTR::from_raw(UXTHEME_REFRESHIMMERSIVECOLORPOLICYSTATE_ORDINAL as usize as *mut _),
)
.map(|handle| std::mem::transmute(handle))
});
if let Some(_refresh_immersive_color_policy_state) = *REFRESH_IMMERSIVE_COLOR_POLICY_STATE {
unsafe { _refresh_immersive_color_policy_state() }
}
}
pub fn try_window_theme(hwnd: HWND, preferred_theme: Option<Theme>) -> Theme {
if *DARK_MODE_SUPPORTED {
let is_dark_mode = match preferred_theme {
Some(theme) => theme == Theme::Dark,
None => should_use_dark_mode(),
};
let theme = match is_dark_mode {
true => Theme::Dark,
false => Theme::Light,
};
refresh_titlebar_theme_color(hwnd, is_dark_mode);
theme
} else {
Theme::Light
}
}
pub fn allow_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) {
const UXTHEME_ALLOWDARKMODEFORWINDOW_ORDINAL: u16 = 133;
type AllowDarkModeForWindow = unsafe extern "system" fn(HWND, bool) -> bool;
static ALLOW_DARK_MODE_FOR_WINDOW: Lazy<Option<AllowDarkModeForWindow>> = Lazy::new(|| unsafe {
if HMODULE(*HUXTHEME as _).is_invalid() {
return None;
}
GetProcAddress(
HMODULE(*HUXTHEME as _),
PCSTR::from_raw(UXTHEME_ALLOWDARKMODEFORWINDOW_ORDINAL as usize as *mut _),
)
.map(|handle| std::mem::transmute(handle))
});
if *DARK_MODE_SUPPORTED {
if let Some(_allow_dark_mode_for_window) = *ALLOW_DARK_MODE_FOR_WINDOW {
unsafe { _allow_dark_mode_for_window(hwnd, is_dark_mode) };
}
}
}
type SetWindowCompositionAttribute =
unsafe extern "system" fn(HWND, *mut WINDOWCOMPOSITIONATTRIBDATA) -> BOOL;
static SET_WINDOW_COMPOSITION_ATTRIBUTE: Lazy<Option<SetWindowCompositionAttribute>> =
Lazy::new(|| get_function!("user32.dll", SetWindowCompositionAttribute));
type WINDOWCOMPOSITIONATTRIB = u32;
const WCA_USEDARKMODECOLORS: WINDOWCOMPOSITIONATTRIB = 26;
#[repr(C)]
struct WINDOWCOMPOSITIONATTRIBDATA {
Attrib: WINDOWCOMPOSITIONATTRIB,
pvData: *mut c_void,
cbData: usize,
}
fn refresh_titlebar_theme_color(hwnd: HWND, is_dark_mode: bool) {
let mut is_dark_mode_bigbool: i32 = is_dark_mode.into();
if let Some(ver) = *WIN10_BUILD_VERSION {
if ver < 18362 {
unsafe {
let _ = SetPropW(
hwnd,
w!("UseImmersiveDarkModeColors"),
HANDLE(&mut is_dark_mode_bigbool as *mut _ as _),
);
}
} else if let Some(set_window_composition_attribute) = *SET_WINDOW_COMPOSITION_ATTRIBUTE {
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 _ = unsafe { set_window_composition_attribute(hwnd, &mut data as *mut _) };
}
}
}
fn should_use_dark_mode() -> bool {
should_apps_use_dark_mode() && !is_high_contrast()
}
fn should_apps_use_dark_mode() -> bool {
const UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL: u16 = 132;
type ShouldAppsUseDarkMode = unsafe extern "system" fn() -> bool;
static SHOULD_APPS_USE_DARK_MODE: Lazy<Option<ShouldAppsUseDarkMode>> = Lazy::new(|| unsafe {
if HMODULE(*HUXTHEME as _).is_invalid() {
return None;
}
GetProcAddress(
HMODULE(*HUXTHEME as _),
PCSTR::from_raw(UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL as usize as *mut _),
)
.map(|handle| std::mem::transmute(handle))
});
SHOULD_APPS_USE_DARK_MODE
.map(|should_apps_use_dark_mode| unsafe { (should_apps_use_dark_mode)() })
.unwrap_or(false)
}
fn is_high_contrast() -> bool {
const HCF_HIGHCONTRASTON: u32 = 1;
let mut hc = HIGHCONTRASTA {
cbSize: 0,
dwFlags: Default::default(),
lpszDefaultScheme: PSTR::null(),
};
let ok = unsafe {
SystemParametersInfoA(
SPI_GETHIGHCONTRAST,
std::mem::size_of_val(&hc) as _,
Some(&mut hc as *mut _ as _),
Default::default(),
)
};
ok.is_ok() && (HCF_HIGHCONTRASTON & hc.dwFlags.0) != 0
}