From fb45fbf901fbe70cc9a877b5d651d0b60c206b08 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sat, 4 Apr 2026 23:59:33 +0900 Subject: [PATCH] wayland: use `ext-background-effect` if available A more modern protocol compared to the KDE one. (cherry picked from commit c4afadbfabf7b1e7989b40b493db1a4c7bd8ff4e) --- src/changelog/unreleased.md | 1 + src/platform_impl/linux/wayland/state.rs | 8 +- .../linux/wayland/types/bgr_effects.rs | 85 +++++++++++++++++++ .../wayland/types/ext_background_effect.rs | 59 +++++++++++++ src/platform_impl/linux/wayland/types/mod.rs | 2 + src/platform_impl/linux/wayland/window/mod.rs | 7 +- .../linux/wayland/window/state.rs | 65 +++++++++----- src/window.rs | 3 +- 8 files changed, 200 insertions(+), 30 deletions(-) create mode 100644 src/platform_impl/linux/wayland/types/bgr_effects.rs create mode 100644 src/platform_impl/linux/wayland/types/ext_background_effect.rs diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index f3a0f6d2c3..5189a627d4 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -16,6 +16,7 @@ on how to add them: - On X11, add `Window::even_more_rare_api`. - On Wayland, add `Window::common_api`. - On Windows, add `Window::some_rare_api`. +- On Wayland, added ext-background-effect-v1 support. ``` When the change requires non-trivial amount of work for users to comply diff --git a/src/platform_impl/linux/wayland/state.rs b/src/platform_impl/linux/wayland/state.rs index 13ef99c26a..205e51248c 100644 --- a/src/platform_impl/linux/wayland/state.rs +++ b/src/platform_impl/linux/wayland/state.rs @@ -29,7 +29,7 @@ use crate::platform_impl::wayland::seat::{ PointerConstraintsState, RelativePointerState, TextInputState, WinitPointerData, WinitPointerDataExt, WinitSeatState, }; -use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager; +use crate::platform_impl::wayland::types::bgr_effects::BgrEffectManager; use crate::platform_impl::wayland::types::wp_fractional_scaling::FractionalScalingManager; use crate::platform_impl::wayland::types::wp_viewporter::ViewporterState; use crate::platform_impl::wayland::types::xdg_activation::XdgActivationState; @@ -106,8 +106,8 @@ pub struct WinitState { /// Fractional scaling manager. pub fractional_scaling_manager: Option, - /// KWin blur manager. - pub kwin_blur_manager: Option, + /// Blur manager. + pub blur_manager: Option, /// Loop handle to re-register event sources, such as keyboard repeat. pub loop_handle: LoopHandle<'static, Self>, @@ -176,7 +176,7 @@ impl WinitState { window_events_sink: Default::default(), viewporter_state, fractional_scaling_manager, - kwin_blur_manager: KWinBlurManager::new(globals, queue_handle).ok(), + blur_manager: BgrEffectManager::new(globals, queue_handle).ok(), seats, text_input_state: TextInputState::new(globals, queue_handle).ok(), diff --git a/src/platform_impl/linux/wayland/types/bgr_effects.rs b/src/platform_impl/linux/wayland/types/bgr_effects.rs new file mode 100644 index 0000000000..2e8bfc506e --- /dev/null +++ b/src/platform_impl/linux/wayland/types/bgr_effects.rs @@ -0,0 +1,85 @@ +use sctk::compositor::Region; +use sctk::reexports::client::QueueHandle; +use sctk::reexports::client::globals::{BindError, GlobalList}; +use sctk::reexports::client::protocol::wl_surface::WlSurface; +use sctk::reexports::protocols::ext::background_effect::v1::client::ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1; +use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur; + +use crate::platform_impl::wayland::state::WinitState; +use crate::platform_impl::wayland::types::ext_background_effect::ExtBackgroundEffectManager; +use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager; + +/// Wrapper around various background effects for [`WlSurface`]. +#[derive(Debug, Clone)] +pub enum BgrEffectManager { + Ext(ExtBackgroundEffectManager), + KWin(KWinBlurManager), +} + +impl BgrEffectManager { + pub fn new( + globals: &GlobalList, + queue_handle: &QueueHandle, + ) -> Result { + ExtBackgroundEffectManager::new(globals, queue_handle) + .map(Self::Ext) + .or_else(|_| KWinBlurManager::new(globals, queue_handle).map(Self::KWin)) + } + + /// Creates a new blur effect for the surface. + pub fn new_blur_effect( + &mut self, + surface: &WlSurface, + queue_handle: &QueueHandle, + ) -> SurfaceBlurEffect { + match self { + BgrEffectManager::Ext(mgr) => SurfaceBlurEffect::Ext(mgr.blur(surface, queue_handle)), + BgrEffectManager::KWin(mgr) => SurfaceBlurEffect::Kwin( + mgr.blur(surface, queue_handle), + mgr.clone(), + surface.clone(), + ), + } + } +} + +#[derive(Debug)] +pub enum SurfaceBlurEffect { + Ext(ExtBackgroundEffectSurfaceV1), + Kwin(OrgKdeKwinBlur, KWinBlurManager, WlSurface), +} + +impl SurfaceBlurEffect { + /// Returns `true` if the main surface commit is required. + /// + /// `None` clears the blur. + #[must_use] + pub fn set_blur(&self, region: Option<&Region>) -> bool { + let region = region.map(|region| region.wl_region()); + match self { + SurfaceBlurEffect::Ext(surface) => { + surface.set_blur_region(region); + true + }, + SurfaceBlurEffect::Kwin(blur, ..) => { + blur.set_region(region); + blur.commit(); + true + }, + } + } +} + +impl Drop for SurfaceBlurEffect { + fn drop(&mut self) { + match self { + SurfaceBlurEffect::Ext(surface) => surface.destroy(), + SurfaceBlurEffect::Kwin(blur, mgr, wl_surface) => { + blur.set_region(None); + blur.commit(); + blur.release(); + mgr.unset(wl_surface); + }, + } + } +} diff --git a/src/platform_impl/linux/wayland/types/ext_background_effect.rs b/src/platform_impl/linux/wayland/types/ext_background_effect.rs new file mode 100644 index 0000000000..11f1e3d11d --- /dev/null +++ b/src/platform_impl/linux/wayland/types/ext_background_effect.rs @@ -0,0 +1,59 @@ +use sctk::globals::GlobalData; +use sctk::reexports::client::globals::{BindError, GlobalList}; +use sctk::reexports::client::protocol::wl_surface::WlSurface; +use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle}; +use wayland_protocols::ext::background_effect::v1::client::ext_background_effect_manager_v1::ExtBackgroundEffectManagerV1; +use wayland_protocols::ext::background_effect::v1::client::ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1; + +use crate::platform_impl::wayland::state::WinitState; + +#[derive(Debug, Clone)] +pub struct ExtBackgroundEffectManager { + manager: ExtBackgroundEffectManagerV1, +} + +impl ExtBackgroundEffectManager { + pub fn new( + globals: &GlobalList, + queue_handle: &QueueHandle, + ) -> Result { + let manager = globals.bind(queue_handle, 1..=1, GlobalData)?; + Ok(Self { manager }) + } + + pub fn blur( + &mut self, + surface: &WlSurface, + queue_handle: &QueueHandle, + ) -> ExtBackgroundEffectSurfaceV1 { + self.manager.get_background_effect(surface, queue_handle, ()) + } +} + +impl Dispatch for ExtBackgroundEffectManager { + fn event( + _: &mut WinitState, + _: &ExtBackgroundEffectManagerV1, + _: ::Event, + _: &GlobalData, + _: &Connection, + _: &QueueHandle, + ) { + } +} + +impl Dispatch for ExtBackgroundEffectManager { + fn event( + _: &mut WinitState, + _: &ExtBackgroundEffectSurfaceV1, + _: ::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + // There is no event + } +} + +delegate_dispatch!(WinitState: [ExtBackgroundEffectManagerV1: GlobalData] => ExtBackgroundEffectManager); +delegate_dispatch!(WinitState: [ExtBackgroundEffectSurfaceV1: ()] => ExtBackgroundEffectManager); diff --git a/src/platform_impl/linux/wayland/types/mod.rs b/src/platform_impl/linux/wayland/types/mod.rs index 77e67f48be..15027d9392 100644 --- a/src/platform_impl/linux/wayland/types/mod.rs +++ b/src/platform_impl/linux/wayland/types/mod.rs @@ -1,6 +1,8 @@ //! Wayland protocol implementation boilerplate. +pub mod bgr_effects; pub mod cursor; +pub mod ext_background_effect; pub mod kwin_blur; pub mod wp_fractional_scaling; pub mod wp_viewporter; diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index 6d29a5a5f0..8b61791e69 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -119,7 +119,8 @@ impl Window { // Set transparency hint. window_state.set_transparent(attributes.transparent); - window_state.set_blur(attributes.blur); + // Set blur. + let _ = window_state.set_blur(attributes.blur); // Set the decorations hint. window_state.set_decorate(attributes.decorations); @@ -413,7 +414,9 @@ impl Window { #[inline] pub fn set_blur(&self, blur: bool) { - self.window_state.lock().unwrap().set_blur(blur); + if self.window_state.lock().unwrap().set_blur(blur) { + self.request_redraw(); + } } #[inline] diff --git a/src/platform_impl/linux/wayland/window/state.rs b/src/platform_impl/linux/wayland/window/state.rs index 1ef7a0656c..a9b564bf95 100644 --- a/src/platform_impl/linux/wayland/window/state.rs +++ b/src/platform_impl/linux/wayland/window/state.rs @@ -28,14 +28,13 @@ use sctk::shell::WaylandSurface; use sctk::shm::slot::SlotPool; use sctk::shm::Shm; use sctk::subcompositor::SubcompositorState; -use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur; use crate::cursor::CustomCursor as RootCustomCursor; use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Size}; use crate::error::{ExternalError, NotSupportedError}; use crate::platform_impl::wayland::logical_to_physical_rounded; +use crate::platform_impl::wayland::types::bgr_effects::{BgrEffectManager, SurfaceBlurEffect}; use crate::platform_impl::wayland::types::cursor::{CustomCursor, SelectedCursor}; -use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager; use crate::platform_impl::{PlatformCustomCursor, WindowId}; use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme}; @@ -143,8 +142,8 @@ pub struct WindowState { viewport: Option, fractional_scale: Option, - blur: Option, - blur_manager: Option, + blur: Option, + blur_manager: Option, /// Whether the client side decorations have pending move operations. /// @@ -185,7 +184,7 @@ impl WindowState { Self { blur: None, - blur_manager: winit_state.kwin_blur_manager.clone(), + blur_manager: winit_state.blur_manager.clone(), compositor, connection, csd_fails: false, @@ -717,6 +716,13 @@ impl WindowState { // Set inner size without the borders. viewport.set_destination(self.size.width as _, self.size.height as _); } + + // Update blur region with new size. + if self.blur.is_some() { + // NOTE: either user resized or configure, in both cases + // the redraw scheduling is done on the caller side. + let _ = self.set_blur(true); + } } /// Get the scale factor of the window. @@ -1077,20 +1083,37 @@ 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(); + /// Make window background blurred. + /// + /// Returns `true` if redraw is required. + #[must_use] + pub fn set_blur(&mut self, blurred: bool) -> bool { + if !blurred { + self.blur = None; + return true; + } + + let mgr = match self.blur_manager.as_mut() { + Some(mgr) => mgr, + None => { + info!("Blur manager unavailable, unable to change blur"); + return false; + }, + }; + + let blur = match self.blur.as_ref() { + Some(blur) => blur, + None => { + self.blur = Some(mgr.new_blur_effect(self.window.wl_surface(), &self.queue_handle)); + self.blur.as_ref().unwrap() + }, + }; + + if let Ok(region) = Region::new(&*self.compositor) { + region.add(0, 0, i32::MAX, i32::MAX); + blur.set_blur(Some(®ion)) + } else { + false } } @@ -1149,10 +1172,6 @@ impl WindowState { impl Drop for WindowState { fn drop(&mut self) { - if let Some(blur) = self.blur.take() { - blur.release(); - } - if let Some(fs) = self.fractional_scale.take() { fs.destroy(); } diff --git a/src/window.rs b/src/window.rs index e0158eff52..d1395164dd 100644 --- a/src/window.rs +++ b/src/window.rs @@ -954,7 +954,8 @@ impl Window { /// ## Platform-specific /// /// - **Android / iOS / X11 / Web / Windows:** Unsupported. - /// - **Wayland:** Only works with org_kde_kwin_blur_manager protocol. + /// - **Wayland:** Only works with `org_kde_kwin_blur_manager` or + /// `ext_background_effect_manager_v1` protocol. #[inline] pub fn set_blur(&self, blur: bool) { let _span = tracing::debug_span!("winit::Window::set_blur", blur).entered();