diff --git a/Cargo.toml b/Cargo.toml index 51f3b29bfae8..5ade59863388 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "smithay" version = "0.6.0" -authors = ["Victor Berger ", "Drakulix (Victoria Brekenfeld)"] +authors = [ + "Victor Berger ", + "Drakulix (Victoria Brekenfeld)", +] license = "MIT" description = "Smithay is a library for writing wayland compositors." repository = "https://github.com/Smithay/smithay" @@ -37,14 +40,27 @@ drm-fourcc = "^2.2.0" drm = { version = "0.14.0", optional = true } drm-ffi = { version = "0.9.0", optional = true } errno = "0.3.5" -gbm = { version = "0.18.0", optional = true, default-features = false, features = ["drm-support"] } +gbm = { version = "0.18.0", optional = true, default-features = false, features = [ + "drm-support", +] } glow = { version = "0.16", optional = true } -input = { version = "0.9.0", default-features = false, features=["libinput_1_19"], optional = true } +input = { version = "0.9.0", default-features = false, features = [ + "libinput_1_19", +], optional = true } indexmap = "2.0" libc = "0.2.103" libseat = { version = "0.2.3", optional = true, default-features = false } -libloading = { version="0.8.0", optional = true } -rustix = { version = "1.0.7", features = ["event", "fs", "mm", "net", "pipe", "process", "shm", "time"] } +libloading = { version = "0.8.0", optional = true } +rustix = { version = "1.0.7", features = [ + "event", + "fs", + "mm", + "net", + "pipe", + "process", + "shm", + "time", +] } rand = "0.9.0" scopeguard = { version = "1.1.0", optional = true } tracing = "0.1.37" @@ -56,19 +72,35 @@ udev = { version = "0.9.0", optional = true } wayland-client = { version = "0.31.10", optional = true } wayland-cursor = { version = "0.31.10", optional = true } wayland-egl = { version = "0.32.7", optional = true } -wayland-protocols = { version = "0.32.8", features = ["unstable", "staging", "server"], optional = true } -wayland-protocols-wlr = { version = "0.3.8", features = ["server"], optional = true } -wayland-protocols-misc = { version = "0.3.8", features = ["server"], optional = true } +wayland-protocols = { version = "0.32.8", features = [ + "unstable", + "staging", + "server", +], optional = true } +wayland-protocols-wlr = { version = "0.3.8", features = [ + "server", +], optional = true } +wayland-protocols-misc = { version = "0.3.8", features = [ + "server", +], optional = true } wayland-server = { version = "0.31.9", optional = true } wayland-sys = { version = "0.31.6", optional = true } wayland-backend = { version = "0.3.10", optional = true } -winit = { version = "0.30.0", default-features = false, features = ["wayland", "wayland-dlopen", "x11", "rwh_06"], optional = true } -x11rb = { version = "0.13.0", optional = true, features = ["res"]} -xkbcommon = { version = "0.8.0", features = ["wayland"]} +winit = { version = "0.30.0", default-features = false, features = [ + "wayland", + "wayland-dlopen", + "x11", + "rwh_06", +], optional = true } +x11rb = { version = "0.13.0", optional = true, features = ["res"] } +xkbcommon = { version = "0.8.0", features = ["wayland"] } encoding_rs = { version = "0.8.33", optional = true } profiling = "1.0.13" smallvec = "1.11" -pixman = { version = "0.2.1", features = ["drm-fourcc", "sync"], optional = true } +pixman = { version = "0.2.1", features = [ + "drm-fourcc", + "sync", +], optional = true } aliasable = { version = "0.1.3", optional = true } atomic_float = "1.1.0" sha2 = "0.10.9" @@ -85,9 +117,41 @@ pkg-config = { version = "0.3.17", optional = true } cc = { version = "1.0.79", optional = true } [features] -default = ["backend_drm", "backend_gbm", "backend_libinput", "backend_udev", "backend_session_libseat", "backend_x11", "backend_winit", "desktop", "renderer_gl", "renderer_pixman", "renderer_multi", "xwayland", "wayland_frontend", "backend_vulkan"] -backend_winit = ["winit", "backend_egl", "wayland-client", "wayland-cursor", "wayland-egl", "renderer_gl"] -backend_x11 = ["x11rb", "x11rb/dri3", "x11rb/xfixes", "x11rb/xinput", "x11rb/present", "x11rb_event_source", "backend_gbm", "backend_drm", "backend_egl"] +default = [ + "backend_drm", + "backend_gbm", + "backend_libinput", + "backend_udev", + "backend_session_libseat", + "backend_x11", + "backend_winit", + "desktop", + "renderer_gl", + "renderer_pixman", + "renderer_multi", + "xwayland", + "wayland_frontend", + "backend_vulkan", +] +backend_winit = [ + "winit", + "backend_egl", + "wayland-client", + "wayland-cursor", + "wayland-egl", + "renderer_gl", +] +backend_x11 = [ + "x11rb", + "x11rb/dri3", + "x11rb/xfixes", + "x11rb/xinput", + "x11rb/present", + "x11rb_event_source", + "backend_gbm", + "backend_drm", + "backend_egl", +] backend_drm = ["drm", "drm-ffi"] backend_gbm = ["gbm", "cc", "pkg-config", "backend_drm"] backend_gbm_has_fd_for_plane = [] @@ -104,12 +168,36 @@ renderer_glow = ["renderer_gl", "glow"] renderer_multi = ["backend_drm", "aliasable"] renderer_pixman = ["pixman"] renderer_test = [] -use_system_lib = ["wayland_frontend", "wayland-backend/server_system", "wayland-sys", "gbm?/import-wayland"] +use_system_lib = [ + "wayland_frontend", + "wayland-backend/server_system", + "wayland-sys", + "gbm?/import-wayland", +] use_bindgen = ["drm-ffi/use_bindgen", "gbm/use_bindgen", "input/use_bindgen"] -wayland_frontend = ["wayland-server", "wayland-protocols", "wayland-protocols-wlr", "wayland-protocols-misc", "tempfile"] +wayland_frontend = [ + "wayland-server", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-protocols-misc", + "tempfile", +] x11rb_event_source = ["x11rb"] -xwayland = ["encoding_rs", "wayland_frontend", "x11rb/composite", "x11rb/xfixes", "x11rb/randr", "x11rb_event_source", "scopeguard"] -test_all_features = ["default", "use_system_lib", "renderer_glow", "renderer_test"] +xwayland = [ + "encoding_rs", + "wayland_frontend", + "x11rb/composite", + "x11rb/xfixes", + "x11rb/randr", + "x11rb_event_source", + "scopeguard", +] +test_all_features = [ + "default", + "use_system_lib", + "renderer_glow", + "renderer_test", +] [[example]] name = "minimal" @@ -129,7 +217,13 @@ required-features = ["backend_vulkan"] [[example]] name = "buffer_test" -required-features = ["backend_drm", "backend_gbm", "backend_egl", "backend_vulkan", "renderer_gl"] +required-features = [ + "backend_drm", + "backend_gbm", + "backend_egl", + "backend_vulkan", + "renderer_gl", +] [[bench]] name = "benchmark" @@ -142,3 +236,20 @@ harness = false [profile.release-with-debug] inherits = "release" debug = true + +[patch.crates-io] +wayland-client = { git = "https://github.com/Smithay/wayland-rs", optional = true } +wayland-protocols = { git = "https://github.com/Smithay/wayland-rs", features = [ + "unstable", + "staging", + "server", +], optional = true } +wayland-protocols-wlr = { git = "https://github.com/Smithay/wayland-rs", features = [ + "server", +], optional = true } +wayland-protocols-misc = { git = "https://github.com/Smithay/wayland-rs", features = [ + "server", +], optional = true } +wayland-server = { git = "https://github.com/Smithay/wayland-rs", optional = true } +wayland-sys = { git = "https://github.com/Smithay/wayland-rs", optional = true } +wayland-backend = { git = "https://github.com/Smithay/wayland-rs", optional = true } diff --git a/src/wayland/background_effect/dispatch.rs b/src/wayland/background_effect/dispatch.rs new file mode 100644 index 000000000000..3686407057ea --- /dev/null +++ b/src/wayland/background_effect/dispatch.rs @@ -0,0 +1,142 @@ +use crate::wayland::background_effect::{BackgroundEffectState, BackgroundEffectSurfaceData}; +use crate::wayland::{ + background_effect::{BackgroundEffectSurfaceCachedState, BackgroundEffectSurfaceUserData}, + compositor::with_states, +}; +use wayland_protocols::ext::background_effect::v1::server::{ + ext_background_effect_manager_v1::{ + Capability, 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}; + +// GlobalDispatch for ext_background_effect_manager_v1 +impl GlobalDispatch for BackgroundEffectState +where + D: GlobalDispatch, + D: Dispatch, + D: Dispatch, + D: 'static, +{ + fn bind( + _state: &mut D, + _handle: &DisplayHandle, + _client: &Client, + resource: New, + _global_data: &(), + data_init: &mut DataInit<'_, D>, + ) { + let manager = data_init.init(resource, ()); + // For now, always advertise blur capability + manager.capabilities(Capability::Blur); + } +} + +// Dispatch for ext_background_effect_manager_v1 +impl Dispatch for BackgroundEffectState +where + D: Dispatch, + D: Dispatch, + D: 'static, +{ + fn request( + _state: &mut D, + _client: &Client, + manager: &ExtBackgroundEffectManagerV1, + request: ManagerRequest, + _data: &(), + _dh: &DisplayHandle, + data_init: &mut DataInit<'_, D>, + ) { + tracing::info!("Received request: {:#?}", request); + match request { + ManagerRequest::GetBackgroundEffect { id, surface } => { + let already_taken = with_states(&surface, |states| { + states + .data_map + .insert_if_missing_threadsafe(BackgroundEffectSurfaceData::new); + let data = states.data_map.get::().unwrap(); + 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 => {} + _ => {} + } + } +} + +// Dispatch for ext_background_effect_surface_v1 +impl Dispatch for BackgroundEffectState +where + D: Dispatch, +{ + fn request( + _state: &mut D, + _client: &Client, + obj: &ExtBackgroundEffectSurfaceV1, + request: SurfaceRequest, + data: &BackgroundEffectSurfaceUserData, + _dh: &DisplayHandle, + _data_init: &mut DataInit<'_, D>, + ) { + tracing::info!("Received request: {:#?}", request); + + 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::(); + let pending = cached.pending(); + pending.blur_region = + region.map(|r| crate::wayland::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::() + .unwrap() + .set_is_resource_attached(false); + let mut cached = states.cached_state.get::(); + 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 + } +} diff --git a/src/wayland/background_effect/mod.rs b/src/wayland/background_effect/mod.rs new file mode 100644 index 000000000000..39908124d3db --- /dev/null +++ b/src/wayland/background_effect/mod.rs @@ -0,0 +1,119 @@ +//! Implementation of `ext_background_effect_manager_v1` protocol + +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Mutex, +}; + +use crate::wayland::compositor::Cacheable; +use wayland_protocols::ext::background_effect::v1::server::{ + ext_background_effect_manager_v1::ExtBackgroundEffectManagerV1, + ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1, +}; +use wayland_server::{ + backend::GlobalId, protocol::wl_surface::WlSurface, Dispatch, DisplayHandle, GlobalDispatch, Resource, + Weak, +}; + +mod dispatch; + +/// Cached state for background effect per surface (double-buffered) +#[derive(Debug, Clone, Default)] +pub struct BackgroundEffectSurfaceCachedState { + pub blur_region: Option, +} + +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>); + +impl BackgroundEffectSurfaceUserData { + fn new(surface: WlSurface) -> Self { + Self(Mutex::new(surface.downgrade())) + } + fn wl_surface(&self) -> Option { + 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 [ExtBackgroundEffectV1] global + pub fn new(display: &DisplayHandle) -> BackgroundEffectState + where + D: GlobalDispatch + + Dispatch + + Dispatch + + 'static, + { + let global = display.create_global::(1, ()); + BackgroundEffectState { global } + } + + /// Returns the [ExtBackgroundEffectV1] 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) => { + type __ExtBackgroundEffectManagerV1 = + $crate::reexports::wayland_protocols::ext::background_effect::v1::server::ext_background_effect_manager_v1::ExtBackgroundEffectManagerV1; + type __ExtBackgroundEffectSurfaceV1 = + $crate::reexports::wayland_protocols::ext::background_effect::v1::server::ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1; + + $crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: + [ + __ExtBackgroundEffectManagerV1: () + ] => $crate::wayland::background_effect::BackgroundEffectState + ); + + $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: + [ + __ExtBackgroundEffectManagerV1: () + ] => $crate::wayland::background_effect::BackgroundEffectState + ); + $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: + [ + __ExtBackgroundEffectSurfaceV1: $crate::wayland::background_effect::BackgroundEffectSurfaceUserData + ] => $crate::wayland::background_effect::BackgroundEffectState + ); + } +} diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index 70d0df354cd8..1b1e64c9e387 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -46,6 +46,7 @@ //! pub mod alpha_modifier; +pub mod background_effect; pub mod buffer; pub mod commit_timing; pub mod compositor; diff --git a/wlcs_anvil/Cargo.toml b/wlcs_anvil/Cargo.toml index a1db558359b7..20932a7c9ffd 100644 --- a/wlcs_anvil/Cargo.toml +++ b/wlcs_anvil/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "wlcs_anvil" version = "0.0.1" -authors = ["Victor Berger ", "Drakulix (Victoria Brekenfeld)"] +authors = [ + "Victor Berger ", + "Drakulix (Victoria Brekenfeld)", +] license = "MIT" publish = false edition = "2018" @@ -10,8 +13,13 @@ edition = "2018" crate-type = ["cdylib"] [dependencies] -smithay = { path = "..", default-features=false, features=["wayland_frontend", "backend_egl", "use_system_lib", "renderer_test"] } -anvil = { path = "../anvil", default-features=false } +smithay = { path = "..", default-features = false, features = [ + "wayland_frontend", + "backend_egl", + "use_system_lib", + "renderer_test", +] } +anvil = { path = "../anvil", default-features = false } wayland-sys = { version = "0.31.1", features = ["client", "server"] } wlcs = "0.1" libc = "0.2"