1use std::{
2 mem::size_of,
3 sync::atomic::{AtomicUsize, Ordering},
4};
5
6use once_cell::sync::Lazy;
7use windows_sys::Win32::{
8 Foundation::*,
9 Graphics::Dwm::*,
10 System::LibraryLoader::{GetModuleHandleA, GetProcAddress},
11 UI::Controls::MARGINS,
12};
13
14#[link(name = "ntdll")]
15extern "system" {
16 fn RtlGetVersion(os_ver_info: *mut OsVersionInfo) -> u32;
17}
18
19#[derive(Debug, Clone, Copy)]
20#[repr(C)]
21struct OsVersionInfo {
22 os_version_info_size: u32,
23 major_version: u32,
24 minor_version: u32,
25 build_number: u32,
26 platform_id: u32,
27 csd_version: [u16; 128],
28}
29
30fn build_no() -> u32 {
31 let mut os_ver_info: OsVersionInfo = unsafe { std::mem::zeroed() };
32 os_ver_info.os_version_info_size = size_of::<OsVersionInfo>() as _;
33 unsafe { RtlGetVersion(&mut os_ver_info) };
34 os_ver_info.build_number
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38#[repr(C)]
39#[allow(dead_code)]
40enum WindowCompositionAttribute {
41 Undefined = 0,
42 NcrenderingEnabled = 1,
43 NcrenderingPolicy = 2,
44 TransitionsForcedisabled = 3,
45 AllowNcpaint = 4,
46 CaptionButtonBounds = 5,
47 NonclientRtlLayout = 6,
48 ForceIconicRepresentation = 7,
49 ExtendedFrameBounds = 8,
50 HasIconicBitmap = 9,
51 ThemeAttributes = 10,
52 NcrenderingExiled = 11,
53 Ncadornmentinfo = 12,
54 ExcludedFromLivepreview = 13,
55 VideoOverlayActive = 14,
56 ForceActivewindowAppearance = 15,
57 DisallowPeek = 16,
58 Cloak = 17,
59 Cloaked = 18,
60 AccentPolicy = 19,
61 FreezeRepresentation = 20,
62 EverUncloaked = 21,
63 VisualOwner = 22,
64 Holographic = 23,
65 ExcludedFromDda = 24,
66 Passiveupdatemode = 25,
67 Usedarkmodecolors = 26,
68 Last = 27,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72#[repr(C)]
73struct WindowCompositionAttributeData {
74 attrib: WindowCompositionAttribute,
75 pv_data: *mut std::ffi::c_void,
76 cb_data: usize,
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80#[allow(dead_code)]
81#[repr(C)]
82enum AccentState {
83 Disabled = 0,
84 EnableGradient = 1,
85 EnableTransparentgradient = 2,
86 EnableBlurbehind = 3,
87 EnableAcrylicblurbehind = 4,
88 EnableHostbackdrop = 5,
89 InvalidState = 6,
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93#[repr(C)]
94struct AccentPolicy {
95 accent_state: AccentState,
96 accent_flags: u32,
97 gradient_color: u32,
98 animation_id: u32,
99}
100
101static SET_WINDOW_COMPOSITION_ATTRIBUTE: Lazy<
102 extern "system" fn(
103 hwnd: HWND,
104 wnd_comp_attrib_data: *const WindowCompositionAttributeData,
105 ) -> BOOL,
106> = Lazy::new(|| unsafe {
107 let user32 = GetModuleHandleA(b"user32.dll\0".as_ptr() as _);
108 std::mem::transmute(GetProcAddress(
109 user32,
110 b"SetWindowCompositionAttribute\0".as_ptr() as _,
111 ))
112});
113
114unsafe fn set_window_composition_attribute<T>(
115 hwnd: HWND,
116 attrib: WindowCompositionAttribute,
117 data: T,
118) -> bool {
119 let window_composition_attribute = WindowCompositionAttributeData {
120 attrib,
121 pv_data: &data as *const _ as _,
122 cb_data: size_of::<T>(),
123 };
124 (*SET_WINDOW_COMPOSITION_ATTRIBUTE)(hwnd, &window_composition_attribute) != 0
125}
126unsafe fn dwm_set_window_attribute<T>(hwnd: HWND, attrib: DWMWINDOWATTRIBUTE, data: T) -> bool {
127 DwmSetWindowAttribute(hwnd, attrib, &data as *const _ as _, size_of::<T>() as _) == 0
128}
129
130static LAST_EFFECT: AtomicUsize = AtomicUsize::new(Effect::None as _);
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
133pub enum Effect {
134 None,
135 Solid,
136 Transparent,
137 Aero,
138 Acrylic,
139 Mica,
140 Tabbed,
141}
142
143impl From<Effect> for AccentState {
144 fn from(effect: Effect) -> Self {
145 match effect {
146 Effect::None => AccentState::Disabled,
147 Effect::Solid => AccentState::EnableGradient,
148 Effect::Transparent => AccentState::EnableTransparentgradient,
149 Effect::Aero => AccentState::EnableBlurbehind,
150 Effect::Acrylic => AccentState::EnableAcrylicblurbehind,
151 Effect::Mica => AccentState::EnableHostbackdrop,
152 Effect::Tabbed => AccentState::InvalidState,
153 }
154 }
155}
156
157const NEGATIVE_ONE_MARGIN: MARGINS = MARGINS {
158 cxLeftWidth: -1,
159 cxRightWidth: -1,
160 cyBottomHeight: -1,
161 cyTopHeight: -1,
162};
163
164const RESTORATIVE_MARGIN: MARGINS = MARGINS {
165 cxLeftWidth: 0,
166 cxRightWidth: 0,
167 cyBottomHeight: 0,
168 cyTopHeight: 1,
169};
170
171const DWMWA_NEW_BACKDROP_MODE: DWMWINDOWATTRIBUTE = 38;
179
180const DWMWA_USE_MICA_BACKDROP: DWMWINDOWATTRIBUTE = 1029;
184
185pub unsafe fn set_effect(
197 hwnd: HWND,
198 effect: Effect,
199 dark_mode: bool,
200 color: Option<(u8, u8, u8, u8)>,
201) {
202 set_window_composition_attribute(
205 hwnd,
206 WindowCompositionAttribute::AccentPolicy,
207 AccentPolicy {
208 accent_state: AccentState::Disabled,
209 accent_flags: 2,
210 gradient_color: 0,
211 animation_id: 0,
212 },
213 );
214
215 let windows_build_no = build_no();
216
217 if windows_build_no >= 22523 && effect > Effect::Aero {
221 DwmExtendFrameIntoClientArea(hwnd, &NEGATIVE_ONE_MARGIN);
222 dwm_set_window_attribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, BOOL::from(dark_mode));
223 dwm_set_window_attribute(
224 hwnd,
225 DWMWA_NEW_BACKDROP_MODE,
226 match effect {
227 Effect::Acrylic => 3i32,
228 Effect::Mica => 2,
229 Effect::Tabbed => 4,
230 _ => unreachable!(),
231 },
232 );
233 } else if effect == Effect::Mica {
234 if windows_build_no >= 22000 {
236 DwmExtendFrameIntoClientArea(hwnd, &NEGATIVE_ONE_MARGIN);
240 dwm_set_window_attribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, BOOL::from(dark_mode));
241 dwm_set_window_attribute(hwnd, DWMWA_USE_MICA_BACKDROP, BOOL::from(true));
242 }
243 } else {
244 if (windows_build_no >= 22000
248 && LAST_EFFECT.load(Ordering::Relaxed) == Effect::Mica as usize)
249 || (windows_build_no >= 22523
250 && LAST_EFFECT.load(Ordering::Relaxed) > Effect::Aero as usize)
251 {
252 DwmExtendFrameIntoClientArea(hwnd, &RESTORATIVE_MARGIN);
258 dwm_set_window_attribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, BOOL::from(false));
259 dwm_set_window_attribute(hwnd, DWMWA_USE_MICA_BACKDROP, BOOL::from(false));
260 }
261 set_window_composition_attribute(
262 hwnd,
263 WindowCompositionAttribute::AccentPolicy,
264 AccentPolicy {
265 accent_state: AccentState::from(effect),
266 accent_flags: 2,
267 gradient_color: color
268 .map(|(r, g, b, a)| {
269 (a as u32) << 24 | (b as u32) << 16 | (g as u32) << 8 | r as u32
270 })
271 .unwrap(),
272 animation_id: 0,
273 },
274 );
275 }
276
277 LAST_EFFECT.store(effect as _, Ordering::Relaxed);
278}