Compare commits

...

1 Commits

Author SHA1 Message Date
Ivan Molodetskikh b93514a82a Implement the kde-blur protocol
Will likely drop this commit as even KDE removed this protocol in favor of ext-background-effect:
https://invent.kde.org/plasma/kwin/-/merge_requests/8798
2026-04-15 14:36:17 +03:00
6 changed files with 260 additions and 1 deletions
Generated
+2
View File
@@ -2257,6 +2257,7 @@ dependencies = [
"tracy-client",
"wayland-backend",
"wayland-client",
"wayland-protocols-plasma",
"wayland-scanner",
"wayland-server",
"xcursor",
@@ -4218,6 +4219,7 @@ dependencies = [
"wayland-client",
"wayland-protocols",
"wayland-scanner",
"wayland-server",
]
[[package]]
+1
View File
@@ -95,6 +95,7 @@ tracing-subscriber.workspace = true
tracing.workspace = true
tracy-client.workspace = true
wayland-backend = "0.3.14"
wayland-protocols-plasma = { version = "0.3.11", features = ["server"] }
wayland-scanner = "0.31.9"
wayland-server = { version = "0.31.12", features = ["libwayland_1_23"] }
xcursor = "0.3.10"
+38 -1
View File
@@ -2,7 +2,7 @@ use std::sync::{Arc, Mutex};
use smithay::delegate_background_effect;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::utils::{Logical, Rectangle};
use smithay::utils::{Logical, Point, Rectangle, Size};
use smithay::wayland::background_effect::{
self, BackgroundEffectSurfaceCachedState, ExtBackgroundEffectHandler,
};
@@ -10,7 +10,9 @@ use smithay::wayland::compositor::{
add_post_commit_hook, with_states, RegionAttributes, SurfaceData,
};
use crate::delegate_kde_blur;
use crate::niri::State;
use crate::protocols::kde_blur::{KdeBlurHandler, KdeBlurRegion, KdeBlurSurfaceCachedState};
use crate::utils::region::region_to_non_overlapping_rects;
/// Per-surface cache for processed blur region (non-overlapping rects).
@@ -59,6 +61,7 @@ fn recompute_blur_region(states: &SurfaceData, inner: &mut CachedBlurRegionInner
};
let rects = Arc::make_mut(rects);
// Prefer ext-background-effect.
if cached.has::<BackgroundEffectSurfaceCachedState>() {
let mut guard = cached.get::<BackgroundEffectSurfaceCachedState>();
if let Some(region) = &guard.current().blur_region {
@@ -69,6 +72,29 @@ fn recompute_blur_region(states: &SurfaceData, inner: &mut CachedBlurRegionInner
return;
}
if cached.has::<KdeBlurSurfaceCachedState>() {
let mut guard = cached.get::<KdeBlurSurfaceCachedState>();
match &guard.current().blur_region {
Some(KdeBlurRegion::WholeSurface) => {
// Store a single "infinite" rect that gets naturally clipped.
let infinite = Rectangle::new(
Point::new(-i32::MAX / 2, -i32::MAX / 2),
Size::new(i32::MAX, i32::MAX),
);
rects.clear();
rects.push(infinite);
}
Some(KdeBlurRegion::Region(region)) => {
region_to_non_overlapping_rects(region, rects);
}
None => {
inner.rects = None;
}
}
return;
}
// Neither is present.
inner.rects = None;
}
@@ -121,3 +147,14 @@ impl ExtBackgroundEffectHandler for State {
}
}
delegate_background_effect!(State);
impl KdeBlurHandler for State {
fn set_blur_region(&mut self, wl_surface: WlSurface) {
mark_blur_region_pending_dirty(&wl_surface);
}
fn unset_blur_region(&mut self, wl_surface: WlSurface) {
mark_blur_region_pending_dirty(&wl_surface);
}
}
delegate_kde_blur!(State);
+4
View File
@@ -146,6 +146,7 @@ use crate::niri_render_elements;
use crate::protocols::ext_workspace::{self, ExtWorkspaceManagerState};
use crate::protocols::foreign_toplevel::{self, ForeignToplevelManagerState};
use crate::protocols::gamma_control::GammaControlManagerState;
use crate::protocols::kde_blur::KdeBlurState;
use crate::protocols::mutter_x11_interop::MutterX11InteropManagerState;
use crate::protocols::output_management::OutputManagementManagerState;
use crate::protocols::screencopy::{Screencopy, ScreencopyBuffer, ScreencopyManagerState};
@@ -282,6 +283,7 @@ pub struct Niri {
pub output_management_state: OutputManagementManagerState,
pub viewporter_state: ViewporterState,
pub background_effect_state: BackgroundEffectState,
pub kde_blur_state: KdeBlurState,
pub xdg_foreign_state: XdgForeignState,
pub shm_state: ShmState,
pub output_manager_state: OutputManagerState,
@@ -2332,6 +2334,7 @@ impl Niri {
ScreencopyManagerState::new::<State, _>(&display_handle, client_is_unrestricted);
let viewporter_state = ViewporterState::new::<State>(&display_handle);
let background_effect_state = BackgroundEffectState::new::<State>(&display_handle);
let kde_blur_state = KdeBlurState::new::<State>(&display_handle);
let xdg_foreign_state = XdgForeignState::new::<State>(&display_handle);
let is_tty = matches!(backend, Backend::Tty(_));
@@ -2516,6 +2519,7 @@ impl Niri {
screencopy_state,
viewporter_state,
background_effect_state,
kde_blur_state,
xdg_foreign_state,
text_input_state,
input_method_state,
+214
View File
@@ -0,0 +1,214 @@
use std::sync::Mutex;
use smithay::reexports::wayland_server;
use smithay::wayland::compositor::{
get_region_attributes, with_states, Cacheable, RegionAttributes,
};
use wayland_protocols_plasma::blur::server::org_kde_kwin_blur::{self, OrgKdeKwinBlur};
use wayland_protocols_plasma::blur::server::org_kde_kwin_blur_manager::{
self, OrgKdeKwinBlurManager,
};
use wayland_server::backend::GlobalId;
use wayland_server::protocol::wl_surface::WlSurface;
use wayland_server::{
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak,
};
pub trait KdeBlurHandler:
GlobalDispatch<OrgKdeKwinBlurManager, ()>
+ Dispatch<OrgKdeKwinBlurManager, ()>
+ Dispatch<OrgKdeKwinBlur, KdeBlurSurfaceUserData>
+ 'static
{
/// Called when a blur region becomes pending on a surface, awaiting a commit.
fn set_blur_region(&mut self, wl_surface: WlSurface) {
let _ = wl_surface;
}
/// Called when a blur region unset becomes pending on a surface, awaiting a commit.
fn unset_blur_region(&mut self, wl_surface: WlSurface) {
let _ = wl_surface;
}
}
#[derive(Debug, Clone, Default)]
pub struct KdeBlurSurfaceCachedState {
/// Region of the surface that will have its background blurred.
///
/// `None` means no blurring.
pub blur_region: Option<KdeBlurRegion>,
}
#[derive(Debug, Clone, Default)]
pub enum KdeBlurRegion {
#[default]
WholeSurface,
Region(RegionAttributes),
}
impl Cacheable for KdeBlurSurfaceCachedState {
fn commit(&mut self, _dh: &DisplayHandle) -> Self {
self.clone()
}
fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) {
*into = self;
}
}
#[derive(Debug)]
pub struct KdeBlurSurfaceUserData {
surface: Weak<WlSurface>,
pending_region: Mutex<KdeBlurRegion>,
}
impl KdeBlurSurfaceUserData {
fn new(surface: WlSurface) -> Self {
Self {
surface: surface.downgrade(),
pending_region: Mutex::new(KdeBlurRegion::WholeSurface),
}
}
fn wl_surface(&self) -> Option<WlSurface> {
self.surface.upgrade().ok()
}
}
#[derive(Debug)]
pub struct KdeBlurState {
global: GlobalId,
}
impl KdeBlurState {
pub fn new<D: KdeBlurHandler>(display: &DisplayHandle) -> KdeBlurState {
let global = display.create_global::<D, OrgKdeKwinBlurManager, _>(1, ());
KdeBlurState { global }
}
pub fn global(&self) -> GlobalId {
self.global.clone()
}
}
impl<D: KdeBlurHandler> GlobalDispatch<OrgKdeKwinBlurManager, (), D> for KdeBlurState {
fn bind(
_state: &mut D,
_handle: &DisplayHandle,
_client: &Client,
resource: New<OrgKdeKwinBlurManager>,
_global_data: &(),
data_init: &mut DataInit<'_, D>,
) {
let _manager = data_init.init(resource, ());
}
}
impl<D: KdeBlurHandler> Dispatch<OrgKdeKwinBlurManager, (), D> for KdeBlurState {
fn request(
state: &mut D,
_client: &Client,
_manager: &OrgKdeKwinBlurManager,
request: org_kde_kwin_blur_manager::Request,
_data: &(),
_dh: &DisplayHandle,
data_init: &mut DataInit<'_, D>,
) {
match request {
org_kde_kwin_blur_manager::Request::Create { id, surface } => {
data_init.init(id, KdeBlurSurfaceUserData::new(surface));
}
org_kde_kwin_blur_manager::Request::Unset { surface } => {
with_states(&surface, |states| {
let mut cached = states.cached_state.get::<KdeBlurSurfaceCachedState>();
let pending = cached.pending();
pending.blur_region = None;
});
state.unset_blur_region(surface);
}
_ => {}
}
}
}
impl<D: KdeBlurHandler> Dispatch<OrgKdeKwinBlur, KdeBlurSurfaceUserData, D> for KdeBlurState {
fn request(
state: &mut D,
_client: &Client,
_obj: &OrgKdeKwinBlur,
request: org_kde_kwin_blur::Request,
data: &KdeBlurSurfaceUserData,
_dh: &DisplayHandle,
_data_init: &mut DataInit<'_, D>,
) {
match request {
org_kde_kwin_blur::Request::SetRegion { region } => {
let region = region.as_ref().map(get_region_attributes);
// In the KDE blur protocol, an empty region means whole surface.
let region = match region {
Some(region) if !region.rects.is_empty() => KdeBlurRegion::Region(region),
_ => KdeBlurRegion::WholeSurface,
};
*data.pending_region.lock().unwrap() = region;
}
org_kde_kwin_blur::Request::Commit => {
let Some(surface) = data.wl_surface() else {
return;
};
with_states(&surface, |states| {
let mut cached = states.cached_state.get::<KdeBlurSurfaceCachedState>();
let pending = cached.pending();
let region = data.pending_region.lock().unwrap().clone();
pending.blur_region = Some(region);
});
state.set_blur_region(surface);
}
org_kde_kwin_blur::Request::Release => {
// No-op.
}
_ => {}
}
}
fn destroyed(
_state: &mut D,
_client_id: wayland_server::backend::ClientId,
_object: &OrgKdeKwinBlur,
_data: &KdeBlurSurfaceUserData,
) {
// No-op: cleanup is handled by double-buffering and surface destruction
}
}
#[macro_export]
macro_rules! delegate_kde_blur {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
const _: () = {
use smithay::reexports::wayland_server;
use wayland_protocols_plasma::blur::server::{
org_kde_kwin_blur_manager::OrgKdeKwinBlurManager,
org_kde_kwin_blur::OrgKdeKwinBlur,
};
use wayland_server::{delegate_dispatch, delegate_global_dispatch};
use $crate::protocols::kde_blur::{KdeBlurState, KdeBlurSurfaceUserData};
delegate_global_dispatch!(
$(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
$ty: [OrgKdeKwinBlurManager: ()] => KdeBlurState
);
delegate_dispatch!(
$(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
$ty: [OrgKdeKwinBlurManager: ()] => KdeBlurState
);
delegate_dispatch!(
$(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
$ty: [OrgKdeKwinBlur: KdeBlurSurfaceUserData] => KdeBlurState
);
};
};
}
+1
View File
@@ -1,6 +1,7 @@
pub mod ext_workspace;
pub mod foreign_toplevel;
pub mod gamma_control;
pub mod kde_blur;
pub mod mutter_x11_interop;
pub mod output_management;
pub mod screencopy;