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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ You also no longer need to manually set `LayerSurfaceAttributes::initial_configu

### Additions

- ExtBackgroundEffect protocol is now available in `smithay::wayland::background_effect` module.

`crate::input::dnd` was introduced to enable implementation of Drag&Drop operations on custom types.
Internally the same types and traits are used to implement `wayland::data_device` dnd-operations and XDND
operations (see below).
Expand Down
126 changes: 126 additions & 0 deletions src/wayland/background_effect/dispatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use crate::wayland::background_effect::{
BackgroundEffectState, BackgroundEffectSurfaceData, ExtBackgroundEffectHandler,
};
use crate::wayland::compositor;
use crate::wayland::{
background_effect::{BackgroundEffectSurfaceCachedState, BackgroundEffectSurfaceUserData},
compositor::with_states,
};
use wayland_protocols::ext::background_effect::v1::server::{
ext_background_effect_manager_v1::{
Error as ManagerError, ExtBackgroundEffectManagerV1, Request as ManagerRequest,
},
ext_background_effect_surface_v1::{
Error as SurfaceError, ExtBackgroundEffectSurfaceV1, Request as SurfaceRequest,
},
};
use wayland_server::{Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource};

impl<D: ExtBackgroundEffectHandler> GlobalDispatch<ExtBackgroundEffectManagerV1, (), D>
for BackgroundEffectState
{
fn bind(
state: &mut D,
_handle: &DisplayHandle,
_client: &Client,
resource: New<ExtBackgroundEffectManagerV1>,
_global_data: &(),
data_init: &mut DataInit<'_, D>,
) {
let manager = data_init.init(resource, ());
manager.capabilities(state.capabilities());
}
}

impl<D: ExtBackgroundEffectHandler> Dispatch<ExtBackgroundEffectManagerV1, (), D> for BackgroundEffectState {
fn request(
_state: &mut D,
_client: &Client,
manager: &ExtBackgroundEffectManagerV1,
request: ManagerRequest,
_data: &(),
_dh: &DisplayHandle,
data_init: &mut DataInit<'_, D>,
) {
match request {
ManagerRequest::GetBackgroundEffect { id, surface } => {
let already_taken = with_states(&surface, |states| {
let data = states
.data_map
.get_or_insert_threadsafe(BackgroundEffectSurfaceData::new);
let already = data.is_resource_attached();
if !already {
data.set_is_resource_attached(true);
}
already
});

if already_taken {
manager.post_error(
ManagerError::BackgroundEffectExists,
"wl_surface already has a background effect object attached",
);
} else {
data_init.init(id, BackgroundEffectSurfaceUserData::new(surface));
}
}
ManagerRequest::Destroy => {}
_ => {}
}
}
}

impl<D: ExtBackgroundEffectHandler> Dispatch<ExtBackgroundEffectSurfaceV1, BackgroundEffectSurfaceUserData, D>
for BackgroundEffectState
{
fn request(
_state: &mut D,
_client: &Client,
obj: &ExtBackgroundEffectSurfaceV1,
request: SurfaceRequest,
data: &BackgroundEffectSurfaceUserData,
_dh: &DisplayHandle,
_data_init: &mut DataInit<'_, D>,
) {
match request {
SurfaceRequest::SetBlurRegion { region } => {
let Some(surface) = data.wl_surface() else {
obj.post_error(SurfaceError::SurfaceDestroyed, "wl_surface was destroyed");
return;
};

with_states(&surface, |states| {
let mut cached = states.cached_state.get::<BackgroundEffectSurfaceCachedState>();
let pending = cached.pending();
pending.blur_region = region.map(|r| compositor::get_region_attributes(&r));
});
}
SurfaceRequest::Destroy => {
let Some(surface) = data.wl_surface() else {
obj.post_error(SurfaceError::SurfaceDestroyed, "wl_surface was destroyed");
return;
};

with_states(&surface, |states| {
states
.data_map
.get::<BackgroundEffectSurfaceData>()
.unwrap()
.set_is_resource_attached(false);
let mut cached = states.cached_state.get::<BackgroundEffectSurfaceCachedState>();
cached.pending().blur_region = None;
});
}
_ => {}
}
}

fn destroyed(
_state: &mut D,
_client_id: wayland_server::backend::ClientId,
_object: &ExtBackgroundEffectSurfaceV1,
_data: &BackgroundEffectSurfaceUserData,
) {
// No-op: cleanup is handled by double-buffering and surface destruction
}
}
189 changes: 189 additions & 0 deletions src/wayland/background_effect/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
//! Implementation of `ext_background_effect_manager_v1` protocol
//!
//! In order to advertise background effect global call [BackgroundEffectState::new] and delegate
//! events to it with [`delegate_background_effect`][crate::delegate_background_effect].
//! Currently attached effects are available in double-buffered [BackgroundEffectSurfaceCachedState]
//!
//! ```
//! use smithay::wayland::background_effect::{BackgroundEffectState, ExtBackgroundEffectHandler, Capability};
//! use wayland_server::protocol::wl_surface::WlSurface;
//! use smithay::delegate_background_effect;
//! use smithay::wayland::compositor;
//!
//! # struct State;
//! # let mut display = wayland_server::Display::<State>::new().unwrap();
//!
//! BackgroundEffectState::new::<State>(
//! &display.handle(),
//! );
//!
//! impl ExtBackgroundEffectHandler for State {
//! fn capabilities(&self) -> Capability {
//! Capability::Blur
//! }
//!
//! fn set_blur_region(&mut self, wl_surface: WlSurface, region: compositor::RegionAttributes) {
//! // Called when blur becomes pending, and awaits surface commit.
//! // Blur region is stored in wl_surface [BackgroundEffectSurfaceCachedState]
//! }
//! }
//!
//! delegate_background_effect!(State);
//! ```

