Skip to main content

window_vibrancy/
lib.rs

1// Copyright 2019-2022 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! Make your windows vibrant.
6//!
7//! ## Platform-specific
8//!
9//! - **Linux**: Unsupported, Blur and any vibrancy effects are controlled by the compositor installed on the end-user system.
10//!
11//! # Example
12//!
13//! ```no_run
14//! use window_vibrancy::{apply_vibrancy, apply_blur, NSVisualEffectMaterial};
15//!
16//! # let window: &dyn raw_window_handle::HasWindowHandle = unsafe { std::mem::zeroed() };
17//! #[cfg(target_os = "macos")]
18//! apply_vibrancy(&window, NSVisualEffectMaterial::AppearanceBased, None, None).expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS");
19//!
20//! #[cfg(target_os = "windows")]
21//! apply_blur(&window, Some((18, 18, 18, 125))).expect("Unsupported platform! 'apply_blur' is only supported on Windows");
22//! ```
23
24#![allow(clippy::deprecated_semver)]
25
26#[cfg(target_os = "windows")]
27use windows_sys::core::HRESULT;
28
29mod macos;
30mod windows;
31
32pub use macos::{NSGlassEffectViewStyle, NSVisualEffectMaterial, NSVisualEffectState};
33
34#[cfg(target_os = "macos")]
35pub use macos::{LiquidGlassOptions, NSGlassEffectViewTagged, NSVisualEffectViewTagged};
36
37/// a tuple of RGBA colors. Each value has minimum of 0 and maximum of 255.
38pub type Color = (u8, u8, u8, u8);
39
40/// Applies blur effect to window. Works only on Windows 7, Windows 10 v1809 or newer.
41///
42/// ## WARNING:
43///
44/// This method has poor performance on Windows 11 build 22621,
45/// the window will lag when resizing or dragging.
46/// It is an issue in the undocumented api used for this method
47/// and microsoft needs to fix it (they probably won't).
48///
49/// ## Platform-specific
50///
51/// - **Windows**: *`color`* is ignored on Windows 7 and has no effect.
52/// - **Linux / macOS**: Unsupported.
53pub fn apply_blur(
54    window: impl raw_window_handle::HasWindowHandle,
55    #[allow(unused)] color: Option<Color>,
56) -> Result<(), Error> {
57    match window.window_handle()?.as_raw() {
58        #[cfg(target_os = "windows")]
59        raw_window_handle::RawWindowHandle::Win32(handle) => {
60            windows::apply_blur(handle.hwnd.get() as _, color)
61        }
62        _ => Err(Error::UnsupportedPlatform(
63            "\"apply_blur()\" is only supported on Windows.",
64        )),
65    }
66}
67
68/// Clears blur effect applied to window. Works only on Windows 7, Windows 10 v1809 or newer.
69///
70/// ## Platform-specific
71///
72/// - **Linux / macOS**: Unsupported.
73pub fn clear_blur(window: impl raw_window_handle::HasWindowHandle) -> Result<(), Error> {
74    match window.window_handle()?.as_raw() {
75        #[cfg(target_os = "windows")]
76        raw_window_handle::RawWindowHandle::Win32(handle) => {
77            windows::clear_blur(handle.hwnd.get() as _)
78        }
79        _ => Err(Error::UnsupportedPlatform(
80            "\"clear_blur()\" is only supported on Windows.",
81        )),
82    }
83}
84
85/// Applies acrylic effect to window. Works only on Windows 10 v1809 or newer.
86///
87/// ## WARNING:
88///
89/// This method has poor performance on Windows 10 v1903+ and Windows 11 build 22000,
90/// the window will lag when resizing or dragging.
91/// It is an issue in the undocumented api used for this method
92/// and microsoft needs to fix it (they probably won't).
93///
94/// ## Platform-specific
95///
96/// - **Windows**: *`color`* is ignored on Windows 7 and has no effect.
97/// - **Linux / macOS**: Unsupported.
98pub fn apply_acrylic(
99    window: impl raw_window_handle::HasWindowHandle,
100    #[allow(unused)] color: Option<Color>,
101) -> Result<(), Error> {
102    match window.window_handle()?.as_raw() {
103        #[cfg(target_os = "windows")]
104        raw_window_handle::RawWindowHandle::Win32(handle) => {
105            windows::apply_acrylic(handle.hwnd.get() as _, color)
106        }
107        _ => Err(Error::UnsupportedPlatform(
108            "\"apply_acrylic()\" is only supported on Windows.",
109        )),
110    }
111}
112
113/// Clears acrylic effect applied to window. Works only on Windows 10 v1809 or newer.
114///
115/// ## Platform-specific
116///
117/// - **Linux / macOS**: Unsupported.
118pub fn clear_acrylic(window: impl raw_window_handle::HasWindowHandle) -> Result<(), Error> {
119    match window.window_handle()?.as_raw() {
120        #[cfg(target_os = "windows")]
121        raw_window_handle::RawWindowHandle::Win32(handle) => {
122            windows::clear_acrylic(handle.hwnd.get() as _)
123        }
124        _ => Err(Error::UnsupportedPlatform(
125            "\"clear_acrylic()\" is only supported on Windows.",
126        )),
127    }
128}
129
130/// Applies mica effect to window. Works only on Windows 11.
131///
132/// ## Arguments
133///
134/// - `dark`: If `None` is provide, it will match the system preference
135///
136/// ## Platform-specific
137///
138/// - **Linux / macOS**: Unsupported.
139pub fn apply_mica(
140    window: impl raw_window_handle::HasWindowHandle,
141    dark: Option<bool>,
142) -> Result<(), Error> {
143    #[cfg(not(target_os = "windows"))]
144    let _ = dark;
145    match window.window_handle()?.as_raw() {
146        #[cfg(target_os = "windows")]
147        raw_window_handle::RawWindowHandle::Win32(handle) => {
148            windows::apply_mica(handle.hwnd.get() as _, dark)
149        }
150        _ => Err(Error::UnsupportedPlatform(
151            "\"apply_mica()\" is only supported on Windows.",
152        )),
153    }
154}
155
156/// Clears mica effect applied to window. Works only on Windows 11.
157///
158/// ## Platform-specific
159///
160/// - **Linux / macOS**: Unsupported.
161pub fn clear_mica(window: impl raw_window_handle::HasWindowHandle) -> Result<(), Error> {
162    match window.window_handle()?.as_raw() {
163        #[cfg(target_os = "windows")]
164        raw_window_handle::RawWindowHandle::Win32(handle) => {
165            windows::clear_mica(handle.hwnd.get() as _)
166        }
167        _ => Err(Error::UnsupportedPlatform(
168            "\"clear_mica()\" is only supported on Windows.",
169        )),
170    }
171}
172
173/// Applies mica tabbed effect to window. Works only on Windows 11.
174///
175/// ## Arguments
176///
177/// - `dark`: If `None` is provide, it will match the system preference
178///
179/// ## Platform-specific
180///
181/// - **Linux / macOS**: Unsupported.
182pub fn apply_tabbed(
183    window: impl raw_window_handle::HasWindowHandle,
184    dark: Option<bool>,
185) -> Result<(), Error> {
186    #[cfg(not(target_os = "windows"))]
187    let _ = dark;
188    match window.window_handle()?.as_raw() {
189        #[cfg(target_os = "windows")]
190        raw_window_handle::RawWindowHandle::Win32(handle) => {
191            windows::apply_tabbed(handle.hwnd.get() as _, dark)
192        }
193        _ => Err(Error::UnsupportedPlatform(
194            "\"apply_tabbed()\" is only supported on Windows.",
195        )),
196    }
197}
198
199/// Clears mica tabbed effect applied to window. Works only on Windows 11.
200///
201/// ## Platform-specific
202///
203/// - **Linux / macOS**: Unsupported.
204pub fn clear_tabbed(window: impl raw_window_handle::HasWindowHandle) -> Result<(), Error> {
205    match window.window_handle()?.as_raw() {
206        #[cfg(target_os = "windows")]
207        raw_window_handle::RawWindowHandle::Win32(handle) => {
208            windows::clear_tabbed(handle.hwnd.get() as _)
209        }
210        _ => Err(Error::UnsupportedPlatform(
211            "\"clear_tabbed()\" is only supported on Windows.",
212        )),
213    }
214}
215
216/// Applies macos vibrancy effect to window. Works only on macOS 10.10 or newer.
217///
218/// ## Platform-specific
219///
220/// - **Linux / Windows**: Unsupported.
221pub fn apply_vibrancy(
222    window: impl raw_window_handle::HasWindowHandle,
223    #[allow(unused)] effect: NSVisualEffectMaterial,
224    #[allow(unused)] state: Option<NSVisualEffectState>,
225    #[allow(unused)] radius: Option<f64>,
226) -> Result<(), Error> {
227    match window.window_handle()?.as_raw() {
228        #[cfg(target_os = "macos")]
229        raw_window_handle::RawWindowHandle::AppKit(handle) => unsafe {
230            macos::apply_vibrancy(handle.ns_view, effect, state, radius)
231        },
232        _ => Err(Error::UnsupportedPlatform(
233            "\"apply_vibrancy()\" is only supported on macOS.",
234        )),
235    }
236}
237
238/// Clears vibrancy effect applied to window. Works only on macOS 10.10 or newer.
239///
240/// ## Platform-specific
241///
242/// - **Linux / Windows**: Unsupported.
243///
244/// # Returns
245///
246/// - `Ok(true)` if the vibrancy effect was cleared
247/// - `Ok(false)` if the vibrancy effect was not previously applied by this crate.
248pub fn clear_vibrancy(window: impl raw_window_handle::HasWindowHandle) -> Result<bool, Error> {
249    match window.window_handle()?.as_raw() {
250        #[cfg(target_os = "macos")]
251        raw_window_handle::RawWindowHandle::AppKit(handle) => unsafe {
252            macos::clear_vibrancy(handle.ns_view)
253        },
254        _ => Err(Error::UnsupportedPlatform(
255            "\"clear_vibrancy()\" is only supported on macOS.",
256        )),
257    }
258}
259
260/// Applies liquid glass effect to window. Works only on macOS 26.0+.
261///
262/// ## Platform-specific
263///
264/// - **Linux / Windows**: Unsupported.
265#[cfg(target_os = "macos")]
266pub fn apply_liquid_glass(
267    window: impl raw_window_handle::HasWindowHandle,
268    #[allow(unused)] options: LiquidGlassOptions<'_>,
269) -> Result<(), Error> {
270    match window.window_handle()?.as_raw() {
271        #[cfg(target_os = "macos")]
272        raw_window_handle::RawWindowHandle::AppKit(handle) => {
273            use objc2_app_kit::NSView;
274            let view = unsafe { handle.ns_view.cast::<NSView>().as_ref() };
275            macos::apply_liquid_glass(view, options)
276        }
277        _ => Err(Error::UnsupportedPlatform(
278            "\"apply_liquid_glass()\" is only supported on macOS.",
279        )),
280    }
281}
282
283/// Clears liquid glass effect applied to window. Works only on macOS 26.0+.
284///
285/// ## Platform-specific
286///
287/// - **Linux / Windows**: Unsupported.
288///
289/// # Returns
290///
291/// - `Ok(true)` if the liquid glass effect was cleared
292/// - `Ok(false)` if the liquid glass effect was not previously applied by this crate.
293#[cfg(target_os = "macos")]
294pub fn clear_liquid_glass(window: impl raw_window_handle::HasWindowHandle) -> Result<bool, Error> {
295    match window.window_handle()?.as_raw() {
296        #[cfg(target_os = "macos")]
297        raw_window_handle::RawWindowHandle::AppKit(handle) => {
298            use objc2_app_kit::NSView;
299            let view = unsafe { handle.ns_view.cast::<NSView>().as_ref() };
300            macos::clear_liquid_glass(view)
301        }
302        _ => Err(Error::UnsupportedPlatform(
303            "\"clear_liquid_glass()\" is only supported on macOS.",
304        )),
305    }
306}
307
308#[derive(Debug)]
309pub enum Error {
310    UnsupportedPlatform(&'static str),
311    UnsupportedPlatformVersion(&'static str),
312    NotMainThread(&'static str),
313    NoWindowHandle(raw_window_handle::HandleError),
314    #[cfg(target_os = "windows")]
315    Win32Error {
316        api: &'static str,
317        result: HRESULT,
318    },
319}
320
321impl std::fmt::Display for Error {
322    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
323        match self {
324            Error::UnsupportedPlatform(e)
325            | Error::UnsupportedPlatformVersion(e)
326            | Error::NotMainThread(e) => {
327                write!(f, "{}", e)
328            }
329            Error::NoWindowHandle(e) => {
330                write!(f, "{}", e)
331            }
332            #[cfg(target_os = "windows")]
333            Error::Win32Error { api, result } => {
334                write!(
335                    f,
336                    "Win32 API {api}() returned the error result 0x{:x}.",
337                    result,
338                )
339            }
340        }
341    }
342}
343
344impl std::error::Error for Error {}
345
346impl From<raw_window_handle::HandleError> for Error {
347    fn from(err: raw_window_handle::HandleError) -> Self {
348        Error::NoWindowHandle(err)
349    }
350}