use std::{
mem::size_of,
sync::atomic::{AtomicUsize, Ordering},
};
use once_cell::sync::Lazy;
use windows_sys::Win32::{
Foundation::*,
Graphics::Dwm::*,
System::LibraryLoader::{GetModuleHandleA, GetProcAddress},
UI::Controls::MARGINS,
};
#[link(name = "ntdll")]
extern "system" {
fn RtlGetVersion(os_ver_info: *mut OsVersionInfo) -> u32;
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct OsVersionInfo {
os_version_info_size: u32,
major_version: u32,
minor_version: u32,
build_number: u32,
platform_id: u32,
csd_version: [u16; 128],
}
fn build_no() -> u32 {
let mut os_ver_info: OsVersionInfo = unsafe { std::mem::zeroed() };
os_ver_info.os_version_info_size = size_of::<OsVersionInfo>() as _;
unsafe { RtlGetVersion(&mut os_ver_info) };
os_ver_info.build_number
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
#[allow(dead_code)]
enum WindowCompositionAttribute {
Undefined = 0,
NcrenderingEnabled = 1,
NcrenderingPolicy = 2,
TransitionsForcedisabled = 3,
AllowNcpaint = 4,
CaptionButtonBounds = 5,
NonclientRtlLayout = 6,
ForceIconicRepresentation = 7,
ExtendedFrameBounds = 8,
HasIconicBitmap = 9,
ThemeAttributes = 10,
NcrenderingExiled = 11,
Ncadornmentinfo = 12,
ExcludedFromLivepreview = 13,
VideoOverlayActive = 14,
ForceActivewindowAppearance = 15,
DisallowPeek = 16,
Cloak = 17,
Cloaked = 18,
AccentPolicy = 19,
FreezeRepresentation = 20,
EverUncloaked = 21,
VisualOwner = 22,
Holographic = 23,
ExcludedFromDda = 24,
Passiveupdatemode = 25,
Usedarkmodecolors = 26,
Last = 27,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
struct WindowCompositionAttributeData {
attrib: WindowCompositionAttribute,
pv_data: *mut std::ffi::c_void,
cb_data: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
#[repr(C)]
enum AccentState {
Disabled = 0,
EnableGradient = 1,
EnableTransparentgradient = 2,
EnableBlurbehind = 3,
EnableAcrylicblurbehind = 4,
EnableHostbackdrop = 5,
InvalidState = 6,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
struct AccentPolicy {
accent_state: AccentState,
accent_flags: u32,
gradient_color: u32,
animation_id: u32,
}
static SET_WINDOW_COMPOSITION_ATTRIBUTE: Lazy<
extern "system" fn(
hwnd: HWND,
wnd_comp_attrib_data: *const WindowCompositionAttributeData,
) -> BOOL,
> = Lazy::new(|| unsafe {
let user32 = GetModuleHandleA(b"user32.dll\0".as_ptr() as _);
std::mem::transmute(GetProcAddress(
user32,
b"SetWindowCompositionAttribute\0".as_ptr() as _,
))
});
unsafe fn set_window_composition_attribute<T>(
hwnd: HWND,
attrib: WindowCompositionAttribute,
data: T,
) -> bool {
let window_composition_attribute = WindowCompositionAttributeData {
attrib,
pv_data: &data as *const _ as _,
cb_data: size_of::<T>(),
};
(*SET_WINDOW_COMPOSITION_ATTRIBUTE)(hwnd, &window_composition_attribute) != 0
}
unsafe fn dwm_set_window_attribute<T>(hwnd: HWND, attrib: DWMWINDOWATTRIBUTE, data: T) -> bool {
DwmSetWindowAttribute(hwnd, attrib, &data as *const _ as _, size_of::<T>() as _) == 0
}
static LAST_EFFECT: AtomicUsize = AtomicUsize::new(Effect::None as _);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
pub enum Effect {
None,
Solid,
Transparent,
Aero,
Acrylic,
Mica,
Tabbed,
}
impl From<Effect> for AccentState {
fn from(effect: Effect) -> Self {
match effect {
Effect::None => AccentState::Disabled,
Effect::Solid => AccentState::EnableGradient,
Effect::Transparent => AccentState::EnableTransparentgradient,
Effect::Aero => AccentState::EnableBlurbehind,
Effect::Acrylic => AccentState::EnableAcrylicblurbehind,
Effect::Mica => AccentState::EnableHostbackdrop,
Effect::Tabbed => AccentState::InvalidState,
}
}
}
const NEGATIVE_ONE_MARGIN: MARGINS = MARGINS {
cxLeftWidth: -1,
cxRightWidth: -1,
cyBottomHeight: -1,
cyTopHeight: -1,
};
const RESTORATIVE_MARGIN: MARGINS = MARGINS {
cxLeftWidth: 0,
cxRightWidth: 0,
cyBottomHeight: 0,
cyTopHeight: 1,
};
const DWMWA_NEW_BACKDROP_MODE: DWMWINDOWATTRIBUTE = 38;
const DWMWA_USE_MICA_BACKDROP: DWMWINDOWATTRIBUTE = 1029;
pub unsafe fn set_effect(
hwnd: HWND,
effect: Effect,
dark_mode: bool,
color: Option<(u8, u8, u8, u8)>,
) {
set_window_composition_attribute(
hwnd,
WindowCompositionAttribute::AccentPolicy,
AccentPolicy {
accent_state: AccentState::Disabled,
accent_flags: 2,
gradient_color: 0,
animation_id: 0,
},
);
let windows_build_no = build_no();
if windows_build_no >= 22523 && effect > Effect::Aero {
DwmExtendFrameIntoClientArea(hwnd, &NEGATIVE_ONE_MARGIN);
dwm_set_window_attribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, BOOL::from(dark_mode));
dwm_set_window_attribute(
hwnd,
DWMWA_NEW_BACKDROP_MODE,
match effect {
Effect::Acrylic => 3i32,
Effect::Mica => 2,
Effect::Tabbed => 4,
_ => unreachable!(),
},
);
} else if effect == Effect::Mica {
if windows_build_no >= 22000 {
DwmExtendFrameIntoClientArea(hwnd, &NEGATIVE_ONE_MARGIN);
dwm_set_window_attribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, BOOL::from(dark_mode));
dwm_set_window_attribute(hwnd, DWMWA_USE_MICA_BACKDROP, BOOL::from(true));
}
} else {
if (windows_build_no >= 22000
&& LAST_EFFECT.load(Ordering::Relaxed) == Effect::Mica as usize)
|| (windows_build_no >= 22523
&& LAST_EFFECT.load(Ordering::Relaxed) > Effect::Aero as usize)
{
DwmExtendFrameIntoClientArea(hwnd, &RESTORATIVE_MARGIN);
dwm_set_window_attribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, BOOL::from(false));
dwm_set_window_attribute(hwnd, DWMWA_USE_MICA_BACKDROP, BOOL::from(false));
}
set_window_composition_attribute(
hwnd,
WindowCompositionAttribute::AccentPolicy,
AccentPolicy {
accent_state: AccentState::from(effect),
accent_flags: 2,
gradient_color: color
.map(|(r, g, b, a)| {
(a as u32) << 24 | (b as u32) << 16 | (g as u32) << 8 | r as u32
})
.unwrap(),
animation_id: 0,
},
);
}
LAST_EFFECT.store(effect as _, Ordering::Relaxed);
}