//! On Windows 10 a blurred window's acrylic clears while it moves or sizes: that DWM samples the blur again at each step, and the window trails the pointer by seconds. use std::sync::atomic::{AtomicIsize, Ordering}; use anyhow::{Context as _, bail}; use gpui::Window; use raw_window_handle::{HasWindowHandle, RawWindowHandle}; use windows_sys::{ Wdk::System::SystemServices::RtlGetVersion, Win32::{ Foundation::{HWND, LPARAM, LRESULT, WPARAM}, System::{ LibraryLoader::{GetModuleHandleW, GetProcAddress}, SystemInformation::OSVERSIONINFOW, }, UI::WindowsAndMessaging::{ CallWindowProcW, GWLP_WNDPROC, GetPropW, RemovePropW, SWP_FRAMECHANGED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, SetPropW, SetWindowLongPtrW, SetWindowPos, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_NCDESTROY, WNDPROC, }, }, core::{BOOL, PCWSTR}, s, w, }; /// Windows 11's first build, whose DWM moves an acrylic window as fast as a clear one. const ELEVEN: u32 = 22000; /// gpui's window procedure, which the main windows' pass every message on to. static GPUI: AtomicIsize = AtomicIsize::new(0); /// A window's property while it is blurred, which its moves read. const BLURRED: PCWSTR = w!("ElyTerminalBlurred"); /// A window's property while it moves or sizes, which a change of its blur reads. const MOVING: PCWSTR = w!("ElyTerminalMoving"); /// gpui's accents: acrylic for Blurred, a clear gradient for Transparent. const ACRYLIC: u32 = 4; const CLEAR: u32 = 2; /// A main window on Windows 10, whose moves clear its acrylic. pub struct Moves { window: HWND, } impl Moves { /// Takes the window's moves on Windows 10; none on 11 and later. pub fn on(window: &Window) -> anyhow::Result> { let build = build()?; if build >= ELEVEN { log::info!("blur: Windows build {build} moves acrylic as it is"); return Ok(None); } let window = native(window)?; let gpui = unsafe { SetWindowLongPtrW(window, GWLP_WNDPROC, moving as *const () as isize) }; anyhow::ensure!(gpui != 0, "blur: the window's procedure does not read"); let kept = GPUI.swap(gpui, Ordering::Relaxed); anyhow::ensure!( kept == 0 || kept == gpui, "blur: gpui's windows have two procedures" ); log::info!("blur: on Windows build {build} acrylic clears while the window moves"); Ok(Some(Self { window })) } /// Notes whether the window is blurred, as its moves read it; one blurred mid-move stays clear until the move ends. pub fn blurred(&self, on: bool) { if !on { unsafe { RemovePropW(self.window, BLURRED) }; return; } if unsafe { SetPropW(self.window, BLURRED, 1 as _) } == 0 { log::error!("blur: the window's blurred mark does not set"); } if !unsafe { GetPropW(self.window, MOVING) }.is_null() { match accent(self.window, CLEAR) { Ok(()) => log::debug!("blur: blurred mid-move, the window moves clear"), Err(error) => log::error!("{error:#}"), } } } } /// The window's procedure: a blurred window clears its acrylic while it moves; all else is gpui's. unsafe extern "system" fn moving( window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM, ) -> LRESULT { let gpui: WNDPROC = unsafe { std::mem::transmute(GPUI.load(Ordering::Relaxed)) }; let blurred = !unsafe { GetPropW(window, BLURRED) }.is_null(); match message { WM_ENTERSIZEMOVE => { if unsafe { SetPropW(window, MOVING, 1 as _) } == 0 { log::error!("blur: the window's moving mark does not set"); } if blurred { match accent(window, CLEAR) { Ok(()) => log::debug!("blur: the window moves clear"), Err(error) => log::error!("{error:#}"), } } } WM_EXITSIZEMOVE => { unsafe { RemovePropW(window, MOVING) }; if blurred { match accent(window, ACRYLIC) { Ok(()) => log::debug!("blur: the window rests blurred again"), Err(error) => log::error!("{error:#}"), } let flags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED; if unsafe { SetWindowPos(window, std::ptr::null_mut(), 0, 0, 0, 0, flags) } == 0 { log::error!("blur: DWM hears of no new frame after the move"); } } } WM_NCDESTROY => { unsafe { RemovePropW(window, BLURRED) }; unsafe { RemovePropW(window, MOVING) }; } _ => {} } unsafe { CallWindowProcW(gpui, window, message, wparam, lparam) } } #[repr(C)] struct AccentPolicy { state: u32, flags: u32, color: u32, animation: u32, } #[repr(C)] struct CompositionData { attribute: u32, data: *mut AccentPolicy, size: usize, } /// Sets the window's accent as gpui's `set_window_composition_attribute` sets it. fn accent(window: HWND, state: u32) -> anyhow::Result<()> { type Set = unsafe extern "system" fn(HWND, *mut CompositionData) -> BOOL; let user32 = unsafe { GetModuleHandleW(w!("user32.dll")) }; anyhow::ensure!(!user32.is_null(), "blur: user32.dll is not loaded"); let found = unsafe { GetProcAddress(user32, s!("SetWindowCompositionAttribute")) } .context("blur: user32.dll has no SetWindowCompositionAttribute")?; let set: Set = unsafe { std::mem::transmute(found) }; let acrylic = state == ACRYLIC; let mut policy = AccentPolicy { state, flags: if acrylic { 0 } else { 2 }, color: if acrylic { 1 << 24 } else { 0 }, animation: 0, }; let mut data = CompositionData { attribute: 0x13, data: &mut policy, size: size_of::(), }; anyhow::ensure!( unsafe { set(window, &mut data) } != 0, "blur: the window's accent {state} does not set" ); Ok(()) } /// The running Windows' build. fn build() -> anyhow::Result { let mut version: OSVERSIONINFOW = unsafe { std::mem::zeroed() }; version.dwOSVersionInfoSize = size_of::() as u32; let status = unsafe { RtlGetVersion(&mut version) }; anyhow::ensure!(status == 0, "blur: RtlGetVersion fails with {status:#x}"); Ok(version.dwBuildNumber) } /// The HWND gpui made for `window`. fn native(window: &Window) -> anyhow::Result { let handle = HasWindowHandle::window_handle(window) .map_err(|error| anyhow::anyhow!("blur: no native handle: {error:?}"))?; let RawWindowHandle::Win32(win32) = handle.as_raw() else { bail!("blur: not a Win32 window"); }; Ok(win32.hwnd.get() as HWND) }