Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion winit-core/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.

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

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah good idea.

fn set_blur(&self, blur: bool);

/// Modifies the window's visibility.
Expand Down
83 changes: 79 additions & 4 deletions winit-wayland/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move all of that somewhere else, like types/blur.

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 {
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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(),
Expand Down
83 changes: 83 additions & 0 deletions winit-wayland/src/types/ext_background_effect.rs
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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just follow KDE blur impl? It's the same protocol, so not sure why it has to be different to what we have. Not sure about region though, but usually i32::MAX does the job because it's clipped.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the same protocol, so not sure why it has to be different to what we have.

I asked Yalter about this and he said that the surface object when created has no effect, so the client have to call set_blur_region to ask the compositor to blur the surface. I'll try to see if i32::MAX works here.

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);
1 change: 1 addition & 0 deletions winit-wayland/src/types/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
42 changes: 28 additions & 14 deletions winit-wayland/src/window/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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")]
Expand Down Expand Up @@ -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.
///
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
drop(self.blur.take().unwrap());
self.blur = None;

}
}

Expand Down Expand Up @@ -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();
}
Expand Down