From 0363be477630254b6805bdf9c3fddf2e09a3baa9 Mon Sep 17 00:00:00 2001 From: Dmitry Sharshakov Date: Sun, 8 Oct 2023 22:53:15 +0300 Subject: [PATCH] Add `Window::set_blur` Allow clients to request blur behind their window, implemented on Wayland for now. --- CHANGELOG.md | 1 + Cargo.toml | 3 +- FEATURES.md | 1 + src/platform_impl/android/mod.rs | 2 + src/platform_impl/ios/window.rs | 4 ++ src/platform_impl/linux/mod.rs | 5 ++ src/platform_impl/linux/wayland/state.rs | 5 ++ .../linux/wayland/types/kwin_blur.rs | 70 +++++++++++++++++++ src/platform_impl/linux/wayland/types/mod.rs | 1 + src/platform_impl/linux/wayland/window/mod.rs | 7 ++ .../linux/wayland/window/state.rs | 32 ++++++++- src/platform_impl/linux/x11/window.rs | 3 + src/platform_impl/macos/window.rs | 2 + src/platform_impl/orbital/window.rs | 3 + src/platform_impl/web/window.rs | 2 + src/platform_impl/windows/window.rs | 2 + src/window.rs | 26 +++++++ 17 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 src/platform_impl/linux/wayland/types/kwin_blur.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9285266268..186152dc81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ And please only add new entries to the top of this list, right below the `# Unre - **Breaking:** Change default `ControlFlow` from `Poll` to `Wait`. - **Breaking:** remove `DeviceEvent::Text`. - On Android, fix `DeviceId` to contain device id's. +- Add `Window::set_blur` to request a blur behind the window; implemented on Wayland for now. # 0.29.1-beta diff --git a/Cargo.toml b/Cargo.toml index 35a5a59aaf..1e189755a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"] x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"] -wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "sctk", "fnv", "memmap2"] +wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "wayland-protocols-plasma", "sctk", "fnv", "memmap2"] wayland-dlopen = ["wayland-backend/dlopen"] wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"] wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"] @@ -149,6 +149,7 @@ sctk-adwaita = { version = "0.6.0", default_features = false, optional = true } wayland-client = { version = "0.30.0", optional = true } wayland-backend = { version = "0.1.0", default_features = false, features = ["client_system"], optional = true } wayland-protocols = { version = "0.30.0", features = [ "staging"], optional = true } +wayland-protocols-plasma = { version = "0.1.0", features = [ "client" ], optional = true } calloop = "0.10.5" rustix = { version = "0.38.4", default-features = false, features = ["std", "system", "thread", "process"] } x11-dl = { version = "2.18.5", optional = true } diff --git a/FEATURES.md b/FEATURES.md index 0250d47dc3..7eac72f216 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -182,6 +182,7 @@ Legend: |Window resizing |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ | |Window resize increments |❌ |✔️ |✔️ |❌ |**N/A**|**N/A**|**N/A**|**N/A** | |Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |✔️ | +|Window blur |❌ |❌ |❌ |✔️ |**N/A**|**N/A**|N/A |❌ | |Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** | |Window maximization toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** | |Window minimization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** | diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 71b1d41739..37bcc75f6b 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -843,6 +843,8 @@ impl Window { pub fn set_transparent(&self, _transparent: bool) {} + pub fn set_blur(&self, _blur: bool) {} + pub fn set_visible(&self, _visibility: bool) {} pub fn is_visible(&self) -> Option { diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index f80ea9f6fd..667e0172dd 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -42,6 +42,10 @@ impl Inner { debug!("`Window::set_transparent` is ignored on iOS") } + pub fn set_blur(&self, _blur: bool) { + debug!("`Window::set_blur` is ignored on iOS") + } + pub fn set_visible(&self, visible: bool) { self.window.setHidden(!visible) } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 8983c9cff6..e8a7586f10 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -329,6 +329,11 @@ impl Window { x11_or_wayland!(match self; Window(w) => w.set_transparent(transparent)); } + #[inline] + pub fn set_blur(&self, blur: bool) { + x11_or_wayland!(match self; Window(w) => w.set_blur(blur)); + } + #[inline] pub fn set_visible(&self, visible: bool) { x11_or_wayland!(match self; Window(w) => w.set_visible(visible)) diff --git a/src/platform_impl/linux/wayland/state.rs b/src/platform_impl/linux/wayland/state.rs index 762f97ac95..2c885ee503 100644 --- a/src/platform_impl/linux/wayland/state.rs +++ b/src/platform_impl/linux/wayland/state.rs @@ -31,6 +31,7 @@ use super::seat::{ PointerConstraintsState, RelativePointerState, TextInputState, WinitPointerData, WinitPointerDataExt, WinitSeatState, }; +use super::types::kwin_blur::KWinBlurManager; use super::types::wp_fractional_scaling::FractionalScalingManager; use super::types::wp_viewporter::ViewporterState; use super::types::xdg_activation::XdgActivationState; @@ -103,6 +104,9 @@ pub struct WinitState { /// Fractional scaling manager. pub fractional_scaling_manager: Option, + /// KWin blur manager. + pub kwin_blur_manager: Option, + /// Loop handle to re-register event sources, such as keyboard repeat. pub loop_handle: LoopHandle<'static, Self>, @@ -161,6 +165,7 @@ impl WinitState { window_events_sink: Default::default(), viewporter_state, fractional_scaling_manager, + kwin_blur_manager: KWinBlurManager::new(globals, queue_handle).ok(), seats, text_input_state: TextInputState::new(globals, queue_handle).ok(), diff --git a/src/platform_impl/linux/wayland/types/kwin_blur.rs b/src/platform_impl/linux/wayland/types/kwin_blur.rs new file mode 100644 index 0000000000..159f0fbaf0 --- /dev/null +++ b/src/platform_impl/linux/wayland/types/kwin_blur.rs @@ -0,0 +1,70 @@ +//! Handling of KDE-compatible blur. + +use sctk::reexports::client::globals::{BindError, GlobalList}; +use sctk::reexports::client::protocol::wl_surface::WlSurface; +use sctk::reexports::client::Dispatch; +use sctk::reexports::client::{delegate_dispatch, Connection, Proxy, QueueHandle}; +use wayland_protocols_plasma::blur::client::{ + org_kde_kwin_blur::OrgKdeKwinBlur, org_kde_kwin_blur_manager::OrgKdeKwinBlurManager, +}; + +use sctk::globals::GlobalData; + +use crate::platform_impl::wayland::state::WinitState; + +/// KWin blur manager. +#[derive(Debug, Clone)] +pub struct KWinBlurManager { + manager: OrgKdeKwinBlurManager, +} + +impl KWinBlurManager { + pub fn new( + globals: &GlobalList, + queue_handle: &QueueHandle, + ) -> Result { + let manager = globals.bind(queue_handle, 1..=1, GlobalData)?; + Ok(Self { manager }) + } + + pub fn blur( + &self, + surface: &WlSurface, + queue_handle: &QueueHandle, + ) -> OrgKdeKwinBlur { + self.manager.create(surface, queue_handle, ()) + } + + pub fn unset(&self, surface: &WlSurface) { + self.manager.unset(surface) + } +} + +impl Dispatch for KWinBlurManager { + fn event( + _: &mut WinitState, + _: &OrgKdeKwinBlurManager, + _: ::Event, + _: &GlobalData, + _: &Connection, + _: &QueueHandle, + ) { + unreachable!("no events defined for org_kde_kwin_blur_manager"); + } +} + +impl Dispatch for KWinBlurManager { + fn event( + _: &mut WinitState, + _: &OrgKdeKwinBlur, + _: ::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + unreachable!("no events defined for org_kde_kwin_blur"); + } +} + +delegate_dispatch!(WinitState: [OrgKdeKwinBlurManager: GlobalData] => KWinBlurManager); +delegate_dispatch!(WinitState: [OrgKdeKwinBlur: ()] => KWinBlurManager); diff --git a/src/platform_impl/linux/wayland/types/mod.rs b/src/platform_impl/linux/wayland/types/mod.rs index 8e92eb1d03..ea74588823 100644 --- a/src/platform_impl/linux/wayland/types/mod.rs +++ b/src/platform_impl/linux/wayland/types/mod.rs @@ -1,5 +1,6 @@ //! Wayland protocol implementation boilerplate. +pub mod kwin_blur; pub mod wp_fractional_scaling; pub mod wp_viewporter; pub mod xdg_activation; diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index 88f1437d79..d7b5bbe396 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -131,6 +131,8 @@ impl Window { // Set transparency hint. window_state.set_transparent(attributes.transparent); + window_state.set_blur(attributes.blur); + // Set the decorations hint. window_state.set_decorate(attributes.decorations); @@ -409,6 +411,11 @@ impl Window { self.window_state.lock().unwrap().scale_factor() } + #[inline] + pub fn set_blur(&self, blur: bool) { + self.window_state.lock().unwrap().set_blur(blur); + } + #[inline] pub fn set_decorations(&self, decorate: bool) { self.window_state.lock().unwrap().set_decorate(decorate) diff --git a/src/platform_impl/linux/wayland/window/state.rs b/src/platform_impl/linux/wayland/window/state.rs index abe8887991..470d2665cf 100644 --- a/src/platform_impl/linux/wayland/window/state.rs +++ b/src/platform_impl/linux/wayland/window/state.rs @@ -4,7 +4,7 @@ use std::mem::ManuallyDrop; use std::num::NonZeroU32; use std::sync::{Arc, Weak}; -use log::warn; +use log::{info, warn}; use sctk::reexports::client::protocol::wl_seat::WlSeat; use sctk::reexports::client::protocol::wl_shm::WlShm; @@ -23,9 +23,11 @@ use sctk::shell::xdg::XdgSurface; use sctk::shell::WaylandSurface; use sctk::shm::Shm; use sctk::subcompositor::SubcompositorState; +use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur; use crate::dpi::{LogicalPosition, LogicalSize}; use crate::error::{ExternalError, NotSupportedError}; +use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager; use crate::platform_impl::WindowId; use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme}; @@ -130,6 +132,8 @@ pub struct WindowState { viewport: Option, fractional_scale: Option, + blur: Option, + blur_manager: Option, /// Whether the client side decorations have pending move operations. /// @@ -159,6 +163,8 @@ impl WindowState { .map(|fsm| fsm.fractional_scaling(window.wl_surface(), queue_handle)); Self { + blur: None, + blur_manager: winit_state.kwin_blur_manager.clone(), compositor, connection, csd_fails: false, @@ -840,6 +846,26 @@ impl WindowState { } } + /// Make window background blurred + #[inline] + pub fn set_blur(&mut self, blurred: bool) { + if blurred && self.blur.is_none() { + if let Some(blur_manager) = self.blur_manager.as_ref() { + let blur = blur_manager.blur(self.window.wl_surface(), &self.queue_handle); + blur.commit(); + self.blur = Some(blur); + } else { + info!("Blur manager unavailable, unable to change blur") + } + } else if !blurred && self.blur.is_some() { + self.blur_manager + .as_ref() + .unwrap() + .unset(self.window.wl_surface()); + self.blur.take().unwrap().release(); + } + } + /// Set the window title to a new value. /// /// This will autmatically truncate the title to something meaningfull. @@ -900,6 +926,10 @@ impl Drop for WindowState { ManuallyDrop::drop(&mut self.window); } + if let Some(blur) = &self.blur { + blur.release(); + } + surface.destroy(); } } diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 946bf24f6c..8dcf2382d2 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -1027,6 +1027,9 @@ impl UnownedWindow { #[inline] pub fn set_transparent(&self, _transparent: bool) {} + #[inline] + pub fn set_blur(&self, _blur: bool) {} + fn set_decorations_inner(&self, decorations: bool) -> Result, X11Error> { self.shared_state_lock().is_decorated = decorations; let mut hints = self.xconn.get_motif_hints(self.xwindow); diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 5ec9c5e5cf..35ce8b5292 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -596,6 +596,8 @@ impl WinitWindow { self.setOpaque(!transparent) } + pub fn set_blur(&self, _blur: bool) {} + pub fn set_visible(&self, visible: bool) { match visible { true => self.makeKeyAndOrderFront(None), diff --git a/src/platform_impl/orbital/window.rs b/src/platform_impl/orbital/window.rs index 7253802009..1f0f633bb7 100644 --- a/src/platform_impl/orbital/window.rs +++ b/src/platform_impl/orbital/window.rs @@ -261,6 +261,9 @@ impl Window { #[inline] pub fn set_transparent(&self, _transparent: bool) {} + #[inline] + pub fn set_blur(&self, _blur: bool) {} + #[inline] pub fn set_visible(&self, _visibility: bool) {} diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index 39961039d4..3cbb9bb4ab 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -92,6 +92,8 @@ impl Inner { pub fn set_transparent(&self, _transparent: bool) {} + pub fn set_blur(&self, _blur: bool) {} + pub fn set_visible(&self, _visible: bool) { // Intentionally a no-op } diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 911cd502aa..bffba5a3fe 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -127,6 +127,8 @@ impl Window { pub fn set_transparent(&self, _transparent: bool) {} + pub fn set_blur(&self, _blur: bool) {} + #[inline] pub fn set_visible(&self, visible: bool) { let window = self.window.clone(); diff --git a/src/window.rs b/src/window.rs index f6fecf78d4..9d5b4382ae 100644 --- a/src/window.rs +++ b/src/window.rs @@ -151,6 +151,7 @@ pub struct WindowAttributes { pub maximized: bool, pub visible: bool, pub transparent: bool, + pub blur: bool, pub decorations: bool, pub window_icon: Option, pub preferred_theme: Option, @@ -176,6 +177,7 @@ impl Default for WindowAttributes { fullscreen: None, visible: true, transparent: false, + blur: false, decorations: true, window_level: Default::default(), window_icon: None, @@ -343,6 +345,17 @@ impl WindowBuilder { self } + /// Sets whether the background of the window should be blurred by the system. + /// + /// The default is `false`. + /// + /// See [`Window::set_blur`] for details. + #[inline] + pub fn with_blur(mut self, blur: bool) -> Self { + self.window.blur = blur; + self + } + /// Get whether the window will support transparency. #[inline] pub fn transparent(&self) -> bool { @@ -884,6 +897,19 @@ impl Window { .maybe_queue_on_main(move |w| w.set_transparent(transparent)) } + /// Change the window blur state. + /// + /// If `true`, this will make the transparent window background blurry. + /// + /// ## Platform-specific + /// + /// - **Android / iOS / macOS / X11 / Web / Windows:** Unsupported. + /// - **Wayland:** Only works with org_kde_kwin_blur_manager protocol. + #[inline] + pub fn set_blur(&self, blur: bool) { + self.window.maybe_queue_on_main(move |w| w.set_blur(blur)) + } + /// Modifies the window's visibility. /// /// If `false`, this will hide the window. If `true`, this will show the window.