diff --git a/winit-core/src/window.rs b/winit-core/src/window.rs index aa7f1710..f22f35e5 100644 --- a/winit-core/src/window.rs +++ b/winit-core/src/window.rs @@ -881,7 +881,8 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// ## 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. fn set_blur(&self, blur: bool); /// Modifies the window's visibility. diff --git a/winit-wayland/src/state.rs b/winit-wayland/src/state.rs index dc9b9e35..adf32e4b 100644 --- a/winit-wayland/src/state.rs +++ b/winit-wayland/src/state.rs @@ -29,7 +29,7 @@ use crate::seat::{ PointerConstraintsState, PointerGesturesState, RelativePointerState, TextInputState, WinitPointerData, WinitPointerDataExt, WinitSeatState, }; -use crate::types::kwin_blur::KWinBlurManager; +use crate::types::bgr_effects::BgrEffectManager; use crate::types::wp_fractional_scaling::FractionalScalingManager; use crate::types::wp_tablet_input_v2::TabletManager; use crate::types::wp_viewporter::ViewporterState; @@ -116,8 +116,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>, @@ -192,7 +192,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/winit-wayland/src/types/bgr_effects.rs b/winit-wayland/src/types/bgr_effects.rs new file mode 100644 index 00000000..0607d907 --- /dev/null +++ b/winit-wayland/src/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::state::WinitState; +use crate::types::ext_background_effect::ExtBackgroundEffectManager; +use crate::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/winit-wayland/src/types/ext_background_effect.rs b/winit-wayland/src/types/ext_background_effect.rs new file mode 100644 index 00000000..6ef1e27c --- /dev/null +++ b/winit-wayland/src/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::{Connection, Dispatch, Proxy, QueueHandle, delegate_dispatch}; +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::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/winit-wayland/src/types/mod.rs b/winit-wayland/src/types/mod.rs index 03b31bf5..2758da43 100644 --- a/winit-wayland/src/types/mod.rs +++ b/winit-wayland/src/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_tablet_input_v2; diff --git a/winit-wayland/src/window/mod.rs b/winit-wayland/src/window/mod.rs index a2f5ca30..ad6b01d5 100644 --- a/winit-wayland/src/window/mod.rs +++ b/winit-wayland/src/window/mod.rs @@ -128,7 +128,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); @@ -491,7 +492,9 @@ impl CoreWindow for Window { #[inline] 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/winit-wayland/src/window/state.rs b/winit-wayland/src/window/state.rs index 86711bdd..4c072cd9 100644 --- a/winit-wayland/src/window/state.rs +++ b/winit-wayland/src/window/state.rs @@ -29,7 +29,6 @@ use sctk::shm::slot::SlotPool; use sctk::subcompositor::SubcompositorState; use tracing::{info, warn}; use wayland_protocols::xdg::toplevel_icon::v1::client::xdg_toplevel_icon_manager_v1::XdgToplevelIconManagerV1; -use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur; use winit_core::cursor::{CursorIcon, CustomCursor as CoreCustomCursor}; use winit_core::error::{NotSupportedError, RequestError}; use winit_core::window::{ @@ -43,8 +42,8 @@ use crate::seat::{ ZwpTextInputV3Ext, }; use crate::state::{WindowCompositorUpdate, WinitState}; +use crate::types::bgr_effects::{BgrEffectManager, SurfaceBlurEffect}; use crate::types::cursor::{CustomCursor, SelectedCursor, WaylandCustomCursor}; -use crate::types::kwin_blur::KWinBlurManager; use crate::types::xdg_toplevel_icon_manager::ToplevelIcon; #[cfg(feature = "sctk-adwaita")] @@ -155,8 +154,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. /// @@ -205,7 +204,7 @@ impl WindowState { toplevel_icon: None, xdg_toplevel_icon_manager, blur: None, - blur_manager: winit_state.kwin_blur_manager.clone(), + blur_manager: winit_state.blur_manager.clone(), compositor, handle, csd_fails: false, @@ -704,6 +703,13 @@ impl WindowState { // Set surface 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. @@ -1063,20 +1069,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 } } @@ -1174,10 +1197,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(); }