-
Notifications
You must be signed in to change notification settings - Fork 1.2k
wayland: use ext-background-effect if available
#4496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) { | ||
|
Comment on lines
+44
to
+60
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move all of that somewhere else, like |
||
| 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<WinitState>, | ||
| ) -> Result<Self, BindError> { | ||
| 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<CompositorState>, | ||
| surface: &WlSurface, | ||
| queue_handle: &QueueHandle<WinitState>, | ||
| size: LogicalSize<u32>, | ||
| ) -> 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<FractionalScalingManager>, | ||
|
|
||
| /// KWin blur manager. | ||
| pub kwin_blur_manager: Option<KWinBlurManager>, | ||
| /// Blur manager. | ||
| pub blur_manager: Option<BlurManager>, | ||
|
|
||
| /// 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(), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<ObjectId, ExtBackgroundEffectSurfaceV1>, | ||
| } | ||
|
|
||
| impl BackgroundEffectManager { | ||
| pub fn new( | ||
| globals: &GlobalList, | ||
| queue_handle: &QueueHandle<WinitState>, | ||
| ) -> Result<Self, BindError> { | ||
| let manager = globals.bind(queue_handle, 1..=1, GlobalData)?; | ||
| Ok(Self { manager, surfaces: HashMap::new() }) | ||
| } | ||
|
|
||
| pub fn blur( | ||
| &mut self, | ||
| compositor_state: &Arc<CompositorState>, | ||
| surface: &WlSurface, | ||
| queue_handle: &QueueHandle<WinitState>, | ||
| size: LogicalSize<u32>, | ||
| ) -> ExtBackgroundEffectSurfaceV1 { | ||
| let region = Region::new(compositor_state.deref()).unwrap(); | ||
| region.add(0, 0, size.width as i32, size.height as i32); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we just follow
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I asked Yalter about this and he said that the surface object when created has no effect, so the client have to call |
||
| 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<ExtBackgroundEffectManagerV1, GlobalData, WinitState> for BackgroundEffectManager { | ||
| fn event( | ||
| _: &mut WinitState, | ||
| _: &ExtBackgroundEffectManagerV1, | ||
| _: <ExtBackgroundEffectManagerV1 as Proxy>::Event, | ||
| _: &GlobalData, | ||
| _: &Connection, | ||
| _: &QueueHandle<WinitState>, | ||
| ) { | ||
| } | ||
| } | ||
|
|
||
| impl Dispatch<ExtBackgroundEffectSurfaceV1, (), WinitState> for BackgroundEffectManager { | ||
| fn event( | ||
| _: &mut WinitState, | ||
| _: &ExtBackgroundEffectSurfaceV1, | ||
| _: <ExtBackgroundEffectSurfaceV1 as Proxy>::Event, | ||
| _: &(), | ||
| _: &Connection, | ||
| _: &QueueHandle<WinitState>, | ||
| ) { | ||
| // There is no event | ||
| } | ||
| } | ||
|
|
||
| delegate_dispatch!(WinitState: [ExtBackgroundEffectManagerV1: GlobalData] => BackgroundEffectManager); | ||
| delegate_dispatch!(WinitState: [ExtBackgroundEffectSurfaceV1: ()] => BackgroundEffectManager); | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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<WpViewport>, | ||||||
| fractional_scale: Option<WpFractionalScaleV1>, | ||||||
| blur: Option<OrgKdeKwinBlur>, | ||||||
| blur_manager: Option<KWinBlurManager>, | ||||||
| blur: Option<BlurSurface>, | ||||||
| blur_manager: Option<BlurManager>, | ||||||
|
|
||||||
| /// 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()); | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -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(); | ||||||
| } | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be an extreme nitpick but should this comment say "or", so it's clear that blur works with either the KDE or the generic protocol? Instead of sounding like it might require both
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah good idea.