use std::any::Any;
use std::sync::Arc;
use kui_core::Backdrop;
use winit::window::{Window, WindowAttributes};
pub(crate) struct Applied {
pub got: Backdrop,
pub ground: Option<crate::ground::Source>,
pub keep: Option<Box<dyn Any>>,
}
impl Applied {
fn just(got: Backdrop) -> Self {
Applied {
got,
ground: None,
keep: None,
}
}
fn emulated() -> Self {
use crate::ground::Source;
let source = match crate::ground::known_path() {
Some(None) => return Applied::just(Backdrop::Opaque),
Some(Some(path)) => Source::Load(path),
None => Source::Find,
};
Applied {
got: Backdrop::Tinted,
ground: Some(source),
keep: None,
}
}
}
fn emulate_forced() -> bool {
std::env::var_os("KUI_BACKDROP_EMULATE").is_some_and(|v| !v.is_empty() && v != "0")
}
pub(crate) fn attrs(attrs: WindowAttributes, backdrop: Backdrop) -> WindowAttributes {
if !backdrop.is_translucent() {
return attrs;
}
#[cfg(target_os = "windows")]
{
windows::attrs(attrs)
}
#[cfg(not(target_os = "windows"))]
{
attrs.with_transparent(true)
}
}
pub(crate) fn apply(
window: &Arc<Window>,
renderer: &kui_wgpu::Renderer,
backdrop: Backdrop,
) -> Applied {
match backdrop {
Backdrop::Opaque => Applied::just(Backdrop::Opaque),
Backdrop::Transparent => Applied::just(if see_through(window, renderer) {
Backdrop::Transparent
} else {
Backdrop::Opaque
}),
Backdrop::Blur | Backdrop::Tinted if emulate_forced() => Applied::emulated(),
Backdrop::Blur | Backdrop::Tinted => effect(window, renderer, backdrop),
}
}
fn see_through(window: &Window, renderer: &kui_wgpu::Renderer) -> bool {
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
{
renderer.transparent() && crate::linux_blur::composited(window)
}
#[cfg(any(target_os = "macos", target_os = "windows"))]
{
let _ = window;
renderer.transparent()
}
}
#[cfg(target_os = "macos")]
fn effect(window: &Window, renderer: &kui_wgpu::Renderer, backdrop: Backdrop) -> Applied {
if renderer.transparent() && macos::install(window, backdrop) {
Applied::just(backdrop)
} else {
Applied::emulated()
}
}
#[cfg(target_os = "windows")]
fn effect(window: &Window, renderer: &kui_wgpu::Renderer, backdrop: Backdrop) -> Applied {
if renderer.transparent() && windows::system_backdrop(window, backdrop) {
Applied::just(backdrop)
} else {
Applied::emulated()
}
}
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
fn effect(window: &Window, renderer: &kui_wgpu::Renderer, backdrop: Backdrop) -> Applied {
if backdrop == Backdrop::Blur
&& see_through(window, renderer)
&& let Some(keep) = crate::linux_blur::request(window)
{
return Applied {
got: Backdrop::Blur,
ground: None,
keep: Some(keep),
};
}
Applied::emulated()
}
#[cfg(target_os = "windows")]
mod windows {
use kui_core::Backdrop;
use windows_sys::Win32::Foundation::HWND;
use windows_sys::Win32::Graphics::Dwm::{
DWMSBT_MAINWINDOW, DWMSBT_TRANSIENTWINDOW, DWMWA_SYSTEMBACKDROP_TYPE,
DwmExtendFrameIntoClientArea, DwmSetWindowAttribute,
};
use windows_sys::Win32::UI::Controls::MARGINS;
use winit::window::{Window, WindowAttributes};
pub(super) fn attrs(attrs: WindowAttributes) -> WindowAttributes {
use winit::platform::windows::WindowAttributesExtWindows;
if !kui_wgpu::see_through_by_visual() {
return attrs;
}
attrs.with_no_redirection_bitmap(true)
}
pub(super) fn backdrop_type(backdrop: Backdrop) -> Option<i32> {
match backdrop {
Backdrop::Blur => Some(DWMSBT_TRANSIENTWINDOW),
Backdrop::Tinted => Some(DWMSBT_MAINWINDOW),
Backdrop::Opaque | Backdrop::Transparent => None,
}
}
pub(super) fn system_backdrop(window: &Window, backdrop: Backdrop) -> bool {
use winit::raw_window_handle::{HasWindowHandle, RawWindowHandle};
let Some(kind) = backdrop_type(backdrop) else {
return false;
};
let Ok(handle) = window.window_handle() else {
return false;
};
let RawWindowHandle::Win32(h) = handle.as_raw() else {
return false;
};
let hwnd = h.hwnd.get() as HWND;
let hr = unsafe {
DwmSetWindowAttribute(
hwnd,
DWMWA_SYSTEMBACKDROP_TYPE as u32,
(&kind as *const i32).cast(),
size_of::<i32>() as u32,
)
};
if hr < 0 {
return false;
}
let margins = MARGINS {
cxLeftWidth: -1,
cxRightWidth: -1,
cyTopHeight: -1,
cyBottomHeight: -1,
};
let hr = unsafe { DwmExtendFrameIntoClientArea(hwnd, &margins) };
hr >= 0
}
}
#[cfg(target_os = "macos")]
mod macos {
use kui_core::Backdrop;
use objc2::rc::Retained;
use objc2::{MainThreadMarker, MainThreadOnly, define_class, msg_send};
use objc2_app_kit::{
NSAutoresizingMaskOptions, NSResponder, NSView, NSVisualEffectBlendingMode,
NSVisualEffectMaterial, NSVisualEffectState, NSVisualEffectView, NSWindowOrderingMode,
};
use objc2_foundation::{NSObject, NSPoint, NSRect};
use winit::window::Window;
define_class!(
#[unsafe(super(NSVisualEffectView, NSView, NSResponder, NSObject))]
#[thread_kind = MainThreadOnly]
#[name = "KuiBackdropView"]
struct BackdropView;
impl BackdropView {
#[unsafe(method(hitTest:))]
fn hit_test(&self, _point: NSPoint) -> *mut NSView {
std::ptr::null_mut()
}
}
);
impl BackdropView {
fn new(mtm: MainThreadMarker, frame: NSRect) -> Retained<Self> {
let this = Self::alloc(mtm).set_ivars(());
unsafe { msg_send![super(this), initWithFrame: frame] }
}
}
pub(super) fn material(backdrop: Backdrop) -> Option<NSVisualEffectMaterial> {
match backdrop {
Backdrop::Blur => Some(NSVisualEffectMaterial::Sidebar),
Backdrop::Tinted => Some(NSVisualEffectMaterial::WindowBackground),
Backdrop::Opaque | Backdrop::Transparent => None,
}
}
pub(super) fn install(window: &Window, backdrop: Backdrop) -> bool {
use winit::raw_window_handle::{HasWindowHandle, RawWindowHandle};
let Some(material) = material(backdrop) else {
return false;
};
let Some(mtm) = MainThreadMarker::new() else {
return false;
};
let Ok(handle) = window.window_handle() else {
return false;
};
let RawWindowHandle::AppKit(h) = handle.as_raw() else {
return false;
};
let Some(view) = (unsafe { Retained::retain(h.ns_view.as_ptr().cast::<NSView>()) }) else {
return false;
};
let effect = BackdropView::new(mtm, view.bounds());
effect.setMaterial(material);
effect.setBlendingMode(NSVisualEffectBlendingMode::BehindWindow);
effect.setState(NSVisualEffectState::FollowsWindowActiveState);
effect.setAutoresizingMask(
NSAutoresizingMaskOptions::ViewWidthSizable
| NSAutoresizingMaskOptions::ViewHeightSizable,
);
effect.setWantsLayer(true);
view.addSubview_positioned_relativeTo(&effect, NSWindowOrderingMode::Below, None);
if let Some(layer) = effect.layer() {
layer.setZPosition(-1.0);
}
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_opaque_window_is_created_as_it_always_was() {
let plain = Window::default_attributes();
let same = attrs(Window::default_attributes(), Backdrop::Opaque);
assert_eq!(same.transparent, plain.transparent);
assert!(!same.transparent);
for b in [Backdrop::Transparent, Backdrop::Blur, Backdrop::Tinted] {
assert_eq!(
attrs(Window::default_attributes(), b).transparent,
!cfg!(target_os = "windows")
);
}
}
#[test]
fn the_fallback_reports_what_it_draws() {
use crate::ground::Source;
let got = Applied::emulated();
assert_eq!(got.got == Backdrop::Tinted, got.ground.is_some());
match crate::ground::known_path() {
Some(None) => assert_eq!(got.got, Backdrop::Opaque),
Some(Some(path)) => assert_eq!(got.ground, Some(Source::Load(path))),
None => assert_eq!(got.ground, Some(Source::Find)),
}
if cfg!(target_os = "macos") {
assert_eq!(got.got, Backdrop::Opaque, "never read on macOS");
}
if cfg!(target_os = "windows") {
assert!(
crate::ground::known_path().is_some(),
"Windows answers on the loop"
);
}
assert!(got.keep.is_none());
}
#[cfg(target_os = "windows")]
#[test]
fn each_effect_is_a_system_backdrop_type() {
use windows_sys::Win32::Graphics::Dwm::{DWMSBT_MAINWINDOW, DWMSBT_TRANSIENTWINDOW};
assert_eq!(
windows::backdrop_type(Backdrop::Blur),
Some(DWMSBT_TRANSIENTWINDOW)
);
assert_eq!(
windows::backdrop_type(Backdrop::Tinted),
Some(DWMSBT_MAINWINDOW)
);
assert_eq!(windows::backdrop_type(Backdrop::Transparent), None);
assert_eq!(windows::backdrop_type(Backdrop::Opaque), None);
}
}