use std::sync::{
atomic::{AtomicBool, Ordering},
Mutex,
};

use crate::wayland::compositor::{self, Cacheable};
use wayland_protocols::ext::background_effect::v1::server::{
ext_background_effect_manager_v1::{self, ExtBackgroundEffectManagerV1},
ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1,
};
use wayland_server::{
backend::GlobalId, protocol::wl_surface::WlSurface, Dispatch, DisplayHandle, GlobalDispatch, Resource,
Weak,
};

pub use ext_background_effect_manager_v1::Capability;

mod dispatch;

/// Handler trait for ext background effect events.
pub trait ExtBackgroundEffectHandler:
GlobalDispatch<ExtBackgroundEffectManagerV1, ()>
+ Dispatch<ExtBackgroundEffectManagerV1, ()>
+ Dispatch<ExtBackgroundEffectSurfaceV1, BackgroundEffectSurfaceUserData>
+ 'static
{
/// Getter for background-effect capabilities of the compositor
fn capabilities(&self) -> Capability {
// For now only blur is supported, so it's safe to assume it's supported by default
Capability::Blur
}

/// Called when blur becomes pending, and awaits surface commit.
/// Blur region is stored in wl_surface [BackgroundEffectSurfaceCachedState]
fn set_blur_region(&mut self, wl_surface: WlSurface, region: compositor::RegionAttributes) {
let _ = wl_surface;
let _ = region;
}
}

/// Cached state for background effect per surface (double-buffered)
///
/// ```no_run
/// use smithay::wayland::compositor;
/// use smithay::wayland::background_effect::BackgroundEffectSurfaceCachedState;
///
/// # let wl_surface = todo!();
/// compositor::with_states(&wl_surface, |states| {
/// let mut modifier_state = states.cached_state.get::<BackgroundEffectSurfaceCachedState>();
/// dbg!(&modifier_state.current().blur_region);
/// });
/// ```
#[derive(Debug, Clone, Default)]
pub struct BackgroundEffectSurfaceCachedState {
/// Region of the surface that will have its background blurred.
pub blur_region: Option<compositor::RegionAttributes>,
}

impl Cacheable for BackgroundEffectSurfaceCachedState {
fn commit(&mut self, _dh: &DisplayHandle) -> Self {
self.clone()
}

fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) {
*into = self;
}
}

/// User data for ext_background_effect_surface_v1
#[derive(Debug)]
pub struct BackgroundEffectSurfaceUserData(Mutex<Weak<WlSurface>>);

impl BackgroundEffectSurfaceUserData {
fn new(surface: WlSurface) -> Self {
Self(Mutex::new(surface.downgrade()))
}

fn wl_surface(&self) -> Option<WlSurface> {
self.0.lock().unwrap().upgrade().ok()
}
}

/// Tracks if a surface already has a background effect object
#[derive(Debug)]
struct BackgroundEffectSurfaceData {
is_resource_attached: AtomicBool,
}

impl BackgroundEffectSurfaceData {
fn new() -> Self {
Self {
is_resource_attached: AtomicBool::new(false),
}
}

fn set_is_resource_attached(&self, is_attached: bool) {
self.is_resource_attached.store(is_attached, Ordering::Release)
}

fn is_resource_attached(&self) -> bool {
self.is_resource_attached.load(Ordering::Acquire)
}
}

/// Global state for background effect protocol
#[derive(Debug)]
pub struct BackgroundEffectState {
global: GlobalId,
}

impl BackgroundEffectState {
/// Regiseter new [ExtBackgroundEffectManagerV1] global
pub fn new<D: ExtBackgroundEffectHandler>(display: &DisplayHandle) -> BackgroundEffectState {
let global = display.create_global::<D, ExtBackgroundEffectManagerV1, _>(1, ());
BackgroundEffectState { global }
}

/// Returns the [ExtBackgroundEffectManagerV1] global id
pub fn global(&self) -> GlobalId {
self.global.clone()
}
}

/// Macro to delegate implementation of the background effect protocol
#[macro_export]
macro_rules! delegate_background_effect {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
const _: () = {
use $crate::{
reexports::{
wayland_protocols::ext::background_effect::v1::server::{
ext_background_effect_manager_v1::ExtBackgroundEffectManagerV1,
ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1,
},
wayland_server::{delegate_dispatch, delegate_global_dispatch},
},
wayland::background_effect::{BackgroundEffectState, BackgroundEffectSurfaceUserData},
};

delegate_global_dispatch!(
$(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
$ty: [ExtBackgroundEffectManagerV1: ()] => BackgroundEffectState
);

delegate_dispatch!(
$(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
$ty: [ExtBackgroundEffectManagerV1: ()] => BackgroundEffectState
);

delegate_dispatch!(
$(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
$ty: [ExtBackgroundEffectSurfaceV1: BackgroundEffectSurfaceUserData] => BackgroundEffectState
);
};
};
}
1 change: 1 addition & 0 deletions src/wayland/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
//!
pub mod alpha_modifier;
pub mod background_effect;
pub mod buffer;
pub mod commit_timing;
pub mod compositor;
Expand Down
Loading