diff --git a/winit-core/src/window.rs b/winit-core/src/window.rs index cfca862806..5d5a6c7ed9 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 9f5eda4037..63692578db 100644 --- a/winit-wayland/src/state.rs +++ b/winit-wayland/src/state.rs @@ -2,12 +2,13 @@ use std::cell::RefCell; use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; +use dpi::LogicalSize; use foldhash::HashMap; use sctk::compositor::{CompositorHandler, CompositorState}; use sctk::output::{OutputHandler, OutputState}; use sctk::reexports::calloop::LoopHandle; use sctk::reexports::client::backend::ObjectId; -use sctk::reexports::client::globals::GlobalList; +use sctk::reexports::client::globals::{BindError, GlobalList}; use sctk::reexports::client::protocol::wl_output::WlOutput; use sctk::reexports::client::protocol::wl_surface::WlSurface; use sctk::reexports::client::{Connection, Proxy, QueueHandle}; @@ -20,6 +21,8 @@ use sctk::shell::xdg::window::{Window, WindowConfigure, WindowHandler}; use sctk::shm::slot::SlotPool; use sctk::shm::{Shm, ShmHandler}; use sctk::subcompositor::SubcompositorState; +use wayland_protocols::ext::background_effect::v1::client::ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1; +use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur; use winit_core::error::OsError; use crate::WindowId; @@ -29,6 +32,7 @@ use crate::seat::{ PointerConstraintsState, PointerGesturesState, RelativePointerState, TextInputState, WinitPointerData, WinitPointerDataExt, WinitSeatState, }; +use crate::types::ext_background_effect::BackgroundEffectManager; use crate::types::kwin_blur::KWinBlurManager; use crate::types::wp_fractional_scaling::FractionalScalingManager; use crate::types::wp_tablet_input_v2::TabletManager; @@ -37,6 +41,77 @@ use crate::types::xdg_activation::XdgActivationState; use crate::types::xdg_toplevel_icon_manager::XdgToplevelIconManagerState; use crate::window::{WindowRequests, WindowState}; +#[derive(Debug)] +pub enum BlurSurface { + Ext(WlSurface, ExtBackgroundEffectSurfaceV1), + Kwin(OrgKdeKwinBlur), +} + +impl BlurSurface { + pub fn commit(&self) { + match self { + BlurSurface::Ext(s, _) => s.commit(), + BlurSurface::Kwin(s) => s.commit(), + } + } +} + +impl Drop for BlurSurface { + fn drop(&mut self) { + match self { + BlurSurface::Ext(_, s) => s.destroy(), + BlurSurface::Kwin(s) => s.release(), + } + } +} + +#[derive(Debug, Clone)] +pub enum BlurManager { + Ext(BackgroundEffectManager), + KWin(KWinBlurManager), +} + +impl BlurManager { + pub fn new( + globals: &GlobalList, + queue_handle: &QueueHandle, + ) -> Result { + match BackgroundEffectManager::new(globals, queue_handle) { + Ok(m) => Ok(Self::Ext(m)), + Err(e) => { + if let Ok(m) = KWinBlurManager::new(globals, queue_handle) { + Ok(Self::KWin(m)) + } else { + Err(e) + } + }, + } + } + + pub fn blur( + &mut self, + compositor_state: &Arc, + surface: &WlSurface, + queue_handle: &QueueHandle, + size: LogicalSize, + ) -> BlurSurface { + match self { + BlurManager::Ext(m) => BlurSurface::Ext( + surface.clone(), + m.blur(compositor_state, surface, queue_handle, size), + ), + BlurManager::KWin(m) => BlurSurface::Kwin(m.blur(surface, queue_handle)), + } + } + + pub fn unset(&mut self, surface: &WlSurface) { + match self { + BlurManager::Ext(m) => m.unset(surface), + BlurManager::KWin(m) => m.unset(surface), + } + } +} + /// Winit's Wayland state. #[derive(Debug)] pub struct WinitState { @@ -116,8 +191,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 +267,7 @@ impl WinitState { window_events_sink: Default::default(), viewporter_state, fractional_scaling_manager, - kwin_blur_manager: KWinBlurManager::new(globals, queue_handle).ok(), + blur_manager: BlurManager::new(globals, queue_handle).ok(), seats, text_input_state: TextInputState::new(globals, queue_handle).ok(), 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 0000000000..76bd577c39 --- /dev/null +++ b/winit-wayland/src/types/ext_background_effect.rs @@ -0,0 +1,83 @@ +use std::collections::HashMap; +use std::ops::Deref; +use std::sync::Arc; + +use dpi::LogicalSize; +use sctk::compositor::{CompositorState, Region}; +use sctk::globals::GlobalData; +use sctk::reexports::client::backend::ObjectId; +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 BackgroundEffectManager { + manager: ExtBackgroundEffectManagerV1, + surfaces: HashMap, +} + +impl BackgroundEffectManager { + pub fn new( + globals: &GlobalList, + queue_handle: &QueueHandle, + ) -> Result { + let manager = globals.bind(queue_handle, 1..=1, GlobalData)?; + Ok(Self { manager, surfaces: HashMap::new() }) + } + + pub fn blur( + &mut self, + compositor_state: &Arc, + surface: &WlSurface, + queue_handle: &QueueHandle, + size: LogicalSize, + ) -> ExtBackgroundEffectSurfaceV1 { + let region = Region::new(compositor_state.deref()).unwrap(); + region.add(0, 0, size.width as i32, size.height as i32); + let surface = if let Some(existing) = self.surfaces.get(&surface.id()) { + existing.clone() + } else { + let surface = self.manager.get_background_effect(surface, queue_handle, ()); + self.surfaces.insert(surface.id(), surface.clone()); + surface + }; + surface.set_blur_region(Some(region.wl_region())); + surface + } + + pub fn unset(&mut self, surface: &WlSurface) { + self.surfaces.remove(&surface.id()); + } +} + +impl Dispatch for BackgroundEffectManager { + fn event( + _: &mut WinitState, + _: &ExtBackgroundEffectManagerV1, + _: ::Event, + _: &GlobalData, + _: &Connection, + _: &QueueHandle, + ) { + } +} + +impl Dispatch for BackgroundEffectManager { + fn event( + _: &mut WinitState, + _: &ExtBackgroundEffectSurfaceV1, + _: ::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + // There is no event + } +} + +delegate_dispatch!(WinitState: [ExtBackgroundEffectManagerV1: GlobalData] => BackgroundEffectManager); +delegate_dispatch!(WinitState: [ExtBackgroundEffectSurfaceV1: ()] => BackgroundEffectManager); diff --git a/winit-wayland/src/types/mod.rs b/winit-wayland/src/types/mod.rs index 03b31bf58e..a578efd09d 100644 --- a/winit-wayland/src/types/mod.rs +++ b/winit-wayland/src/types/mod.rs @@ -1,6 +1,7 @@ //! Wayland protocol implementation boilerplate. 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/state.rs b/winit-wayland/src/window/state.rs index d340742a15..090159d619 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::{ @@ -42,9 +41,8 @@ use crate::seat::{ PointerConstraintsState, TextInputClientState, WinitPointerData, WinitPointerDataExt, ZwpTextInputV3Ext, }; -use crate::state::{WindowCompositorUpdate, WinitState}; +use crate::state::{BlurManager, BlurSurface, WindowCompositorUpdate, WinitState}; 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 +153,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 +203,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 +702,21 @@ 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() { + if let Some(blur_manager) = self.blur_manager.as_mut() { + drop(self.blur.take().unwrap()); + let blur = blur_manager.blur( + &self.compositor, + self.window.wl_surface(), + &self.queue_handle, + self.size, + ); + blur.commit(); + self.blur = Some(blur); + } + } } /// Get the scale factor of the window. @@ -1067,16 +1080,21 @@ impl WindowState { #[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); + if let Some(blur_manager) = self.blur_manager.as_mut() { + let blur = blur_manager.blur( + &self.compositor, + self.window.wl_surface(), + &self.queue_handle, + self.size, + ); 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(); + self.blur_manager.as_mut().unwrap().unset(self.window.wl_surface()); + drop(self.blur.take().unwrap()); } } @@ -1174,10 +1192,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(); }