From 40542f55daa833ed1abdf13eede0f207ae046621 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 22 Jan 2026 16:13:52 +0100 Subject: [PATCH 01/16] element/surface: Fix reported corner radius --- src/shell/element/surface.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index b8b7cf94e..5cf94ea4b 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -151,10 +151,10 @@ impl CosmicSurface { let corners = guard.current().0?; Some([ - corners.bottom_right.min(half_min_dim), + corners.top_left.min(half_min_dim), corners.top_right.min(half_min_dim), + corners.bottom_right.min(half_min_dim), corners.bottom_left.min(half_min_dim), - corners.top_left.min(half_min_dim), ]) }) }) From d0bbae8cae2dee4d0f6878f0784a28f07f151d5c Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 22 Jan 2026 16:10:57 +0100 Subject: [PATCH 02/16] chore: Update smithay --- Cargo.lock | 3 +- Cargo.toml | 3 +- src/backend/render/clipped_surface.rs | 22 ++-- src/backend/render/element.rs | 68 +++++++++++-- src/shell/element/mod.rs | 140 ++++++++++++++++++++++++-- src/shell/element/stack.rs | 53 +++++++++- src/shell/element/window.rs | 53 +++++++++- src/shell/mod.rs | 8 +- src/shell/workspace.rs | 65 +++++++++++- src/state.rs | 1 + 10 files changed, 377 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e6380cc9e..165360725 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -866,6 +866,7 @@ dependencies = [ "sanitize-filename", "serde", "serde_json", + "smallvec", "smithay", "smithay-egui", "thiserror 2.0.18", @@ -4923,7 +4924,7 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=e84a4ca#e84a4ca82d58d783839083bd48c4ab120715c4b6" +source = "git+https://github.com/smithay/smithay.git?rev=f6d1070#f6d10709db2bb68c46b9c128a4163d035356572c" dependencies = [ "aliasable", "appendlist", diff --git a/Cargo.toml b/Cargo.toml index f5e69fab8..08c158c0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,6 +83,7 @@ logind-zbus = { version = "5.3.2", optional = true } futures-executor = { version = "0.3.32", features = ["thread-pool"] } futures-util = "0.3.32" cgmath = "0.18.0" +smallvec = "1.15.1" [dependencies.id_tree] branch = "feature/copy_clone" @@ -142,4 +143,4 @@ cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch cosmic-client-toolkit = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } [patch.crates-io] -smithay = { git = "https://github.com/smithay/smithay.git", rev = "e84a4ca" } +smithay = { git = "https://github.com/smithay/smithay.git", rev = "f6d1070" } diff --git a/src/backend/render/clipped_surface.rs b/src/backend/render/clipped_surface.rs index 189312f2a..8705051e2 100644 --- a/src/backend/render/clipped_surface.rs +++ b/src/backend/render/clipped_surface.rs @@ -3,15 +3,19 @@ use std::borrow::{Borrow, BorrowMut}; use cgmath::{Matrix3, Vector2}; -use smithay::backend::renderer::{ - ImportAll, ImportMem, Renderer, - element::{ - Element, Id, Kind, RenderElement, UnderlyingStorage, surface::WaylandSurfaceRenderElement, +use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform}; +use smithay::{ + backend::renderer::{ + ImportAll, ImportMem, Renderer, + element::{ + Element, Id, Kind, RenderElement, UnderlyingStorage, + surface::WaylandSurfaceRenderElement, + }, + gles::{GlesFrame, GlesRenderer, GlesTexProgram, Uniform, UniformValue}, + utils::{CommitCounter, DamageSet, OpaqueRegions}, }, - gles::{GlesFrame, GlesRenderer, GlesTexProgram, Uniform, UniformValue}, - utils::{CommitCounter, DamageSet, OpaqueRegions}, + utils::user_data::UserDataMap, }; -use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform}; use crate::backend::render::element::AsGlowRenderer; @@ -253,10 +257,12 @@ where dst: Rectangle, damage: &[Rectangle], opaque_regions: &[Rectangle], + cache: Option<&UserDataMap>, ) -> Result<(), R::Error> { BorrowMut::::borrow_mut(::glow_frame_mut(frame)) .override_default_tex_program(self.program.clone(), self.uniforms.clone()); - self.inner.draw(frame, src, dst, damage, opaque_regions)?; + self.inner + .draw(frame, src, dst, damage, opaque_regions, cache)?; BorrowMut::::borrow_mut(::glow_frame_mut(frame)) .clear_tex_program_override(); Ok(()) diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index 5f75725f5..496702d06 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -18,7 +18,9 @@ use smithay::{ utils::{CommitCounter, DamageSet, OpaqueRegions}, }, }, - utils::{Buffer as BufferCoords, Logical, Physical, Point, Rectangle, Scale}, + utils::{ + Buffer as BufferCoords, Logical, Physical, Point, Rectangle, Scale, user_data::UserDataMap, + }, }; use super::{GlMultiRenderer, cursor::CursorRenderElement}; @@ -182,6 +184,19 @@ where CosmicElement::Egui(elem) => elem.kind(), } } + + fn is_framebuffer_effect(&self) -> bool { + match self { + CosmicElement::Workspace(elem) => elem.is_framebuffer_effect(), + CosmicElement::Cursor(elem) => elem.is_framebuffer_effect(), + CosmicElement::Dnd(elem) => elem.is_framebuffer_effect(), + CosmicElement::MoveGrab(elem) => elem.is_framebuffer_effect(), + CosmicElement::Postprocess(elem) => elem.is_framebuffer_effect(), + CosmicElement::Zoom(elem) => elem.is_framebuffer_effect(), + #[cfg(feature = "debug")] + CosmicElement::Egui(elem) => elem.is_framebuffer_effect(), + } + } } impl RenderElement for CosmicElement @@ -198,12 +213,19 @@ where dst: Rectangle, damage: &[Rectangle], opaque_regions: &[Rectangle], + cache: Option<&UserDataMap>, ) -> Result<(), R::Error> { match self { - CosmicElement::Workspace(elem) => elem.draw(frame, src, dst, damage, opaque_regions), - CosmicElement::Cursor(elem) => elem.draw(frame, src, dst, damage, opaque_regions), - CosmicElement::Dnd(elem) => elem.draw(frame, src, dst, damage, opaque_regions), - CosmicElement::MoveGrab(elem) => elem.draw(frame, src, dst, damage, opaque_regions), + CosmicElement::Workspace(elem) => { + elem.draw(frame, src, dst, damage, opaque_regions, cache) + } + CosmicElement::Cursor(elem) => { + elem.draw(frame, src, dst, damage, opaque_regions, cache) + } + CosmicElement::Dnd(elem) => elem.draw(frame, src, dst, damage, opaque_regions, cache), + CosmicElement::MoveGrab(elem) => { + elem.draw(frame, src, dst, damage, opaque_regions, cache) + } CosmicElement::Postprocess(elem) => { let glow_frame = R::glow_frame_mut(frame); RenderElement::::draw( @@ -213,10 +235,11 @@ where dst, damage, opaque_regions, + cache, ) .map_err(FromGlesError::from_gles_error) } - CosmicElement::Zoom(elem) => elem.draw(frame, src, dst, damage, opaque_regions), + CosmicElement::Zoom(elem) => elem.draw(frame, src, dst, damage, opaque_regions, cache), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => { let glow_frame = R::glow_frame_mut(frame); @@ -227,6 +250,7 @@ where dst, damage, opaque_regions, + cache, ) .map_err(FromGlesError::from_gles_error) } @@ -251,6 +275,37 @@ where } } } + + fn capture_framebuffer( + &self, + frame: &mut ::Frame<'_, '_>, + src: Rectangle, + dst: Rectangle, + cache: &UserDataMap, + ) -> Result<(), ::Error> { + match self { + CosmicElement::Workspace(elem) => elem.capture_framebuffer(frame, src, dst, cache), + CosmicElement::Cursor(elem) => elem.capture_framebuffer(frame, src, dst, cache), + CosmicElement::Dnd(elem) => elem.capture_framebuffer(frame, src, dst, cache), + CosmicElement::MoveGrab(elem) => elem.capture_framebuffer(frame, src, dst, cache), + CosmicElement::Postprocess(elem) => { + let glow_frame = R::glow_frame_mut(frame); + RenderElement::::capture_framebuffer( + elem, glow_frame, src, dst, cache, + ) + .map_err(FromGlesError::from_gles_error) + } + CosmicElement::Zoom(elem) => elem.capture_framebuffer(frame, src, dst, cache), + #[cfg(feature = "debug")] + CosmicElement::Egui(elem) => { + let glow_frame = R::glow_frame_mut(frame); + RenderElement::::capture_framebuffer( + elem, glow_frame, src, dst, cache, + ) + .map_err(FromGlesError::from_gles_error) + } + } + } } impl From>>> @@ -398,6 +453,7 @@ impl RenderElement for DamageElement { _dst: Rectangle, _damage: &[Rectangle], _opaque_regions: &[Rectangle], + _cache: Option<&UserDataMap>, ) -> Result<(), R::Error> { Ok(()) } diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 7c65e23b9..4147a0423 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -11,7 +11,7 @@ use smithay::{ input::KeyState, renderer::{ element::{ - Element, RenderElement, UnderlyingStorage, + Element, Kind, RenderElement, UnderlyingStorage, memory::MemoryRenderBufferRenderElement, utils::{CropRenderElement, RelocateRenderElement, RescaleRenderElement}, }, @@ -30,6 +30,7 @@ use smithay::{ space_elements, utils::{ Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, + user_data::UserDataMap, }, wayland::seat::WaylandFocus, xwayland::{X11Surface, xwm::X11Relatable}, @@ -1274,6 +1275,44 @@ where CosmicMappedRenderElement::Egui(elem) => elem.alpha(), } } + + fn kind(&self) -> Kind { + match self { + CosmicMappedRenderElement::Stack(elem) => elem.kind(), + CosmicMappedRenderElement::Window(elem) => elem.kind(), + CosmicMappedRenderElement::TiledStack(elem) => elem.kind(), + CosmicMappedRenderElement::TiledWindow(elem) => elem.kind(), + CosmicMappedRenderElement::TiledOverlay(elem) => elem.kind(), + CosmicMappedRenderElement::MovingStack(elem) => elem.kind(), + CosmicMappedRenderElement::MovingWindow(elem) => elem.kind(), + CosmicMappedRenderElement::GrabbedStack(elem) => elem.kind(), + CosmicMappedRenderElement::GrabbedWindow(elem) => elem.kind(), + CosmicMappedRenderElement::FocusIndicator(elem) => elem.kind(), + CosmicMappedRenderElement::Overlay(elem) => elem.kind(), + CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.kind(), + #[cfg(feature = "debug")] + CosmicMappedRenderElement::Egui(elem) => elem.kind(), + } + } + + fn is_framebuffer_effect(&self) -> bool { + match self { + CosmicMappedRenderElement::Stack(elem) => elem.is_framebuffer_effect(), + CosmicMappedRenderElement::Window(elem) => elem.is_framebuffer_effect(), + CosmicMappedRenderElement::TiledStack(elem) => elem.is_framebuffer_effect(), + CosmicMappedRenderElement::TiledWindow(elem) => elem.is_framebuffer_effect(), + CosmicMappedRenderElement::TiledOverlay(elem) => elem.is_framebuffer_effect(), + CosmicMappedRenderElement::MovingStack(elem) => elem.is_framebuffer_effect(), + CosmicMappedRenderElement::MovingWindow(elem) => elem.is_framebuffer_effect(), + CosmicMappedRenderElement::GrabbedStack(elem) => elem.is_framebuffer_effect(), + CosmicMappedRenderElement::GrabbedWindow(elem) => elem.is_framebuffer_effect(), + CosmicMappedRenderElement::FocusIndicator(elem) => elem.is_framebuffer_effect(), + CosmicMappedRenderElement::Overlay(elem) => elem.is_framebuffer_effect(), + CosmicMappedRenderElement::StackHoverIndicator(elem) => elem.is_framebuffer_effect(), + #[cfg(feature = "debug")] + CosmicMappedRenderElement::Egui(elem) => elem.is_framebuffer_effect(), + } + } } impl RenderElement for CosmicMappedRenderElement @@ -1289,19 +1328,20 @@ where dst: Rectangle, damage: &[Rectangle], opaque_regions: &[Rectangle], + cache: Option<&UserDataMap>, ) -> Result<(), R::Error> { match self { CosmicMappedRenderElement::Stack(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } CosmicMappedRenderElement::Window(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } CosmicMappedRenderElement::TiledStack(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } CosmicMappedRenderElement::TiledWindow(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } CosmicMappedRenderElement::TiledOverlay(elem) => RenderElement::::draw( elem, @@ -1310,19 +1350,20 @@ where dst, damage, opaque_regions, + cache, ) .map_err(FromGlesError::from_gles_error), CosmicMappedRenderElement::MovingStack(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } CosmicMappedRenderElement::MovingWindow(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } CosmicMappedRenderElement::GrabbedStack(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } CosmicMappedRenderElement::GrabbedWindow(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } CosmicMappedRenderElement::FocusIndicator(elem) => RenderElement::::draw( elem, @@ -1331,6 +1372,7 @@ where dst, damage, opaque_regions, + cache, ) .map_err(FromGlesError::from_gles_error), CosmicMappedRenderElement::Overlay(elem) => RenderElement::::draw( @@ -1340,10 +1382,11 @@ where dst, damage, opaque_regions, + cache, ) .map_err(FromGlesError::from_gles_error), CosmicMappedRenderElement::StackHoverIndicator(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } #[cfg(feature = "debug")] CosmicMappedRenderElement::Egui(elem) => { @@ -1355,6 +1398,7 @@ where dst, damage, opaque_regions, + cache, ) .map_err(FromGlesError::from_gles_error) } @@ -1390,6 +1434,82 @@ where } } } + + fn capture_framebuffer( + &self, + frame: &mut R::Frame<'_, '_>, + src: Rectangle, + dst: Rectangle, + cache: &UserDataMap, + ) -> Result<(), R::Error> { + match self { + CosmicMappedRenderElement::Stack(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + CosmicMappedRenderElement::Window(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + CosmicMappedRenderElement::TiledStack(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + CosmicMappedRenderElement::TiledWindow(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + CosmicMappedRenderElement::TiledOverlay(elem) => { + RenderElement::::capture_framebuffer( + elem, + R::glow_frame_mut(frame), + src, + dst, + cache, + ) + .map_err(FromGlesError::from_gles_error) + } + CosmicMappedRenderElement::MovingStack(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + CosmicMappedRenderElement::MovingWindow(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + CosmicMappedRenderElement::GrabbedStack(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + CosmicMappedRenderElement::GrabbedWindow(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + CosmicMappedRenderElement::FocusIndicator(elem) => { + RenderElement::::capture_framebuffer( + elem, + R::glow_frame_mut(frame), + src, + dst, + cache, + ) + .map_err(FromGlesError::from_gles_error) + } + CosmicMappedRenderElement::Overlay(elem) => { + RenderElement::::capture_framebuffer( + elem, + R::glow_frame_mut(frame), + src, + dst, + cache, + ) + .map_err(FromGlesError::from_gles_error) + } + CosmicMappedRenderElement::StackHoverIndicator(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + #[cfg(feature = "debug")] + CosmicMappedRenderElement::Egui(elem) => { + let glow_frame = R::glow_frame_mut(frame); + RenderElement::::capture_framebuffer( + elem, glow_frame, src, dst, cache, + ) + .map_err(FromGlesError::from_gles_error) + } + } + } } impl From> for CosmicMappedRenderElement diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 85979dac8..fca52a7a0 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -67,7 +67,10 @@ use smithay::{ }, output::Output, reexports::wayland_server::protocol::wl_surface::WlSurface, - utils::{Buffer, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform}, + utils::{ + Buffer, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform, + user_data::UserDataMap, + }, wayland::seat::WaylandFocus, }; use std::{ @@ -1988,6 +1991,16 @@ where CosmicStackRenderElement::Clipped(elem) => elem.kind(), } } + + fn is_framebuffer_effect(&self) -> bool { + match self { + CosmicStackRenderElement::Header(elem) => elem.is_framebuffer_effect(), + CosmicStackRenderElement::Shadow(elem) => elem.is_framebuffer_effect(), + CosmicStackRenderElement::Border(elem) => elem.is_framebuffer_effect(), + CosmicStackRenderElement::Window(elem) => elem.is_framebuffer_effect(), + CosmicStackRenderElement::Clipped(elem) => elem.is_framebuffer_effect(), + } + } } impl RenderElement for CosmicStackRenderElement @@ -2003,10 +2016,11 @@ where dst: Rectangle, damage: &[Rectangle], opaque_regions: &[Rectangle], + cache: Option<&UserDataMap>, ) -> Result<(), ::Error> { match self { CosmicStackRenderElement::Header(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } CosmicStackRenderElement::Shadow(elem) | CosmicStackRenderElement::Border(elem) => { RenderElement::::draw( @@ -2016,14 +2030,15 @@ where dst, damage, opaque_regions, + cache, ) .map_err(FromGlesError::from_gles_error) } CosmicStackRenderElement::Window(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } CosmicStackRenderElement::Clipped(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } } } @@ -2038,4 +2053,34 @@ where CosmicStackRenderElement::Clipped(elem) => elem.underlying_storage(renderer), } } + + fn capture_framebuffer( + &self, + frame: &mut R::Frame<'_, '_>, + src: Rectangle, + dst: Rectangle, + cache: &UserDataMap, + ) -> Result<(), ::Error> { + match self { + CosmicStackRenderElement::Header(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + CosmicStackRenderElement::Shadow(elem) | CosmicStackRenderElement::Border(elem) => { + RenderElement::::capture_framebuffer( + elem, + R::glow_frame_mut(frame), + src, + dst, + cache, + ) + .map_err(FromGlesError::from_gles_error) + } + CosmicStackRenderElement::Window(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + CosmicStackRenderElement::Clipped(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + } + } } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 083434256..3e7e3add8 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -53,7 +53,10 @@ use smithay::{ }, output::Output, reexports::wayland_server::protocol::wl_surface::WlSurface, - utils::{Buffer, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform}, + utils::{ + Buffer, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform, + user_data::UserDataMap, + }, wayland::seat::WaylandFocus, }; use std::{ @@ -1356,6 +1359,16 @@ where CosmicWindowRenderElement::Clipped(elem) => elem.kind(), } } + + fn is_framebuffer_effect(&self) -> bool { + match self { + CosmicWindowRenderElement::Header(elem) => elem.is_framebuffer_effect(), + CosmicWindowRenderElement::Shadow(elem) => elem.is_framebuffer_effect(), + CosmicWindowRenderElement::Border(elem) => elem.is_framebuffer_effect(), + CosmicWindowRenderElement::Window(elem) => elem.is_framebuffer_effect(), + CosmicWindowRenderElement::Clipped(elem) => elem.is_framebuffer_effect(), + } + } } impl RenderElement for CosmicWindowRenderElement @@ -1371,10 +1384,11 @@ where dst: Rectangle, damage: &[Rectangle], opaque_regions: &[Rectangle], + cache: Option<&UserDataMap>, ) -> Result<(), ::Error> { match self { CosmicWindowRenderElement::Header(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } CosmicWindowRenderElement::Shadow(elem) | CosmicWindowRenderElement::Border(elem) => { RenderElement::::draw( @@ -1384,14 +1398,15 @@ where dst, damage, opaque_regions, + cache, ) .map_err(FromGlesError::from_gles_error) } CosmicWindowRenderElement::Window(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } CosmicWindowRenderElement::Clipped(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } } } @@ -1406,4 +1421,34 @@ where CosmicWindowRenderElement::Clipped(elem) => elem.underlying_storage(renderer), } } + + fn capture_framebuffer( + &self, + frame: &mut ::Frame<'_, '_>, + src: Rectangle, + dst: Rectangle, + cache: &UserDataMap, + ) -> Result<(), ::Error> { + match self { + CosmicWindowRenderElement::Header(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + CosmicWindowRenderElement::Shadow(elem) | CosmicWindowRenderElement::Border(elem) => { + RenderElement::::capture_framebuffer( + elem, + R::glow_frame_mut(frame), + src, + dst, + cache, + ) + .map_err(FromGlesError::from_gles_error) + } + CosmicWindowRenderElement::Window(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + CosmicWindowRenderElement::Clipped(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + } + } } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index f9cfe5fe2..4dde0f82b 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -4859,6 +4859,7 @@ impl Shell { |surface, _| { surface_presentation_feedback_flags_from_states( surface, + None, render_element_states, ) }, @@ -4875,6 +4876,7 @@ impl Shell { |surface, _| { surface_presentation_feedback_flags_from_states( surface, + None, render_element_states, ) }, @@ -4888,7 +4890,11 @@ impl Shell { &mut output_presentation_feedback, surface_primary_scanout_output, |surface, _| { - surface_presentation_feedback_flags_from_states(surface, render_element_states) + surface_presentation_feedback_flags_from_states( + surface, + None, + render_element_states, + ) }, ); } diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 5a5351456..1be2c2aea 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -31,7 +31,9 @@ use cosmic_protocols::workspace::v2::server::zcosmic_workspace_handle_v2::Tiling use id_tree::Tree; use indexmap::IndexSet; use keyframe::{ease, functions::EaseInOutCubic}; +use smithay::backend::renderer::element::Kind; use smithay::output::WeakOutput; +use smithay::utils::user_data::UserDataMap; use smithay::{ backend::renderer::{ element::{ @@ -1987,6 +1989,26 @@ where WorkspaceRenderElement::Backdrop(elem) => elem.alpha(), } } + + fn kind(&self) -> Kind { + match self { + WorkspaceRenderElement::OverrideRedirect(elem) => elem.kind(), + WorkspaceRenderElement::Fullscreen(elem) => elem.kind(), + WorkspaceRenderElement::FullscreenPopup(elem) => elem.kind(), + WorkspaceRenderElement::Window(elem) => elem.kind(), + WorkspaceRenderElement::Backdrop(elem) => elem.kind(), + } + } + + fn is_framebuffer_effect(&self) -> bool { + match self { + WorkspaceRenderElement::OverrideRedirect(elem) => elem.is_framebuffer_effect(), + WorkspaceRenderElement::Fullscreen(elem) => elem.is_framebuffer_effect(), + WorkspaceRenderElement::FullscreenPopup(elem) => elem.is_framebuffer_effect(), + WorkspaceRenderElement::Window(elem) => elem.is_framebuffer_effect(), + WorkspaceRenderElement::Backdrop(elem) => elem.is_framebuffer_effect(), + } + } } impl RenderElement for WorkspaceRenderElement @@ -2002,19 +2024,20 @@ where dst: Rectangle, damage: &[Rectangle], opaque_regions: &[Rectangle], + cache: Option<&UserDataMap>, ) -> Result<(), R::Error> { match self { WorkspaceRenderElement::OverrideRedirect(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } WorkspaceRenderElement::Fullscreen(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } WorkspaceRenderElement::FullscreenPopup(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } WorkspaceRenderElement::Window(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions) + elem.draw(frame, src, dst, damage, opaque_regions, cache) } WorkspaceRenderElement::Backdrop(elem) => RenderElement::::draw( elem, @@ -2023,6 +2046,7 @@ where dst, damage, opaque_regions, + cache, ) .map_err(FromGlesError::from_gles_error), } @@ -2042,6 +2066,39 @@ where } } } + + fn capture_framebuffer( + &self, + frame: &mut R::Frame<'_, '_>, + src: Rectangle, + dst: Rectangle, + cache: &UserDataMap, + ) -> Result<(), R::Error> { + match self { + WorkspaceRenderElement::OverrideRedirect(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + WorkspaceRenderElement::Fullscreen(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + WorkspaceRenderElement::FullscreenPopup(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + WorkspaceRenderElement::Window(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } + WorkspaceRenderElement::Backdrop(elem) => { + RenderElement::::capture_framebuffer( + elem, + R::glow_frame_mut(frame), + src, + dst, + cache, + ) + .map_err(FromGlesError::from_gles_error) + } + } + } } impl From>> for WorkspaceRenderElement diff --git a/src/state.rs b/src/state.rs index 81e16e4eb..21dfcef0a 100644 --- a/src/state.rs +++ b/src/state.rs @@ -940,6 +940,7 @@ impl Common { surface, output, states, + None, render_element_states, primary_scanout_output_compare, ); From f132b1218e37b5b8a7f583fa7176ab478cf0e3ed Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 4 Mar 2026 16:57:17 +0100 Subject: [PATCH 03/16] render: Merge FromGlesError into AsGlowRenderer --- src/backend/kms/render/gles.rs | 16 +--------- src/backend/render/element.rs | 31 +++++++++---------- src/backend/render/mod.rs | 7 +---- src/shell/element/mod.rs | 19 ++++++------ src/shell/element/stack.rs | 12 +++---- src/shell/element/window.rs | 12 +++---- src/shell/workspace.rs | 10 ++---- .../handlers/image_copy_capture/render.rs | 11 ++----- 8 files changed, 40 insertions(+), 78 deletions(-) diff --git a/src/backend/kms/render/gles.rs b/src/backend/kms/render/gles.rs index 5d79b1eef..a68269a86 100644 --- a/src/backend/kms/render/gles.rs +++ b/src/backend/kms/render/gles.rs @@ -9,10 +9,9 @@ use smithay::backend::{ }, drm::{CreateDrmNodeError, DrmNode}, renderer::{ - RendererSuper, gles::{GlesError, GlesRenderer}, glow::GlowRenderer, - multigpu::{ApiDevice, Error as MultiError, GraphicsApi}, + multigpu::{ApiDevice, GraphicsApi}, }, }; use std::{borrow::Borrow, cell::Cell}; @@ -23,8 +22,6 @@ use std::{ sync::atomic::{AtomicBool, Ordering}, }; -use crate::backend::render::element::FromGlesError; - /// Errors raised by the [`GbmGlesBackend`] #[derive(Debug, thiserror::Error)] pub enum Error { @@ -181,14 +178,3 @@ impl ApiDevice for GbmGlowDevice { !Borrow::::borrow(&self.renderer).is_software() } } - -impl FromGlesError for MultiError, T> -where - T::Error: 'static, - <::Renderer as RendererSuper>::Error: 'static, -{ - #[inline] - fn from_gles_error(err: GlesError) -> MultiError, T> { - MultiError::Render(err) - } -} diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index 496702d06..04f7fd9e4 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -1,4 +1,7 @@ -use crate::shell::{CosmicMappedRenderElement, WorkspaceRenderElement}; +use crate::{ + backend::render::GlMultiError, + shell::{CosmicMappedRenderElement, WorkspaceRenderElement}, +}; #[cfg(feature = "debug")] use smithay::backend::renderer::element::texture::TextureRenderElement; @@ -203,7 +206,6 @@ impl RenderElement for CosmicElement where R: AsGlowRenderer, R::TextureId: 'static, - R::Error: FromGlesError, CosmicMappedRenderElement: RenderElement, { fn draw( @@ -237,7 +239,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicElement::Zoom(elem) => elem.draw(frame, src, dst, damage, opaque_regions, cache), #[cfg(feature = "debug")] @@ -252,7 +254,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } } } @@ -293,7 +295,7 @@ where RenderElement::::capture_framebuffer( elem, glow_frame, src, dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicElement::Zoom(elem) => elem.capture_framebuffer(frame, src, dst, cache), #[cfg(feature = "debug")] @@ -302,7 +304,7 @@ where RenderElement::::capture_framebuffer( elem, glow_frame, src, dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } } } @@ -365,6 +367,7 @@ pub trait AsGlowRenderer: fn glow_frame_mut<'a, 'frame, 'buffer>( frame: &'a mut Self::Frame<'frame, 'buffer>, ) -> &'a mut GlowFrame<'frame, 'buffer>; + fn from_gles_error(err: GlesError) -> Self::Error; } impl AsGlowRenderer for GlowRenderer { @@ -384,6 +387,9 @@ impl AsGlowRenderer for GlowRenderer { ) -> &'a mut GlowFrame<'frame, 'buffer> { frame } + fn from_gles_error(err: GlesError) -> Self::Error { + err + } } impl AsGlowRenderer for GlMultiRenderer<'_> { @@ -403,6 +409,9 @@ impl AsGlowRenderer for GlMultiRenderer<'_> { ) -> &'b mut GlowFrame<'frame, 'buffer> { frame.as_mut() } + fn from_gles_error(err: GlesError) -> Self::Error { + GlMultiError::Render(err) + } } pub struct DamageElement { @@ -458,13 +467,3 @@ impl RenderElement for DamageElement { Ok(()) } } - -pub trait FromGlesError { - fn from_gles_error(err: GlesError) -> Self; -} - -impl FromGlesError for GlesError { - fn from_gles_error(err: GlesError) -> Self { - err - } -} diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index bfbb21a39..40537167d 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -42,7 +42,6 @@ use crate::{ }; use cosmic::Theme; -use element::FromGlesError; use smithay::{ backend::{ allocator::Fourcc, @@ -619,7 +618,6 @@ pub fn output_elements( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - R::Error: FromGlesError, CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { @@ -646,7 +644,7 @@ where ), scale, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) .map_err(RenderError::Rendering)? .into(), ] @@ -717,7 +715,6 @@ pub fn workspace_elements( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - R::Error: FromGlesError, CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { @@ -1190,7 +1187,6 @@ pub fn render_output<'d, R>( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - R::Error: FromGlesError, CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, @@ -1495,7 +1491,6 @@ pub fn render_workspace<'d, R>( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - R::Error: FromGlesError, CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 4147a0423..73d7d5d30 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -1,5 +1,5 @@ use crate::{ - backend::render::element::{AsGlowRenderer, FromGlesError}, + backend::render::element::AsGlowRenderer, state::State, utils::{iced::IcedElementInternal, prelude::*}, }; @@ -1319,7 +1319,6 @@ impl RenderElement for CosmicMappedRenderElement where R: AsGlowRenderer, R::TextureId: 'static, - R::Error: FromGlesError, { fn draw( &self, @@ -1352,7 +1351,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error), + .map_err(R::from_gles_error), CosmicMappedRenderElement::MovingStack(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } @@ -1374,7 +1373,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error), + .map_err(R::from_gles_error), CosmicMappedRenderElement::Overlay(elem) => RenderElement::::draw( elem, R::glow_frame_mut(frame), @@ -1384,7 +1383,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error), + .map_err(R::from_gles_error), CosmicMappedRenderElement::StackHoverIndicator(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } @@ -1400,7 +1399,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } } } @@ -1463,7 +1462,7 @@ where dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicMappedRenderElement::MovingStack(elem) => { elem.capture_framebuffer(frame, src, dst, cache) @@ -1485,7 +1484,7 @@ where dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicMappedRenderElement::Overlay(elem) => { RenderElement::::capture_framebuffer( @@ -1495,7 +1494,7 @@ where dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicMappedRenderElement::StackHoverIndicator(elem) => { elem.capture_framebuffer(frame, src, dst, cache) @@ -1506,7 +1505,7 @@ where RenderElement::::capture_framebuffer( elem, glow_frame, src, dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } } } diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index fca52a7a0..fbcba6117 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -4,11 +4,8 @@ use super::{ }; use crate::{ backend::render::{ - IndicatorShader, Key, Usage, - clipped_surface::ClippedSurfaceRenderElement, - cursor::CursorState, - element::{AsGlowRenderer, FromGlesError}, - shadow::ShadowShader, + IndicatorShader, Key, Usage, clipped_surface::ClippedSurfaceRenderElement, + cursor::CursorState, element::AsGlowRenderer, shadow::ShadowShader, }, hooks::{Decorations, HOOKS}, shell::{ @@ -2007,7 +2004,6 @@ impl RenderElement for CosmicStackRenderElement where R: AsGlowRenderer, R::TextureId: 'static, - R::Error: FromGlesError, { fn draw( &self, @@ -2032,7 +2028,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicStackRenderElement::Window(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) @@ -2073,7 +2069,7 @@ where dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicStackRenderElement::Window(elem) => { elem.capture_framebuffer(frame, src, dst, cache) diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 3e7e3add8..ac07e13e6 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1,10 +1,7 @@ use crate::{ backend::render::{ - IndicatorShader, Key, Usage, - clipped_surface::ClippedSurfaceRenderElement, - cursor::CursorState, - element::{AsGlowRenderer, FromGlesError}, - shadow::ShadowShader, + IndicatorShader, Key, Usage, clipped_surface::ClippedSurfaceRenderElement, + cursor::CursorState, element::AsGlowRenderer, shadow::ShadowShader, }, hooks::{Decorations, HOOKS}, shell::{ @@ -1375,7 +1372,6 @@ impl RenderElement for CosmicWindowRenderElement where R: AsGlowRenderer, R::TextureId: 'static, - R::Error: FromGlesError, { fn draw( &self, @@ -1400,7 +1396,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicWindowRenderElement::Window(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) @@ -1441,7 +1437,7 @@ where dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicWindowRenderElement::Window(elem) => { elem.capture_framebuffer(frame, src, dst, cache) diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 1be2c2aea..8026e96e6 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -2,10 +2,7 @@ use crate::shell::focus::FocusTarget; use crate::shell::layout::tiling::RestoreTilingState; use crate::wayland::handlers::xdg_activation::ActivationContext; use crate::{ - backend::render::{ - BackdropShader, - element::{AsGlowRenderer, FromGlesError}, - }, + backend::render::{BackdropShader, element::AsGlowRenderer}, shell::{ ANIMATION_DURATION, OverviewMode, SeatMoveGrabState, layout::{ @@ -2015,7 +2012,6 @@ impl RenderElement for WorkspaceRenderElement where R: AsGlowRenderer, R::TextureId: 'static, - R::Error: FromGlesError, { fn draw( &self, @@ -2048,7 +2044,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error), + .map_err(R::from_gles_error), } } @@ -2095,7 +2091,7 @@ where dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } } } diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index 28ccd1cb4..6f8f2520e 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -41,7 +41,7 @@ use tracing::warn; use crate::{ backend::render::{ CursorMode, ElementFilter, RendererRef, cursor, - element::{AsGlowRenderer, CosmicElement, DamageElement, FromGlesError}, + element::{AsGlowRenderer, CosmicElement, DamageElement}, render_workspace, }, shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement}, @@ -105,8 +105,7 @@ pub fn submit_buffer( mut sync: SyncPoint, ) -> Result, R::Error> where - R: ExportMem, - R::Error: FromGlesError, + R: ExportMem + AsGlowRenderer, { let Some(damage) = damage else { frame.success( @@ -164,7 +163,7 @@ where Ok(()) }) - .map_err(|err| R::Error::from_gles_error(GlesError::BufferAccessError(err))) + .map_err(|err| R::from_gles_error(GlesError::BufferAccessError(err))) .and_then(|x| x) { frame.fail(CaptureFailureReason::Unknown); @@ -194,7 +193,6 @@ pub fn render_session( ) -> Result, DTError> where R: AsGlowRenderer, - R::Error: FromGlesError, F: for<'d> FnOnce( &WlBuffer, &mut R, @@ -321,7 +319,6 @@ pub fn render_workspace_to_buffer( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - R::Error: FromGlesError, CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, @@ -554,7 +551,6 @@ pub fn render_window_to_buffer( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - R::Error: FromGlesError, CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, { @@ -789,7 +785,6 @@ pub fn render_cursor_to_buffer( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - R::Error: FromGlesError, CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, { From ea819554f8c43bfaed519a14c19b5b706f5e1191 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 4 Mar 2026 18:16:48 +0100 Subject: [PATCH 04/16] element: Introduce SurfaceRenderElement --- src/backend/render/cursor.rs | 29 +++-- src/backend/render/element.rs | 5 +- src/backend/render/mod.rs | 4 +- .../render/{ => wayland}/clipped_surface.rs | 16 ++- src/backend/render/wayland/mod.rs | 111 ++++++++++++++++++ src/shell/element/stack.rs | 77 +++--------- src/shell/element/surface.rs | 51 ++++---- src/shell/element/window.rs | 84 ++++--------- src/shell/layout/tiling/mod.rs | 49 ++++---- src/shell/workspace.rs | 2 + src/utils/screenshot.rs | 14 +-- .../handlers/image_copy_capture/render.rs | 16 +-- 12 files changed, 249 insertions(+), 209 deletions(-) rename src/backend/render/{ => wayland}/clipped_surface.rs (95%) create mode 100644 src/backend/render/wayland/mod.rs diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index 76426c5ec..c85070dae 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -1,6 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::{utils::prelude::*, wayland::handlers::compositor::FRAME_TIME_FILTER}; +use crate::{ + backend::render::{ + element::AsGlowRenderer, + wayland::{SurfaceRenderElement, render_elements_from_surface_tree}, + }, + utils::prelude::*, + wayland::handlers::compositor::FRAME_TIME_FILTER, +}; use smithay::{ backend::{ allocator::Fourcc, @@ -9,10 +16,10 @@ use smithay::{ element::{ Kind, memory::{MemoryRenderBuffer, MemoryRenderBufferRenderElement}, - surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree}, }, }, }, + desktop::utils::bbox_from_surface_tree, input::{ Seat, pointer::{CursorIcon, CursorImageAttributes, CursorImageStatus}, @@ -118,9 +125,9 @@ fn load_icon(theme: &CursorTheme, shape: CursorIcon) -> Result, Error } render_elements! { - pub CursorRenderElement where R: ImportAll + ImportMem; + pub CursorRenderElement where R: ImportAll + ImportMem + AsGlowRenderer; Static=MemoryRenderBufferRenderElement, - Surface=WaylandSurfaceRenderElement, + Surface=SurfaceRenderElement, } pub fn draw_surface_cursor( @@ -130,7 +137,7 @@ pub fn draw_surface_cursor( scale: impl Into>, ) -> Vec<(CursorRenderElement, Point)> where - R: Renderer + ImportAll, + R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, { let scale = scale.into(); @@ -149,8 +156,11 @@ where renderer, surface, location.to_physical(scale).to_i32_round(), + bbox_from_surface_tree(surface, location.to_i32_round()).to_f64(), scale, 1.0, + false, + [0; 4], Kind::Cursor, ) .into_iter() @@ -164,9 +174,9 @@ pub fn draw_dnd_icon( surface: &wl_surface::WlSurface, location: Point, scale: impl Into>, -) -> Vec> +) -> Vec> where - R: Renderer + ImportAll, + R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, { if get_role(surface) != Some("dnd_icon") { @@ -180,8 +190,11 @@ where renderer, surface, location.to_physical(scale).to_i32_round(), + bbox_from_surface_tree(surface, location.to_i32_round()).to_f64(), scale, 1.0, + false, + [0; 4], FRAME_TIME_FILTER, ) } @@ -261,7 +274,7 @@ pub fn draw_cursor( draw_default: bool, ) -> Vec<(CursorRenderElement, Point)> where - R: Renderer + ImportMem + ImportAll, + R: Renderer + ImportMem + ImportAll + AsGlowRenderer, R::TextureId: Send + Clone + 'static, { // draw the cursor as relevant diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index 04f7fd9e4..34a351c90 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -1,5 +1,5 @@ use crate::{ - backend::render::GlMultiError, + backend::render::{GlMultiError, wayland::SurfaceRenderElement}, shell::{CosmicMappedRenderElement, WorkspaceRenderElement}, }; @@ -13,7 +13,6 @@ use smithay::{ element::{ Element, Id, Kind, RenderElement, UnderlyingStorage, memory::MemoryRenderBufferRenderElement, - surface::WaylandSurfaceRenderElement, utils::{CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement}, }, gles::{GlesError, GlesRenderbuffer, GlesTexture, element::TextureShaderElement}, @@ -38,7 +37,7 @@ where RelocateRenderElement>>>, ), Cursor(RescaleRenderElement>>), - Dnd(WaylandSurfaceRenderElement), + Dnd(SurfaceRenderElement), MoveGrab(RescaleRenderElement>), Postprocess( CropRenderElement>>, diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 40537167d..d7f219bf7 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -15,9 +15,9 @@ use crate::{ backend::{ kms::render::gles::GbmGlowBackend, render::{ - clipped_surface::{CLIPPING_SHADER, ClippingShader}, element::DamageElement, shadow::{SHADOW_SHADER, ShadowShader}, + wayland::clipped_surface::{CLIPPING_SHADER, ClippingShader}, }, }, config::ScreenFilter, @@ -80,10 +80,10 @@ use smithay::{ use smithay_egui::EguiState; pub mod animations; -pub mod clipped_surface; pub mod cursor; pub mod element; pub mod shadow; +pub mod wayland; use self::element::{AsGlowRenderer, CosmicElement}; use super::kms::Timings; diff --git a/src/backend/render/clipped_surface.rs b/src/backend/render/wayland/clipped_surface.rs similarity index 95% rename from src/backend/render/clipped_surface.rs rename to src/backend/render/wayland/clipped_surface.rs index 8705051e2..37eee7f85 100644 --- a/src/backend/render/clipped_surface.rs +++ b/src/backend/render/wayland/clipped_surface.rs @@ -6,7 +6,7 @@ use cgmath::{Matrix3, Vector2}; use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform}; use smithay::{ backend::renderer::{ - ImportAll, ImportMem, Renderer, + ImportAll, Renderer, element::{ Element, Id, Kind, RenderElement, UnderlyingStorage, surface::WaylandSurfaceRenderElement, @@ -19,7 +19,7 @@ use smithay::{ use crate::backend::render::element::AsGlowRenderer; -pub static CLIPPING_SHADER: &str = include_str!("./shaders/clipped_surface.frag"); +pub static CLIPPING_SHADER: &str = include_str!("../shaders/clipped_surface.frag"); pub struct ClippingShader(pub GlesTexProgram); impl ClippingShader { @@ -35,10 +35,7 @@ impl ClippingShader { } #[derive(Debug)] -pub struct ClippedSurfaceRenderElement -where - R: Renderer + ImportAll + ImportMem, -{ +pub struct ClippedSurfaceRenderElement { inner: WaylandSurfaceRenderElement, program: GlesTexProgram, radius: [u8; 4], @@ -48,7 +45,7 @@ where impl ClippedSurfaceRenderElement where - R: Renderer + ImportAll + ImportMem, + R: Renderer + ImportAll, { pub fn new( renderer: &mut R, @@ -174,7 +171,8 @@ where impl Element for ClippedSurfaceRenderElement where - R: Renderer + ImportAll + ImportMem, + R: Renderer + ImportAll + AsGlowRenderer, + R::TextureId: 'static, { fn id(&self) -> &Id { self.inner.id() @@ -247,7 +245,7 @@ where impl RenderElement for ClippedSurfaceRenderElement where - R: AsGlowRenderer + Renderer + ImportAll + ImportMem, + R: AsGlowRenderer + Renderer + ImportAll, R::TextureId: 'static, { fn draw( diff --git a/src/backend/render/wayland/mod.rs b/src/backend/render/wayland/mod.rs new file mode 100644 index 000000000..64c5945f9 --- /dev/null +++ b/src/backend/render/wayland/mod.rs @@ -0,0 +1,111 @@ +use smithay::{ + backend::renderer::{ + ImportAll, Renderer, + element::surface::{KindEvaluation, WaylandSurfaceRenderElement}, + utils::RendererSurfaceStateUserData, + }, + reexports::wayland_server::protocol::wl_surface, + render_elements, + utils::{Logical, Physical, Point, Rectangle, Scale}, + wayland::compositor::{self, TraversalAction}, +}; +use tracing::warn; + +use crate::backend::render::{ + element::AsGlowRenderer, wayland::clipped_surface::ClippedSurfaceRenderElement, +}; + +pub mod clipped_surface; + +render_elements! { + pub SurfaceRenderElement where R: AsGlowRenderer + ImportAll; + Clipped=ClippedSurfaceRenderElement, + Wayland=WaylandSurfaceRenderElement, +} + +pub fn render_elements_from_surface_tree( + renderer: &mut R, + main_surface: &wl_surface::WlSurface, + location: impl Into>, + geometry: impl Into>, + scale: impl Into>, + alpha: f32, + should_clip: bool, + radii: [u8; 4], + kind: impl Into, +) -> Vec +where + R: Renderer + ImportAll + AsGlowRenderer, + R::TextureId: Clone + 'static, + E: From>, +{ + let location = location.into().to_f64(); + let geometry = geometry.into().to_f64(); + let scale = scale.into(); + let kind = kind.into(); + let mut surfaces: Vec = Vec::new(); + + compositor::with_surface_tree_downward( + main_surface, + location, + |_, states, location| { + let mut location = *location; + let data = states.data_map.get::(); + + if let Some(data) = data { + if let Some(view) = data.lock().unwrap().view() { + location += view.offset.to_f64().to_physical(scale); + TraversalAction::DoChildren(location) + } else { + TraversalAction::SkipChildren + } + } else { + TraversalAction::SkipChildren + } + }, + |surface, states, location| { + let mut location = *location; + let kind = kind.eval(states); + let data = states.data_map.get::(); + + if let Some(data) = data { + let has_view = if let Some(view) = data.lock().unwrap().view() { + location += view.offset.to_f64().to_physical(scale); + + true + } else { + false + }; + + if has_view { + match WaylandSurfaceRenderElement::from_surface( + renderer, surface, states, location, alpha, kind, + ) { + Ok(Some(surface)) => { + let elem: SurfaceRenderElement = if radii.iter().any(|r| *r != 0) + && should_clip + && ClippedSurfaceRenderElement::will_clip( + &surface, scale, geometry, radii, + ) { + ClippedSurfaceRenderElement::new( + renderer, surface, scale, geometry, radii, + ) + .into() + } else { + surface.into() + }; + surfaces.push(elem.into()); + } + Ok(None) => {} // surface is not mapped + Err(err) => { + warn!("Failed to import surface: {}", err); + } + }; + } + } + }, + |_, _, _| true, + ); + + surfaces +} diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index fbcba6117..4a00529d5 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -4,8 +4,8 @@ use super::{ }; use crate::{ backend::render::{ - IndicatorShader, Key, Usage, clipped_surface::ClippedSurfaceRenderElement, - cursor::CursorState, element::AsGlowRenderer, shadow::ShadowShader, + IndicatorShader, Key, Usage, cursor::CursorState, element::AsGlowRenderer, + shadow::ShadowShader, wayland::SurfaceRenderElement, }, hooks::{Decorations, HOOKS}, shell::{ @@ -40,7 +40,6 @@ use smithay::{ element::{ AsRenderElements, Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage, memory::MemoryRenderBufferRenderElement, - surface::WaylandSurfaceRenderElement, }, gles::element::PixelShaderElement, glow::GlowRenderer, @@ -796,31 +795,15 @@ impl CosmicStack { }); border.into_iter().chain( - windows[active] - .render_elements::>( - renderer, - window_loc, - scale, - alpha, - scanout_override, - ) - .into_iter() - .map(move |elem| { - let radii = radii.map(|[a, _, c, _]| [a, 0, c, 0]); - if radii.is_some_and(|radii| { - ClippedSurfaceRenderElement::will_clip(&elem, scale, geo, radii) - }) { - CosmicStackRenderElement::Clipped(ClippedSurfaceRenderElement::new( - renderer, - elem, - scale, - geo, - radii.unwrap(), - )) - } else { - CosmicStackRenderElement::Window(elem) - } - }), + windows[active].render_elements::>( + renderer, + window_loc, + scale, + alpha, + scanout_override, + radii.is_some(), + radii.unwrap_or([0; 4]), + ), ) })); @@ -1853,8 +1836,7 @@ pub enum CosmicStackRenderElement { Header(MemoryRenderBufferRenderElement), Shadow(PixelShaderElement), Border(PixelShaderElement), - Window(WaylandSurfaceRenderElement), - Clipped(ClippedSurfaceRenderElement), + Window(SurfaceRenderElement), } impl From> @@ -1865,25 +1847,18 @@ impl From From> +impl From> for CosmicStackRenderElement { - fn from(value: WaylandSurfaceRenderElement) -> Self { + fn from(value: SurfaceRenderElement) -> Self { Self::Window(value) } } -impl From> - for CosmicStackRenderElement -{ - fn from(value: ClippedSurfaceRenderElement) -> Self { - Self::Clipped(value) - } -} - impl Element for CosmicStackRenderElement where - R: Renderer + ImportAll + ImportMem, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + R::TextureId: 'static, { fn id(&self) -> &RendererId { match self { @@ -1891,7 +1866,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.id(), CosmicStackRenderElement::Border(elem) => elem.id(), CosmicStackRenderElement::Window(elem) => elem.id(), - CosmicStackRenderElement::Clipped(elem) => elem.id(), } } @@ -1901,7 +1875,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.current_commit(), CosmicStackRenderElement::Border(elem) => elem.current_commit(), CosmicStackRenderElement::Window(elem) => elem.current_commit(), - CosmicStackRenderElement::Clipped(elem) => elem.current_commit(), } } @@ -1911,7 +1884,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.src(), CosmicStackRenderElement::Border(elem) => elem.src(), CosmicStackRenderElement::Window(elem) => elem.src(), - CosmicStackRenderElement::Clipped(elem) => elem.src(), } } @@ -1921,7 +1893,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.geometry(scale), CosmicStackRenderElement::Border(elem) => elem.geometry(scale), CosmicStackRenderElement::Window(elem) => elem.geometry(scale), - CosmicStackRenderElement::Clipped(elem) => elem.geometry(scale), } } @@ -1931,7 +1902,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.location(scale), CosmicStackRenderElement::Border(elem) => elem.location(scale), CosmicStackRenderElement::Window(elem) => elem.location(scale), - CosmicStackRenderElement::Clipped(elem) => elem.location(scale), } } @@ -1941,7 +1911,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.transform(), CosmicStackRenderElement::Border(elem) => elem.transform(), CosmicStackRenderElement::Window(elem) => elem.transform(), - CosmicStackRenderElement::Clipped(elem) => elem.transform(), } } @@ -1955,7 +1924,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.damage_since(scale, commit), CosmicStackRenderElement::Border(elem) => elem.damage_since(scale, commit), CosmicStackRenderElement::Window(elem) => elem.damage_since(scale, commit), - CosmicStackRenderElement::Clipped(elem) => elem.damage_since(scale, commit), } } @@ -1965,7 +1933,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.opaque_regions(scale), CosmicStackRenderElement::Border(elem) => elem.opaque_regions(scale), CosmicStackRenderElement::Window(elem) => elem.opaque_regions(scale), - CosmicStackRenderElement::Clipped(elem) => elem.opaque_regions(scale), } } @@ -1975,7 +1942,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.alpha(), CosmicStackRenderElement::Border(elem) => elem.alpha(), CosmicStackRenderElement::Window(elem) => elem.alpha(), - CosmicStackRenderElement::Clipped(elem) => elem.alpha(), } } @@ -1985,7 +1951,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.kind(), CosmicStackRenderElement::Border(elem) => elem.kind(), CosmicStackRenderElement::Window(elem) => elem.kind(), - CosmicStackRenderElement::Clipped(elem) => elem.kind(), } } @@ -1995,7 +1960,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.is_framebuffer_effect(), CosmicStackRenderElement::Border(elem) => elem.is_framebuffer_effect(), CosmicStackRenderElement::Window(elem) => elem.is_framebuffer_effect(), - CosmicStackRenderElement::Clipped(elem) => elem.is_framebuffer_effect(), } } } @@ -2007,7 +1971,7 @@ where { fn draw( &self, - frame: &mut ::Frame<'_, '_>, + frame: &mut R::Frame<'_, '_>, src: Rectangle, dst: Rectangle, damage: &[Rectangle], @@ -2033,9 +1997,6 @@ where CosmicStackRenderElement::Window(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } - CosmicStackRenderElement::Clipped(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions, cache) - } } } @@ -2046,7 +2007,6 @@ where elem.underlying_storage(renderer.glow_renderer_mut()) } CosmicStackRenderElement::Window(elem) => elem.underlying_storage(renderer), - CosmicStackRenderElement::Clipped(elem) => elem.underlying_storage(renderer), } } @@ -2074,9 +2034,6 @@ where CosmicStackRenderElement::Window(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } - CosmicStackRenderElement::Clipped(elem) => { - elem.capture_framebuffer(frame, src, dst, cache) - } } } } diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 5cf94ea4b..bac072a3c 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -1,4 +1,8 @@ use crate::{ + backend::render::{ + element::AsGlowRenderer, + wayland::{SurfaceRenderElement, render_elements_from_surface_tree}, + }, shell::focus::target::PointerFocusTarget, wayland::{ handlers::compositor::frame_time_filter_fn, protocols::corner_radius::CacheableCorners, @@ -16,10 +20,7 @@ use std::{ use smithay::{ backend::renderer::{ ImportAll, Renderer, - element::{ - AsRenderElements, Kind, RenderElementStates, - surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree}, - }, + element::{Kind, RenderElementStates}, }, desktop::{ PopupManager, WeakWindow, Window, WindowSurface, WindowSurfaceType, space::SpaceElement, @@ -767,9 +768,9 @@ impl CosmicSurface { alpha: f32, ) -> Vec where - R: Renderer + ImportAll, + R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, - C: From>, + C: From>, { match self.0.underlying_surface() { WindowSurface::Wayland(toplevel) => { @@ -778,13 +779,18 @@ impl CosmicSurface { .flat_map(move |(popup, popup_offset)| { let offset = (self.0.geometry().loc + popup_offset - popup.geometry().loc) .to_physical_precise_round(scale); + let mut geometry = popup.geometry().to_f64(); + geometry.loc += location.to_f64().to_logical(scale) + popup_offset.to_f64(); render_elements_from_surface_tree( renderer, popup.wl_surface(), location + offset, + geometry, scale, alpha, + false, + [0; 4], FRAME_TIME_FILTER, ) }) @@ -801,12 +807,17 @@ impl CosmicSurface { scale: Scale, alpha: f32, scanout_override: Option, + should_clip: bool, + radii: [u8; 4], ) -> Vec where - R: Renderer + ImportAll, + R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, - C: From>, + C: From>, { + let mut geometry = self.0.geometry().to_f64(); + geometry.loc += location.to_f64().to_logical(scale); + match self.0.underlying_surface() { WindowSurface::Wayland(toplevel) => { let surface = toplevel.wl_surface(); @@ -815,8 +826,11 @@ impl CosmicSurface { renderer, surface, location, + geometry, scale, alpha, + should_clip, + radii, scanout_override .map(|val| { if val { @@ -838,8 +852,11 @@ impl CosmicSurface { renderer, &surface, location, + geometry, scale, alpha, + should_clip, + radii, scanout_override .map(|val| { if val { @@ -993,24 +1010,6 @@ impl X11Relatable for CosmicSurface { } } -impl AsRenderElements for CosmicSurface -where - R: Renderer + ImportAll, - R::TextureId: Clone + 'static, -{ - type RenderElement = WaylandSurfaceRenderElement; - - fn render_elements>( - &self, - renderer: &mut R, - location: Point, - scale: Scale, - alpha: f32, - ) -> Vec { - self.0.render_elements(renderer, location, scale, alpha) - } -} - fn with_toplevel_state) -> T>( toplevel: &ToplevelSurface, pending: bool, diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index ac07e13e6..cb09f323a 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1,7 +1,7 @@ use crate::{ backend::render::{ - IndicatorShader, Key, Usage, clipped_surface::ClippedSurfaceRenderElement, - cursor::CursorState, element::AsGlowRenderer, shadow::ShadowShader, + IndicatorShader, Key, Usage, cursor::CursorState, element::AsGlowRenderer, + shadow::ShadowShader, wayland::SurfaceRenderElement, }, hooks::{Decorations, HOOKS}, shell::{ @@ -26,7 +26,6 @@ use smithay::{ element::{ AsRenderElements, Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage, memory::MemoryRenderBufferRenderElement, - surface::WaylandSurfaceRenderElement, }, gles::element::PixelShaderElement, glow::GlowRenderer, @@ -333,7 +332,7 @@ impl CosmicWindow { alpha: f32, ) -> Vec where - R: Renderer + ImportAll + ImportMem, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, R::TextureId: Send + Clone + 'static, C: From>, { @@ -519,35 +518,20 @@ impl CosmicWindow { elements.push(elem); } - let window_elements = self.0.with_program(|p| { - p.window - .render_elements::>( - renderer, - window_loc, - scale, - alpha, - scanout_override, - ) - }); - if window_elements.is_empty() { - return Vec::new(); + if has_ssd { + radii[1] = 0; + radii[3] = 0; } - - elements.extend(window_elements.into_iter().map(|elem| { - if has_ssd { - radii[1] = 0; - radii[3] = 0; - } - if radii.iter().any(|x| *x != 0) - && clip - && ClippedSurfaceRenderElement::will_clip(&elem, scale, geo, radii) - { - CosmicWindowRenderElement::Clipped(ClippedSurfaceRenderElement::new( - renderer, elem, scale, geo, radii, - )) - } else { - CosmicWindowRenderElement::Window(elem) - } + elements.extend(self.0.with_program(|p| { + p.window.render_elements::>( + renderer, + window_loc, + scale, + alpha, + scanout_override, + clip, + radii, + ) })); if has_ssd { @@ -1221,8 +1205,7 @@ pub enum CosmicWindowRenderElement { Header(MemoryRenderBufferRenderElement), Shadow(PixelShaderElement), Border(PixelShaderElement), - Window(WaylandSurfaceRenderElement), - Clipped(ClippedSurfaceRenderElement), + Window(SurfaceRenderElement), } impl From> @@ -1233,25 +1216,18 @@ impl From From> +impl From> for CosmicWindowRenderElement { - fn from(value: WaylandSurfaceRenderElement) -> Self { + fn from(value: SurfaceRenderElement) -> Self { Self::Window(value) } } -impl From> - for CosmicWindowRenderElement -{ - fn from(value: ClippedSurfaceRenderElement) -> Self { - Self::Clipped(value) - } -} - impl Element for CosmicWindowRenderElement where - R: Renderer + ImportAll + ImportMem, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + R::TextureId: 'static, { fn id(&self) -> &RendererId { match self { @@ -1259,7 +1235,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.id(), CosmicWindowRenderElement::Border(elem) => elem.id(), CosmicWindowRenderElement::Window(elem) => elem.id(), - CosmicWindowRenderElement::Clipped(elem) => elem.id(), } } @@ -1269,7 +1244,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.current_commit(), CosmicWindowRenderElement::Border(elem) => elem.current_commit(), CosmicWindowRenderElement::Window(elem) => elem.current_commit(), - CosmicWindowRenderElement::Clipped(elem) => elem.current_commit(), } } @@ -1279,7 +1253,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.src(), CosmicWindowRenderElement::Border(elem) => elem.src(), CosmicWindowRenderElement::Window(elem) => elem.src(), - CosmicWindowRenderElement::Clipped(elem) => elem.src(), } } @@ -1289,7 +1262,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.geometry(scale), CosmicWindowRenderElement::Border(elem) => elem.geometry(scale), CosmicWindowRenderElement::Window(elem) => elem.geometry(scale), - CosmicWindowRenderElement::Clipped(elem) => elem.geometry(scale), } } @@ -1299,7 +1271,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.location(scale), CosmicWindowRenderElement::Border(elem) => elem.location(scale), CosmicWindowRenderElement::Window(elem) => elem.location(scale), - CosmicWindowRenderElement::Clipped(elem) => elem.location(scale), } } @@ -1309,7 +1280,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.transform(), CosmicWindowRenderElement::Border(elem) => elem.transform(), CosmicWindowRenderElement::Window(elem) => elem.transform(), - CosmicWindowRenderElement::Clipped(elem) => elem.transform(), } } @@ -1323,7 +1293,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.damage_since(scale, commit), CosmicWindowRenderElement::Border(elem) => elem.damage_since(scale, commit), CosmicWindowRenderElement::Window(elem) => elem.damage_since(scale, commit), - CosmicWindowRenderElement::Clipped(elem) => elem.damage_since(scale, commit), } } @@ -1333,7 +1302,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.opaque_regions(scale), CosmicWindowRenderElement::Border(elem) => elem.opaque_regions(scale), CosmicWindowRenderElement::Window(elem) => elem.opaque_regions(scale), - CosmicWindowRenderElement::Clipped(elem) => elem.opaque_regions(scale), } } @@ -1343,7 +1311,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.alpha(), CosmicWindowRenderElement::Border(elem) => elem.alpha(), CosmicWindowRenderElement::Window(elem) => elem.alpha(), - CosmicWindowRenderElement::Clipped(elem) => elem.alpha(), } } @@ -1353,7 +1320,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.kind(), CosmicWindowRenderElement::Border(elem) => elem.kind(), CosmicWindowRenderElement::Window(elem) => elem.kind(), - CosmicWindowRenderElement::Clipped(elem) => elem.kind(), } } @@ -1363,7 +1329,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.is_framebuffer_effect(), CosmicWindowRenderElement::Border(elem) => elem.is_framebuffer_effect(), CosmicWindowRenderElement::Window(elem) => elem.is_framebuffer_effect(), - CosmicWindowRenderElement::Clipped(elem) => elem.is_framebuffer_effect(), } } } @@ -1401,9 +1366,6 @@ where CosmicWindowRenderElement::Window(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } - CosmicWindowRenderElement::Clipped(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions, cache) - } } } @@ -1414,7 +1376,6 @@ where elem.underlying_storage(renderer.glow_renderer_mut()) } CosmicWindowRenderElement::Window(elem) => elem.underlying_storage(renderer), - CosmicWindowRenderElement::Clipped(elem) => elem.underlying_storage(renderer), } } @@ -1442,9 +1403,6 @@ where CosmicWindowRenderElement::Window(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } - CosmicWindowRenderElement::Clipped(elem) => { - elem.capture_framebuffer(frame, src, dst, cache) - } } } } diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 6db8aef28..157df87cb 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -5370,29 +5370,32 @@ where (swap_geo.loc.as_logical() - window_geo.loc).to_physical_precise_round(output_scale); swap_elements.extend( - AsRenderElements::render_elements::>( - &window, - renderer, - render_loc, - output_scale.into(), - 1.0, - ) - .into_iter() - .map(|window| { - CosmicMappedRenderElement::GrabbedWindow(RescaleRenderElement::from_element( - window, - swap_geo - .loc - .as_logical() - .to_physical_precise_round(output_scale), - ease( - Linear, - 1.0, - swap_factor(window_geo.size), - transition.unwrap_or(1.0), - ), - )) - }), + window + .render_elements( + renderer, + render_loc, + output_scale.into(), + 1.0, + None, + false, + [0; 4], + ) + .into_iter() + .map(|window| { + CosmicMappedRenderElement::GrabbedWindow(RescaleRenderElement::from_element( + window, + swap_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), + ease( + Linear, + 1.0, + swap_factor(window_geo.size), + transition.unwrap_or(1.0), + ), + )) + }), ) } diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 8026e96e6..ff4bc2b8d 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1619,6 +1619,8 @@ impl Workspace { output_scale.into(), alpha, Some(true), + false, + [0; 4], ) .into_iter() .map(animation_rescale) diff --git a/src/utils/screenshot.rs b/src/utils/screenshot.rs index e90fb532d..149227870 100644 --- a/src/utils/screenshot.rs +++ b/src/utils/screenshot.rs @@ -3,9 +3,7 @@ use smithay::{ backend::{ allocator::Fourcc, renderer::{ - ExportMem, ImportAll, Offscreen, Renderer, - damage::OutputDamageTracker, - element::{AsRenderElements, surface::WaylandSurfaceRenderElement}, + ExportMem, ImportAll, Offscreen, Renderer, damage::OutputDamageTracker, gles::GlesRenderbuffer, }, }, @@ -16,7 +14,7 @@ use smithay::{ use tracing::warn; use crate::{ - backend::render::RendererRef, + backend::render::{RendererRef, element::AsGlowRenderer, wayland::SurfaceRenderElement}, shell::element::CosmicSurface, state::{State, advertised_node_for_surface}, }; @@ -24,17 +22,19 @@ use crate::{ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) { fn render_window(renderer: &mut R, window: &CosmicSurface) -> anyhow::Result<()> where - R: Renderer + ImportAll + Offscreen + ExportMem, + R: Renderer + ImportAll + Offscreen + ExportMem + AsGlowRenderer, R::TextureId: Clone + 'static, R::Error: Send + Sync + 'static, { let bbox = bbox_from_surface_tree(&window.wl_surface().unwrap(), (0, 0)); - let elements = AsRenderElements::::render_elements::>( - window, + let elements = window.render_elements::>( renderer, (-bbox.loc.x, -bbox.loc.y).into(), Scale::from(1.0), 1.0, + None, + false, + [0; 4], ); // TODO: 10-bit diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index 6f8f2520e..e7bb36ba2 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -9,8 +9,7 @@ use smithay::{ buffer_dimensions, buffer_type, damage::{Error as DTError, OutputDamageTracker, RenderOutputResult}, element::{ - AsRenderElements, RenderElement, - surface::WaylandSurfaceRenderElement, + RenderElement, utils::{Relocate, RelocateRenderElement}, }, gles::{GlesError, GlesRenderbuffer}, @@ -43,6 +42,7 @@ use crate::{ CursorMode, ElementFilter, RendererRef, cursor, element::{AsGlowRenderer, CosmicElement, DamageElement}, render_workspace, + wayland::SurfaceRenderElement, }, shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement}, state::{Common, KmsNodes, State}, @@ -501,8 +501,8 @@ pub fn render_workspace_to_buffer( } smithay::render_elements! { - pub WindowCaptureElement where R: ImportAll + ImportMem; - WaylandElement=WaylandSurfaceRenderElement, + pub WindowCaptureElement where R: ImportAll + ImportMem + AsGlowRenderer; + WaylandElement=SurfaceRenderElement, CursorElement=RelocateRenderElement>, } @@ -628,14 +628,14 @@ pub fn render_window_to_buffer( } } - elements.extend(AsRenderElements::::render_elements::< - WindowCaptureElement, - >( - toplevel, + elements.extend(toplevel.render_elements::>( renderer, (-geometry.loc.x, -geometry.loc.y).into(), Scale::from(1.0), 1.0, + None, + false, + [0; 4], )); if let Ok(dmabuf) = get_dmabuf(buffer) { From 5cda13e3ead6d91aed2f15a20fe607ef1a059b05 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 4 Mar 2026 18:57:37 +0100 Subject: [PATCH 05/16] element: Refactor element acquisition to be push based --- src/backend/render/cursor.rs | 39 +- src/backend/render/mod.rs | 343 ++++++------ src/backend/render/wayland/mod.rs | 24 +- src/shell/element/mod.rs | 75 ++- src/shell/element/stack.rs | 79 ++- src/shell/element/surface.rs | 69 +-- src/shell/element/window.rs | 59 +- src/shell/grabs/menu/mod.rs | 42 +- src/shell/grabs/moving.rs | 230 ++++---- src/shell/layout/floating/mod.rs | 270 +++++---- src/shell/layout/tiling/mod.rs | 511 +++++++++--------- src/shell/workspace.rs | 174 +++--- src/shell/zoom.rs | 30 +- src/utils/iced/mod.rs | 22 +- src/utils/screenshot.rs | 7 +- .../handlers/image_copy_capture/render.rs | 83 +-- 16 files changed, 1018 insertions(+), 1039 deletions(-) diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index c85070dae..0951ebda6 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -3,7 +3,7 @@ use crate::{ backend::render::{ element::AsGlowRenderer, - wayland::{SurfaceRenderElement, render_elements_from_surface_tree}, + wayland::{SurfaceRenderElement, push_render_elements_from_surface_tree}, }, utils::prelude::*, wayland::handlers::compositor::FRAME_TIME_FILTER, @@ -135,8 +135,8 @@ pub fn draw_surface_cursor( surface: &wl_surface::WlSurface, location: Point, scale: impl Into>, -) -> Vec<(CursorRenderElement, Point)> -where + push: &mut dyn FnMut(CursorRenderElement, Point), +) where R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, { @@ -152,7 +152,7 @@ where .to_physical_precise_round(scale) }); - render_elements_from_surface_tree( + push_render_elements_from_surface_tree( renderer, surface, location.to_physical(scale).to_i32_round(), @@ -162,10 +162,9 @@ where false, [0; 4], Kind::Cursor, - ) - .into_iter() - .map(|elem| (elem, h)) - .collect() + &mut |elem| push(elem.into(), h), + None, + ); } #[profiling::function] @@ -174,8 +173,8 @@ pub fn draw_dnd_icon( surface: &wl_surface::WlSurface, location: Point, scale: impl Into>, -) -> Vec> -where + push: &mut dyn FnMut(SurfaceRenderElement), +) where R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, { @@ -186,7 +185,7 @@ where ); } let scale = scale.into(); - render_elements_from_surface_tree( + push_render_elements_from_surface_tree( renderer, surface, location.to_physical(scale).to_i32_round(), @@ -196,7 +195,9 @@ where false, [0; 4], FRAME_TIME_FILTER, - ) + push, + None, + ); } pub type CursorState = Mutex; @@ -272,8 +273,8 @@ pub fn draw_cursor( buffer_scale: f64, time: Time, draw_default: bool, -) -> Vec<(CursorRenderElement, Point)> -where + push: &mut dyn FnMut(CursorRenderElement, Point), +) where R: Renderer + ImportMem + ImportAll + AsGlowRenderer, R::TextureId: Send + Clone + 'static, { @@ -290,7 +291,7 @@ where }); if let Some(current_cursor) = named_cursor { if !draw_default && current_cursor == CursorIcon::Default { - return Vec::new(); + return; } let integer_scale = (scale.x.max(scale.y) * buffer_scale).ceil() as u32; @@ -327,7 +328,7 @@ where ); state.current_image = Some(frame); - return vec![( + push( CursorRenderElement::Static( MemoryRenderBufferRenderElement::from_buffer( renderer, @@ -341,10 +342,8 @@ where .expect("Failed to import cursor bitmap"), ), hotspot.to_physical_precise_round(scale), - )]; + ); } else if let CursorImageStatus::Surface(ref wl_surface) = cursor_status { - return draw_surface_cursor(renderer, wl_surface, location, scale); - } else { - Vec::new() + draw_surface_cursor(renderer, wl_surface, location, scale, push); } } diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index d7f219bf7..395431331 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -17,7 +17,11 @@ use crate::{ render::{ element::DamageElement, shadow::{SHADOW_SHADER, ShadowShader}, - wayland::clipped_surface::{CLIPPING_SHADER, ClippingShader}, + wayland::{ + SurfaceRenderElement, + clipped_surface::{CLIPPING_SHADER, ClippingShader}, + push_render_elements_from_surface_tree, + }, }, }, config::ScreenFilter, @@ -47,11 +51,10 @@ use smithay::{ allocator::Fourcc, drm::{DrmDeviceFd, DrmNode}, renderer::{ - Color32F, ImportAll, Offscreen, Renderer, Texture, TextureFilter, + Color32F, Offscreen, Texture, TextureFilter, damage::{Error as RenderError, OutputDamageTracker, RenderOutputResult}, element::{ Element, Id, Kind, RenderElement, WeakId, - surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree}, texture::{TextureRenderBuffer, TextureRenderElement}, utils::{ ConstrainAlign, ConstrainScaleBehavior, CropRenderElement, Relocate, @@ -68,6 +71,7 @@ use smithay::{ sync::SyncPoint, }, }, + desktop::utils::bbox_from_surface_tree, input::Seat, output::{Output, OutputModeSource, OutputNoMode}, utils::{ @@ -474,8 +478,8 @@ pub fn cursor_elements<'a, 'frame, R>( output: &Output, mode: CursorMode, exclude_dnd_icon: bool, -) -> Vec> -where + push: &mut dyn FnMut(CosmicElement), +) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -489,7 +493,6 @@ where ) }) .unwrap_or_else(|| ((0., 0.).into(), 1.)); - let mut elements = Vec::new(); for seat in seats { let pointer = match seat.get_pointer() { @@ -499,19 +502,16 @@ where let location = pointer.current_location() - output.current_location().to_f64(); if mode != CursorMode::None { - elements.extend( - cursor::draw_cursor( - renderer, - seat, - location, - scale.into(), - zoom_scale, - now, - mode != CursorMode::NotDefault, - ) - .into_iter() - .map(|(elem, hotspot)| { - CosmicElement::Cursor(RescaleRenderElement::from_element( + cursor::draw_cursor( + renderer, + seat, + location, + scale.into(), + zoom_scale, + now, + mode != CursorMode::NotDefault, + &mut |elem, hotspot| { + push(CosmicElement::Cursor(RescaleRenderElement::from_element( RelocateRenderElement::from_element( elem, Point::from((-hotspot.x, -hotspot.y)), @@ -522,63 +522,54 @@ where .to_physical(output.current_scale().fractional_scale()) .to_i32_round(), zoom_scale, - )) - }), + ))) + }, ); } if !exclude_dnd_icon && let Some(dnd_icon) = get_dnd_icon(seat) { - elements.extend( - cursor::draw_dnd_icon( - renderer, - &dnd_icon.surface, - (location + dnd_icon.offset.to_f64()).to_i32_round(), - scale, - ) - .into_iter() - .map(CosmicElement::Dnd), + cursor::draw_dnd_icon( + renderer, + &dnd_icon.surface, + (location + dnd_icon.offset.to_f64()).to_i32_round(), + scale, + &mut |elem| push(CosmicElement::Dnd(elem)), ); } let theme = theme.cosmic(); - if let Some(grab_elements) = seat + if let Some(grab_state) = seat .user_data() .get::() .unwrap() .lock() .unwrap() .as_ref() - .map(|state| state.render::, R>(renderer, output, theme)) { - elements.extend(grab_elements.into_iter().map(|elem| { - CosmicElement::MoveGrab(RescaleRenderElement::from_element( + grab_state.render(renderer, output, theme, &mut |elem| { + push(CosmicElement::MoveGrab(RescaleRenderElement::from_element( elem, focal_point .as_logical() .to_physical(output.current_scale().fractional_scale()) .to_i32_round(), zoom_scale, - )) - })); + ))); + }) } - if let Some((grab_elements, should_scale)) = seat + if let Some(grab_state) = seat .user_data() .get::() .unwrap() .lock() .unwrap() .as_ref() - .map(|state| { - ( - state.render::, R>(renderer, output), - !state.is_in_screen_space(), - ) - }) { - elements.extend(grab_elements.into_iter().map(|elem| { - CosmicElement::MoveGrab(RescaleRenderElement::from_element( - elem, + let should_scale = !grab_state.is_in_screen_space(); + grab_state.render(renderer, output, &mut |elem| { + push(CosmicElement::MoveGrab(RescaleRenderElement::from_element( + elem.into(), if should_scale { focal_point .as_logical() @@ -588,12 +579,10 @@ where Point::from((0, 0)) }, if should_scale { zoom_scale } else { 1.0 }, - )) - })); + ))); + }) } } - - elements } #[cfg(not(feature = "debug"))] @@ -718,7 +707,7 @@ where CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { - let mut elements = Vec::new(); + let mut elements = Vec::>::new(); let shell_ref = shell.read(); let seats = shell_ref.seats.iter().cloned().collect::>(); @@ -731,7 +720,7 @@ where // that is prone to deadlock with the main-thread on some grabs. std::mem::drop(shell_ref); - elements.extend(cursor_elements( + cursor_elements( renderer, seats.iter(), zoom_level, @@ -740,7 +729,8 @@ where output, cursor_mode, element_filter == ElementFilter::ExcludeWorkspaceOverview, - )); + &mut |elem| elements.push(elem), + ); let shell = shell.read(); let overview = shell.overview_mode(); @@ -816,72 +806,81 @@ where render_input_order::<()>(&shell, output, previous, current, element_filter, |stage| { match stage { Stage::ZoomUI => { - elements.extend(ZoomState::render(renderer, output)); + ZoomState::render(renderer, output, &mut |elem| { + elements.push(CosmicElement::Zoom(elem)) + }); } Stage::SessionLock(lock_surface) => { - elements.extend( - session_lock_elements(renderer, output, lock_surface) - .into_iter() - .map(Into::into) - .flat_map(crop_to_output) - .map(Into::into), - ); + session_lock_elements(renderer, output, lock_surface, &mut |elem| { + elements.extend(crop_to_output(elem.into()).map(Into::into)) + }) } Stage::LayerPopup { popup, location, .. } => { - elements.extend( - render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>( - renderer, - popup.wl_surface(), - location - .to_local(output) - .as_logical() - .to_physical_precise_round(scale), - Scale::from(scale), - 1.0, - FRAME_TIME_FILTER, - ) - .into_iter() - .flat_map(crop_to_output) - .map(Into::into), - ); + let mut geometry = popup.geometry().as_global(); + geometry.loc += location; + + push_render_elements_from_surface_tree( + renderer, + popup.wl_surface(), + location + .to_local(output) + .as_logical() + .to_physical_precise_round(scale), + geometry.to_local(output).as_logical().to_f64(), + Scale::from(scale), + 1.0, + false, + [0; 4], + FRAME_TIME_FILTER, + &mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)), + None, + ) } Stage::LayerSurface { layer, location } => { - elements.extend( - render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>( - renderer, - layer.wl_surface(), - location - .to_local(output) - .as_logical() - .to_physical_precise_round(scale), - Scale::from(scale), - 1.0, - FRAME_TIME_FILTER, - ) - .into_iter() - .flat_map(crop_to_output) - .map(Into::into), + let mut geometry = layer.geometry().as_global(); + geometry.loc += location; + + push_render_elements_from_surface_tree( + renderer, + layer.wl_surface(), + location + .to_local(output) + .as_logical() + .to_physical_precise_round(scale), + geometry.to_local(output).as_logical().to_f64(), + Scale::from(scale), + 1.0, + false, + [0; 4], + FRAME_TIME_FILTER, + &mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)), + None, ); } Stage::OverrideRedirect { surface, location } => { - elements.extend(surface.wl_surface().into_iter().flat_map(|surface| { - render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>( + if let Some(wl_surface) = surface.wl_surface() { + let mut geometry = surface.geometry().as_global(); + geometry.loc += location; + + push_render_elements_from_surface_tree( renderer, - &surface, + &wl_surface, location .to_local(output) .as_logical() .to_physical_precise_round(scale), + geometry.to_local(output).as_logical().to_f64(), Scale::from(scale), 1.0, + false, + [0; 4], FRAME_TIME_FILTER, - ) - .into_iter() - .flat_map(crop_to_output) - .map(Into::into) - })); + &mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)), + None, + ); + } } Stage::StickyPopups(layout) => { let alpha = match &overview.0 { @@ -902,14 +901,11 @@ where OverviewMode::None => 1.0, }; - elements.extend( - layout - .render_popups(renderer, alpha) - .into_iter() - .map(Into::into) - .flat_map(crop_to_output) - .map(Into::into), - ); + layout.render_popups(renderer, alpha, &mut |elem| { + if let Some(elem) = crop_to_output(elem.into()) { + elements.push(elem.into()) + } + }); } Stage::Sticky(layout) => { let alpha = match &overview.0 { @@ -934,79 +930,63 @@ where .then_some(last_active_seat) .map(|seat| workspace.focus_stack.get(seat)); - elements.extend( - layout - .render( - renderer, - current_focus.as_ref().and_then(|stack| { - stack.last().and_then(|t| match t { - FocusTarget::Window(w) => Some(w), - _ => None, - }) - }), - resize_indicator.clone(), - active_hint, - alpha, - theme.cosmic(), - ) - .into_iter() - .map(Into::into) - .flat_map(crop_to_output) - .map(Into::into), - ) + layout.render( + renderer, + current_focus.as_ref().and_then(|stack| { + stack.last().and_then(|t| match t { + FocusTarget::Window(w) => Some(w), + _ => None, + }) + }), + resize_indicator.clone(), + active_hint, + alpha, + theme.cosmic(), + &mut |elem| { + if let Some(elem) = crop_to_output(elem.into()) { + elements.push(elem.into()) + } + }, + ); } Stage::WorkspacePopups { workspace, offset } => { - elements.extend( - match workspace.render_popups( - renderer, - last_active_seat, - !move_active && is_active_space, - overview.clone(), - theme.cosmic(), - ) { - Ok(elements) => { - elements - .into_iter() - .flat_map(crop_to_output) - .map(|element| { - CosmicElement::Workspace(RelocateRenderElement::from_element( - element, - offset.to_physical_precise_round(scale), - Relocate::Relative, - )) - }) - } - Err(_) => { - return ControlFlow::Break(Err(OutputNoMode)); + workspace.render_popups( + renderer, + last_active_seat, + !move_active && is_active_space, + overview.clone(), + theme.cosmic(), + &mut |elem| { + if let Some(elem) = crop_to_output(elem) { + elements.push(CosmicElement::Workspace( + RelocateRenderElement::from_element( + elem, + offset.to_physical_precise_round(scale), + Relocate::Relative, + ), + )); } }, ); } Stage::Workspace { workspace, offset } => { - elements.extend( - match workspace.render( - renderer, - last_active_seat, - !move_active && is_active_space, - overview.clone(), - resize_indicator.clone(), - active_hint, - theme.cosmic(), - ) { - Ok(elements) => { - elements - .into_iter() - .flat_map(crop_to_output) - .map(|element| { - CosmicElement::Workspace(RelocateRenderElement::from_element( - element, - offset.to_physical_precise_round(scale), - Relocate::Relative, - )) - }) - } - Err(_) => { - return ControlFlow::Break(Err(OutputNoMode)); + workspace.render( + renderer, + last_active_seat, + !move_active && is_active_space, + overview.clone(), + resize_indicator.clone(), + active_hint, + theme.cosmic(), + &mut |elem| { + if let Some(elem) = crop_to_output(elem) { + elements.push(CosmicElement::Workspace( + RelocateRenderElement::from_element( + elem, + offset.to_physical_precise_round(scale), + Relocate::Relative, + ), + )); } }, ); @@ -1023,23 +1003,26 @@ fn session_lock_elements( renderer: &mut R, output: &Output, lock_surface: Option<&LockSurface>, -) -> Vec> -where - R: Renderer + ImportAll, + push: &mut dyn FnMut(SurfaceRenderElement), +) where + R: AsGlowRenderer, R::TextureId: Clone + 'static, { if let Some(surface) = lock_surface { let scale = Scale::from(output.current_scale().fractional_scale()); - render_elements_from_surface_tree( + push_render_elements_from_surface_tree( renderer, surface.wl_surface(), (0, 0), + bbox_from_surface_tree(surface.wl_surface(), (0, 0)).to_f64(), scale, 1.0, + false, + [0; 4], FRAME_TIME_FILTER, + push, + None, ) - } else { - Vec::new() } } diff --git a/src/backend/render/wayland/mod.rs b/src/backend/render/wayland/mod.rs index 64c5945f9..d766552ad 100644 --- a/src/backend/render/wayland/mod.rs +++ b/src/backend/render/wayland/mod.rs @@ -23,7 +23,7 @@ render_elements! { Wayland=WaylandSurfaceRenderElement, } -pub fn render_elements_from_surface_tree( +pub fn push_render_elements_from_surface_tree( renderer: &mut R, main_surface: &wl_surface::WlSurface, location: impl Into>, @@ -33,17 +33,17 @@ pub fn render_elements_from_surface_tree( should_clip: bool, radii: [u8; 4], kind: impl Into, -) -> Vec -where + push_above: &mut dyn FnMut(SurfaceRenderElement), + mut push_below: Option<&mut dyn FnMut(SurfaceRenderElement)>, +) where R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, - E: From>, { let location = location.into().to_f64(); let geometry = geometry.into().to_f64(); let scale = scale.into(); let kind = kind.into(); - let mut surfaces: Vec = Vec::new(); + let mut passed_main = false; compositor::with_surface_tree_downward( main_surface, @@ -94,7 +94,13 @@ where } else { surface.into() }; - surfaces.push(elem.into()); + if let Some(push_below) = push_below.as_mut() + && passed_main + { + push_below(elem); + } else { + push_above(elem); + } } Ok(None) => {} // surface is not mapped Err(err) => { @@ -103,9 +109,11 @@ where }; } } + + if surface == main_surface { + passed_main = true; + } }, |_, _, _| true, ); - - surfaces } diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 73d7d5d30..dfab0f9ed 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -563,33 +563,31 @@ impl CosmicMapped { } } - pub fn popup_render_elements( + pub fn push_popup_render_elements( &self, renderer: &mut R, location: smithay::utils::Point, scale: smithay::utils::Scale, alpha: f32, - ) -> Vec - where + push: &mut dyn FnMut(CosmicMappedRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, - C: From>, { match &self.element { - CosmicMappedInternal::Stack(s) => s - .popup_render_elements::>( - renderer, location, scale, alpha, - ), - CosmicMappedInternal::Window(w) => w - .popup_render_elements::>( - renderer, location, scale, alpha, - ), + CosmicMappedInternal::Stack(s) => { + s.push_popup_render_elements(renderer, location, scale, alpha, &mut |elem| { + push(elem.into()) + }) + } + CosmicMappedInternal::Window(w) => { + w.push_popup_render_elements(renderer, location, scale, alpha, &mut |elem| { + push(elem.into()) + }) + } _ => unreachable!(), } - .into_iter() - .map(C::from) - .collect() } pub fn shadow_render_element( @@ -636,7 +634,7 @@ impl CosmicMapped { } } - pub fn render_elements( + pub fn push_render_elements( &self, renderer: &mut R, location: smithay::utils::Point, @@ -644,15 +642,15 @@ impl CosmicMapped { scale: smithay::utils::Scale, alpha: f32, scanout_override: Option, - ) -> Vec - where + push_above: &mut dyn FnMut(CosmicMappedRenderElement), + push_below: &mut dyn FnMut(CosmicMappedRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, - C: From>, { #[cfg(feature = "debug")] - let mut elements = if let Some(debug) = self.debug.lock().unwrap().as_mut() { + if let Some(debug) = self.debug.lock().unwrap().as_mut() { let window = self.active_window(); let window_geo = window.geometry(); let (min_size, max_size, size) = ( @@ -813,41 +811,36 @@ impl CosmicMapped { scale.x, 0.8, ) { - Ok(element) => vec![CosmicMappedRenderElement::from(element)], + Ok(element) => push_above(element.into()), Err(err) => { debug!(?err, "Error rendering debug overlay."); - Vec::new() } } - } else { - Vec::new() }; - #[cfg(not(feature = "debug"))] - let mut elements = Vec::new(); - #[cfg_attr(not(feature = "debug"), allow(unused_mut))] - elements.extend(match &self.element { - CosmicMappedInternal::Stack(s) => s.render_elements::>( + match &self.element { + CosmicMappedInternal::Stack(s) => s.push_render_elements( renderer, location, max_size, scale, alpha, scanout_override, + &mut |elem| push_above(elem.into()), + &mut |elem| push_below(elem.into()), + ), + CosmicMappedInternal::Window(w) => w.push_render_elements( + renderer, + location, + max_size, + scale, + alpha, + scanout_override, + &mut |elem| push_above(elem.into()), + &mut |elem| push_below(elem.into()), ), - CosmicMappedInternal::Window(w) => w - .render_elements::>( - renderer, - location, - max_size, - scale, - alpha, - scanout_override, - ), _ => unreachable!(), - }); - - elements.into_iter().map(C::from).collect() + } } pub(crate) fn update_theme(&self, theme: cosmic::Theme) { diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 4a00529d5..25e27a7d3 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -38,8 +38,8 @@ use smithay::{ renderer::{ ImportAll, ImportMem, Renderer, element::{ - AsRenderElements, Element, Id as RendererId, Kind, RenderElement, - UnderlyingStorage, memory::MemoryRenderBufferRenderElement, + Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage, + memory::MemoryRenderBufferRenderElement, }, gles::element::PixelShaderElement, glow::GlowRenderer, @@ -627,30 +627,29 @@ impl CosmicStack { self.0.loop_handle() } - pub fn popup_render_elements( + pub fn push_popup_render_elements( &self, renderer: &mut R, location: Point, scale: Scale, alpha: f32, - ) -> Vec - where + push: &mut dyn FnMut(CosmicStackRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - C: From>, { let window_loc = location + Point::from((0, (TAB_HEIGHT as f64 * scale.y) as i32)); self.0.with_program(|p| { let windows = p.windows.lock().unwrap(); let active = p.active.load(Ordering::SeqCst); - windows[active] - .popup_render_elements::>( - renderer, window_loc, scale, alpha, - ) - .into_iter() - .map(C::from) - .collect() + windows[active].push_popup_render_elements( + renderer, + window_loc, + scale, + alpha, + &mut |elem| push(elem.into()), + ) }) } @@ -721,7 +720,7 @@ impl CosmicStack { }) } - pub fn render_elements( + pub fn push_render_elements( &self, renderer: &mut R, location: Point, @@ -729,17 +728,17 @@ impl CosmicStack { scale: Scale, alpha: f32, scanout_override: Option, - ) -> Vec - where + push_above: &mut dyn FnMut(CosmicStackRenderElement), + push_below: &mut dyn FnMut(CosmicStackRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - C: From>, { if !self .0 .with_program(|p| p.override_alive.load(Ordering::Acquire)) { - return Vec::new(); + return; } let geometry = self @@ -749,11 +748,12 @@ impl CosmicStack { let stack_loc = location + geometry.loc; let window_loc = location + Point::from((0, (TAB_HEIGHT as f64 * scale.y) as i32)); - let mut elements = AsRenderElements::::render_elements::>( - &self.0, renderer, stack_loc, scale, alpha, - ); + self.0 + .push_render_elements(renderer, stack_loc, scale, alpha, &mut |elem| { + push_above(elem.into()) + }); - elements.extend(self.0.with_program(|p| { + self.0.with_program(|p| { let windows = p.windows.lock().unwrap(); let active = p.active.load(Ordering::SeqCst); let theme = p.theme.lock().unwrap(); @@ -780,9 +780,9 @@ impl CosmicStack { let window_key = CosmicMappedKey(CosmicMappedKeyInner::Stack(Arc::downgrade(&self.0.0))); - let border = (!maximized).then(|| { + if !maximized { let (r, g, b, a) = theme.cosmic().bg_divider().into_components(); - CosmicStackRenderElement::Border(IndicatorShader::element( + push_above(CosmicStackRenderElement::Border(IndicatorShader::element( renderer, Key::Window(Usage::Border, window_key.clone()), geo.to_i32_round().as_local(), @@ -791,23 +791,22 @@ impl CosmicStack { a * alpha, scale.x, [r, g, b], - )) - }); - - border.into_iter().chain( - windows[active].render_elements::>( - renderer, - window_loc, - scale, - alpha, - scanout_override, - radii.is_some(), - radii.unwrap_or([0; 4]), - ), - ) - })); + ))); + }; - elements.into_iter().map(C::from).collect() + let radii = radii.map(|[a, _, c, _]| [a, 0, c, 0]); + windows[active].push_render_elements( + renderer, + window_loc, + scale, + alpha, + scanout_override, + radii.is_some(), + radii.unwrap_or([0; 4]), + &mut |elem| push_above(elem.into()), + Some(&mut |elem| push_below(elem.into())), + ); + }); } pub(crate) fn set_theme(&self, theme: cosmic::Theme) { diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index bac072a3c..cd599f0c1 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -1,7 +1,7 @@ use crate::{ backend::render::{ element::AsGlowRenderer, - wayland::{SurfaceRenderElement, render_elements_from_surface_tree}, + wayland::{SurfaceRenderElement, push_render_elements_from_surface_tree}, }, shell::focus::target::PointerFocusTarget, wayland::{ @@ -760,47 +760,46 @@ impl CosmicSurface { self.0.user_data() } - pub fn popup_render_elements( + pub fn push_popup_render_elements( &self, renderer: &mut R, location: Point, scale: Scale, alpha: f32, - ) -> Vec - where + push: &mut dyn FnMut(SurfaceRenderElement), + ) where R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, - C: From>, { match self.0.underlying_surface() { WindowSurface::Wayland(toplevel) => { let surface = toplevel.wl_surface(); - PopupManager::popups_for_surface(surface) - .flat_map(move |(popup, popup_offset)| { - let offset = (self.0.geometry().loc + popup_offset - popup.geometry().loc) - .to_physical_precise_round(scale); - let mut geometry = popup.geometry().to_f64(); - geometry.loc += location.to_f64().to_logical(scale) + popup_offset.to_f64(); - - render_elements_from_surface_tree( - renderer, - popup.wl_surface(), - location + offset, - geometry, - scale, - alpha, - false, - [0; 4], - FRAME_TIME_FILTER, - ) - }) - .collect() + for (popup, popup_offset) in PopupManager::popups_for_surface(surface) { + let offset = (self.0.geometry().loc + popup_offset - popup.geometry().loc) + .to_physical_precise_round(scale); + let mut geometry = popup.geometry().to_f64(); + geometry.loc += location.to_f64().to_logical(scale) + popup_offset.to_f64(); + + push_render_elements_from_surface_tree( + renderer, + popup.wl_surface(), + location + offset, + geometry, + scale, + alpha, + false, + [0; 4], + FRAME_TIME_FILTER, + push, + None, + ) + } } - WindowSurface::X11(_) => Vec::new(), + WindowSurface::X11(_) => {} } } - pub fn render_elements( + pub fn push_render_elements( &self, renderer: &mut R, location: Point, @@ -809,11 +808,11 @@ impl CosmicSurface { scanout_override: Option, should_clip: bool, radii: [u8; 4], - ) -> Vec - where + push_above: &mut dyn FnMut(SurfaceRenderElement), + push_below: Option<&mut dyn FnMut(SurfaceRenderElement)>, + ) where R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, - C: From>, { let mut geometry = self.0.geometry().to_f64(); geometry.loc += location.to_f64().to_logical(scale); @@ -822,7 +821,7 @@ impl CosmicSurface { WindowSurface::Wayland(toplevel) => { let surface = toplevel.wl_surface(); - render_elements_from_surface_tree( + push_render_elements_from_surface_tree( renderer, surface, location, @@ -841,14 +840,16 @@ impl CosmicSurface { .into() }) .unwrap_or(FRAME_TIME_FILTER), + push_above, + push_below, ) } WindowSurface::X11(surface) => { let Some(surface) = surface.wl_surface() else { - return Vec::new(); + return; }; - render_elements_from_surface_tree( + push_render_elements_from_surface_tree( renderer, &surface, location, @@ -867,6 +868,8 @@ impl CosmicSurface { .into() }) .unwrap_or(FRAME_TIME_FILTER), + push_above, + push_below, ) } } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index cb09f323a..6fbf65911 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -24,8 +24,8 @@ use smithay::{ renderer::{ ImportAll, ImportMem, Renderer, element::{ - AsRenderElements, Element, Id as RendererId, Kind, RenderElement, - UnderlyingStorage, memory::MemoryRenderBufferRenderElement, + Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage, + memory::MemoryRenderBufferRenderElement, }, gles::element::PixelShaderElement, glow::GlowRenderer, @@ -324,17 +324,16 @@ impl CosmicWindow { self.0.loop_handle() } - pub fn popup_render_elements( + pub fn push_popup_render_elements( &self, renderer: &mut R, location: Point, scale: Scale, alpha: f32, - ) -> Vec - where - R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + push: &mut dyn FnMut(CosmicWindowRenderElement), + ) where + R: Renderer + AsGlowRenderer + ImportAll + ImportMem, R::TextureId: Send + Clone + 'static, - C: From>, { let has_ssd = self.0.with_program(|p| p.has_ssd(false)); @@ -346,12 +345,9 @@ impl CosmicWindow { self.0.with_program(|p| { p.window - .popup_render_elements::>( - renderer, window_loc, scale, alpha, - ) - .into_iter() - .map(C::from) - .collect() + .push_popup_render_elements(renderer, window_loc, scale, alpha, &mut |elem| { + push(elem.into()) + }) }) } @@ -435,7 +431,7 @@ impl CosmicWindow { }) } - pub fn render_elements( + pub fn push_render_elements( &self, renderer: &mut R, location: Point, @@ -443,11 +439,11 @@ impl CosmicWindow { scale: Scale, alpha: f32, scanout_override: Option, - ) -> Vec - where + push_above: &mut dyn FnMut(CosmicWindowRenderElement), + push_below: &mut dyn FnMut(CosmicWindowRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - C: From>, { let (has_ssd, is_tiled, is_maximized, mut radii, appearance) = self.0.with_program(|p| { ( @@ -484,8 +480,6 @@ impl CosmicWindow { location }; - let mut elements = Vec::new(); - let (mut geo, bg_divider) = self.0.with_program(|p| { ( SpaceElement::geometry(&p.window).to_f64(), @@ -515,15 +509,15 @@ impl CosmicWindow { scale.x, [r, g, b], )); - elements.push(elem); + push_above(elem); } - if has_ssd { - radii[1] = 0; - radii[3] = 0; - } - elements.extend(self.0.with_program(|p| { - p.window.render_elements::>( + self.0.with_program(|p| { + if has_ssd { + radii[1] = 0; + radii[3] = 0; + } + p.window.push_render_elements( renderer, window_loc, scale, @@ -531,8 +525,10 @@ impl CosmicWindow { scanout_override, clip, radii, + &mut |elem| push_above(elem.into()), + Some(&mut |elem| push_below(elem.into())), ) - })); + }); if has_ssd { let ssd_loc = location @@ -540,12 +536,11 @@ impl CosmicWindow { .0 .with_program(|p| p.window.geometry().loc) .to_physical_precise_round(scale); - elements.extend(AsRenderElements::::render_elements::< - CosmicWindowRenderElement, - >(&self.0, renderer, ssd_loc, scale, alpha)) + self.0 + .push_render_elements(renderer, ssd_loc, scale, alpha, &mut |elem| { + push_above(elem.into()) + }); } - - elements.into_iter().map(C::from).collect() } pub(crate) fn set_theme(&self, theme: cosmic::Theme) { diff --git a/src/shell/grabs/menu/mod.rs b/src/shell/grabs/menu/mod.rs index 094f941c9..1292510b2 100644 --- a/src/shell/grabs/menu/mod.rs +++ b/src/shell/grabs/menu/mod.rs @@ -18,10 +18,7 @@ use cosmic::{ use smithay::{ backend::{ input::{ButtonState, TouchSlot}, - renderer::{ - ImportMem, Renderer, - element::{AsRenderElements, memory::MemoryRenderBufferRenderElement}, - }, + renderer::{ImportMem, Renderer, element::memory::MemoryRenderBufferRenderElement}, }, desktop::space::SpaceElement, input::{ @@ -65,29 +62,28 @@ pub struct MenuGrabState { pub type SeatMenuGrabState = Mutex>; impl MenuGrabState { - pub fn render(&self, renderer: &mut R, output: &Output) -> Vec - where + pub fn render( + &self, + renderer: &mut R, + output: &Output, + push: &mut dyn FnMut(MemoryRenderBufferRenderElement), + ) where R: Renderer + ImportMem, R::TextureId: Send + Clone + 'static, - I: From>, { let scale = output.current_scale().fractional_scale(); - self.elements - .lock() - .unwrap() - .iter() - .flat_map(|elem| { - elem.iced.render_elements( - renderer, - elem.position - .to_local(output) - .as_logical() - .to_physical_precise_round(scale), - scale.into(), - 1.0, - ) - }) - .collect() + for elem in self.elements.lock().unwrap().iter() { + elem.iced.push_render_elements( + renderer, + elem.position + .to_local(output) + .as_logical() + .to_physical_precise_round(scale), + scale.into(), + 1.0, + push, + ) + } } pub fn is_in_screen_space(&self) -> bool { diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index b49bdd844..81e4c38fe 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -19,12 +19,13 @@ use crate::{ use calloop::LoopHandle; use cosmic::theme::CosmicTheme; +use smallvec::SmallVec; use smithay::{ backend::{ input::ButtonState, renderer::{ ImportAll, ImportMem, Renderer, - element::{AsRenderElements, RenderElement, utils::RescaleRenderElement}, + element::{RenderElement, utils::RescaleRenderElement}, }, }, desktop::{WindowSurfaceType, layer_map_for_output, space::SpaceElement}, @@ -68,12 +69,16 @@ pub struct MoveGrabState { impl MoveGrabState { #[profiling::function] - pub fn render(&self, renderer: &mut R, output: &Output, theme: &CosmicTheme) -> Vec - where + pub fn render( + &self, + renderer: &mut R, + output: &Output, + theme: &CosmicTheme, + push: &mut dyn FnMut(CosmicMappedRenderElement), + ) where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, - I: From>, { let scale = if self.previous == ManagedLayer::Tiling { 0.6 + ((1.0 @@ -98,7 +103,7 @@ impl MoveGrabState { .intersection(window_geo) .is_none() { - return Vec::new(); + return; } let output_scale: Scale = output.current_scale().fractional_scale().into(); @@ -108,13 +113,31 @@ impl MoveGrabState { + self.window_offset - scaling_offset; + for (indicator, location) in self.stacking_indicator.iter() { + indicator.push_render_elements( + renderer, + location.to_physical_precise_round(output_scale), + output_scale, + 1.0, + &mut |elem| push(elem.into()), + ); + } + + self.window.push_popup_render_elements::( + renderer, + (render_location - self.window.geometry().loc).to_physical_precise_round(output_scale), + output_scale, + alpha, + push, + ); + let active_window_hint = crate::theme::active_window_hint(theme); let radius = self .element() .corner_radius(window_geo.size, self.indicator_thickness); - let focus_element = if self.indicator_thickness > 0 { - Some(CosmicMappedRenderElement::from( + if self.indicator_thickness > 0 { + push( IndicatorShader::focus_element( renderer, Key::Window(Usage::MoveGrabIndicator, self.window.key()), @@ -137,131 +160,104 @@ impl MoveGrabState { active_window_hint.green, active_window_hint.blue, ], - ), - )) - } else { - None - }; - - let non_exclusive_geometry = { - let layers = layer_map_for_output(output); - layers.non_exclusive_zone() - }; - - let gaps = (theme.gaps.0 as i32, theme.gaps.1 as i32); - let thickness = self.indicator_thickness.max(1); + ) + .into(), + ) + } - let snapping_indicator = match &self.snapping_zone { - Some(t) if &self.cursor_output == output => { - let base_color = theme.palette.neutral_9; - let overlay_geometry = t.overlay_geometry(non_exclusive_geometry, gaps); - vec![ - CosmicMappedRenderElement::from(IndicatorShader::element( - renderer, - Key::Window(Usage::SnappingIndicator, self.window.key()), - overlay_geometry, - thickness, - [ - theme.radius_s()[0] as u8, - theme.radius_s()[1] as u8, - theme.radius_s()[2] as u8, - theme.radius_s()[3] as u8, - ], - 1.0, - output_scale.x, - [ - active_window_hint.red, - active_window_hint.green, - active_window_hint.blue, - ], - )), - CosmicMappedRenderElement::from(BackdropShader::element( - renderer, - Key::Window(Usage::SnappingIndicator, self.window.key()), - t.overlay_geometry(non_exclusive_geometry, gaps), - theme.radius_s()[0], // TODO: Fix once shaders support 4 corner radii customization - 0.4, - [base_color.red, base_color.green, base_color.blue], - )), - ] + let map_window_element = |elem| match elem { + CosmicMappedRenderElement::Stack(stack) => { + CosmicMappedRenderElement::GrabbedStack(RescaleRenderElement::from_element( + stack, + render_location + .to_physical_precise_round(output.current_scale().fractional_scale()), + scale, + )) + } + CosmicMappedRenderElement::Window(window) => { + CosmicMappedRenderElement::GrabbedWindow(RescaleRenderElement::from_element( + window, + render_location + .to_physical_precise_round(output.current_scale().fractional_scale()), + scale, + )) } - _ => vec![], + x => x, }; - let w_elements = self - .window - .render_elements::>( - renderer, - (render_location - self.window.geometry().loc) - .to_physical_precise_round(output_scale), - None, - output_scale, - alpha, - Some(false), - ); - let p_elements = self - .window - .popup_render_elements::>( - renderer, - (render_location - self.window.geometry().loc) - .to_physical_precise_round(output_scale), - output_scale, - alpha, - ); - let shadow_element = self.window.shadow_render_element( + let mut lower_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); + self.window.push_render_elements( renderer, (render_location - self.window.geometry().loc).to_physical_precise_round(output_scale), None, output_scale, - scale, alpha, + Some(false), + &mut |elem| push(map_window_element(elem)), + &mut |elem| lower_elements.push(map_window_element(elem)), ); + if let Some(shadow_element) = self.window.shadow_render_element( + renderer, + (render_location - self.window.geometry().loc).to_physical_precise_round(output_scale), + None, + output_scale, + scale, + alpha, + ) { + push(shadow_element); + } + for elem in lower_elements.into_iter() { + push(elem); + } + + let non_exclusive_geometry = { + let layers = layer_map_for_output(output); + layers.non_exclusive_zone() + }; + + let gaps = (theme.gaps.0 as i32, theme.gaps.1 as i32); + let thickness = self.indicator_thickness.max(1); - self.stacking_indicator - .iter() - .flat_map(|(indicator, location)| { - indicator.render_elements( + if let Some(t) = &self.snapping_zone + && &self.cursor_output == output + { + let base_color = theme.palette.neutral_9; + let overlay_geometry = t.overlay_geometry(non_exclusive_geometry, gaps); + + push( + IndicatorShader::element( renderer, - location.to_physical_precise_round(output_scale), - output_scale, + Key::Window(Usage::SnappingIndicator, self.window.key()), + overlay_geometry, + thickness, + [ + theme.radius_s()[0] as u8, + theme.radius_s()[1] as u8, + theme.radius_s()[2] as u8, + theme.radius_s()[3] as u8, + ], 1.0, + output_scale.x, + [ + active_window_hint.red, + active_window_hint.green, + active_window_hint.blue, + ], ) - }) - .chain(p_elements) - .chain(focus_element) - .chain( - w_elements - .into_iter() - .chain(shadow_element) - .map(|elem| match elem { - CosmicMappedRenderElement::Stack(stack) => { - CosmicMappedRenderElement::GrabbedStack( - RescaleRenderElement::from_element( - stack, - render_location.to_physical_precise_round( - output.current_scale().fractional_scale(), - ), - scale, - ), - ) - } - CosmicMappedRenderElement::Window(window) => { - CosmicMappedRenderElement::GrabbedWindow( - RescaleRenderElement::from_element( - window, - render_location.to_physical_precise_round( - output.current_scale().fractional_scale(), - ), - scale, - ), - ) - } - x => x, - }), + .into(), + ); + push( + BackdropShader::element( + renderer, + Key::Window(Usage::SnappingIndicator, self.window.key()), + t.overlay_geometry(non_exclusive_geometry, gaps), + theme.radius_s()[0], // TODO: Fix once shaders support 4 corner radii customization + 0.4, + [base_color.red, base_color.green, base_color.blue], + ) + .into(), ) - .chain(snapping_indicator) - .map(I::from) - .collect() + } } pub fn element(&self) -> CosmicMapped { diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 882df2945..1e9cda942 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -9,9 +9,10 @@ use std::{ use cosmic_comp_config::AppearanceConfig; use cosmic_settings_config::shortcuts::action::ResizeDirection; use keyframe::{ease, functions::EaseInOutCubic}; +use smallvec::SmallVec; use smithay::{ backend::renderer::element::{ - AsRenderElements, RenderElement, + RenderElement, utils::{Relocate, RelocateRenderElement, RescaleRenderElement}, }, desktop::{PopupKind, Space, WindowSurfaceType, layer_map_for_output, space::SpaceElement}, @@ -1417,8 +1418,8 @@ impl FloatingLayout { &self, renderer: &mut R, alpha: f32, - ) -> Vec> - where + push: &mut dyn FnMut(CosmicMappedRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -1428,8 +1429,6 @@ impl FloatingLayout { let output = self.space.outputs().next().unwrap(); let output_scale = output.current_scale().fractional_scale(); - let mut elements = Vec::default(); - for elem in self .animations .iter() @@ -1444,19 +1443,16 @@ impl FloatingLayout { .unwrap_or_else(|| (self.space.element_geometry(elem).unwrap().as_local(), alpha)); let render_location = geometry.loc - elem.geometry().loc.as_local(); - elements.extend( - elem.popup_render_elements( - renderer, - render_location - .as_logical() - .to_physical_precise_round(output_scale), - output_scale.into(), - alpha, - ), + elem.push_popup_render_elements( + renderer, + render_location + .as_logical() + .to_physical_precise_round(output_scale), + output_scale.into(), + alpha, + push, ); } - - elements } #[profiling::function] @@ -1468,8 +1464,8 @@ impl FloatingLayout { indicator_thickness: u8, alpha: f32, theme: &cosmic::theme::CosmicTheme, - ) -> Vec> - where + push: &mut dyn FnMut(CosmicMappedRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -1482,8 +1478,7 @@ impl FloatingLayout { layers.non_exclusive_zone() }; let output_scale = output.current_scale().fractional_scale(); - - let mut elements = Vec::default(); + let mut lower_elements = SmallVec::<[_; 4]>::new_const(); for elem in self .animations @@ -1497,121 +1492,9 @@ impl FloatingLayout { .get(elem) .map(|anim| (*anim.previous_geometry(), alpha * anim.alpha())) .unwrap_or_else(|| (self.space.element_geometry(elem).unwrap().as_local(), alpha)); - let render_location = geometry.loc - elem.geometry().loc.as_local(); - let mut window_elements = elem.render_elements( - renderer, - render_location - .as_logical() - .to_physical_precise_round(output_scale), - None, - output_scale.into(), - alpha, - None, - ); - window_elements.extend( - elem.shadow_render_element( - renderer, - render_location - .as_logical() - .to_physical_precise_round(output_scale), - None, - output_scale.into(), - 1., - alpha, - ), - ); - - if let Some(anim) = self.animations.get(elem) { - let original_geo = anim.previous_geometry(); - geometry = anim.geometry( - output_geometry, - self.space - .element_geometry(elem) - .map(RectExt::as_local) - .unwrap_or(geometry), - elem.floating_tiled.lock().unwrap().as_ref(), - self.gaps(), - ); - - let buffer_size = elem.geometry().size; - let scale = Scale { - x: geometry.size.w as f64 / buffer_size.w as f64, - y: geometry.size.h as f64 / buffer_size.h as f64, - }; - - window_elements = window_elements - .into_iter() - .map(|element| match element { - CosmicMappedRenderElement::Stack(elem) => { - CosmicMappedRenderElement::MovingStack({ - let rescaled = RescaleRenderElement::from_element( - elem, - original_geo - .loc - .as_logical() - .to_physical_precise_round(output_scale), - scale, - ); - - RelocateRenderElement::from_element( - rescaled, - (geometry.loc - original_geo.loc) - .as_logical() - .to_physical_precise_round(output_scale), - Relocate::Relative, - ) - }) - } - CosmicMappedRenderElement::Window(elem) => { - CosmicMappedRenderElement::MovingWindow({ - let rescaled = RescaleRenderElement::from_element( - elem, - original_geo - .loc - .as_logical() - .to_physical_precise_round(output_scale), - scale, - ); - - RelocateRenderElement::from_element( - rescaled, - (geometry.loc - original_geo.loc) - .as_logical() - .to_physical_precise_round(output_scale), - Relocate::Relative, - ) - }) - } - x => x, - }) - .collect(); - } if focused == Some(elem) && !elem.is_maximized(false) { - if let Some((mode, resize)) = resize_indicator.as_mut() { - let mut resize_geometry = geometry; - resize_geometry.loc -= (18, 18).into(); - resize_geometry.size += (36, 36).into(); - - resize.resize(resize_geometry.size.as_logical()); - resize.output_enter(output, Rectangle::default() /* unused */); - window_elements = resize - .render_elements::>( - renderer, - resize_geometry - .loc - .as_logical() - .to_physical_precise_round(output_scale), - output_scale.into(), - alpha * mode.alpha().unwrap_or(1.0), - ) - .into_iter() - .map(CosmicMappedRenderElement::Window) - .chain(window_elements.into_iter()) - .collect(); - } - let active_window_hint = crate::theme::active_window_hint(theme); let radius = elem.corner_radius(geometry.size.as_logical(), indicator_thickness); if indicator_thickness > 0 { @@ -1629,14 +1512,129 @@ impl FloatingLayout { active_window_hint.blue, ], ); - window_elements.insert(0, element.into()); + push(element.into()); + } + + if let Some((mode, resize)) = resize_indicator.as_mut() { + let mut resize_geometry = geometry; + resize_geometry.loc -= (18, 18).into(); + resize_geometry.size += (36, 36).into(); + + resize.resize(resize_geometry.size.as_logical()); + resize.output_enter(output, Rectangle::default() /* unused */); + resize.push_render_elements( + renderer, + resize_geometry + .loc + .as_logical() + .to_physical_precise_round(output_scale), + output_scale.into(), + alpha * mode.alpha().unwrap_or(1.0), + &mut |elem| push(CosmicMappedRenderElement::Window(elem.into())), + ); } } - elements.extend(window_elements); - } + let maybe_map = if let Some(anim) = self.animations.get(elem) { + let original_geo = anim.previous_geometry(); + geometry = anim.geometry( + output_geometry, + self.space + .element_geometry(elem) + .map(RectExt::as_local) + .unwrap_or(geometry), + elem.floating_tiled.lock().unwrap().as_ref(), + self.gaps(), + ); + + let buffer_size = elem.geometry().size; + let scale = Scale { + x: geometry.size.w as f64 / buffer_size.w as f64, + y: geometry.size.h as f64 / buffer_size.h as f64, + }; + + Some(move |element| match element { + CosmicMappedRenderElement::Stack(elem) => { + CosmicMappedRenderElement::MovingStack({ + let rescaled = RescaleRenderElement::from_element( + elem, + original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), + scale, + ); + + RelocateRenderElement::from_element( + rescaled, + (geometry.loc - original_geo.loc) + .as_logical() + .to_physical_precise_round(output_scale), + Relocate::Relative, + ) + }) + } + CosmicMappedRenderElement::Window(elem) => { + CosmicMappedRenderElement::MovingWindow({ + let rescaled = RescaleRenderElement::from_element( + elem, + original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), + scale, + ); + + RelocateRenderElement::from_element( + rescaled, + (geometry.loc - original_geo.loc) + .as_logical() + .to_physical_precise_round(output_scale), + Relocate::Relative, + ) + }) + } + x => x, + }) + } else { + None + }; + let map_anim = |elem| { + if let Some(map) = maybe_map { + map(elem) + } else { + elem + } + }; - elements + elem.push_render_elements( + renderer, + render_location + .as_logical() + .to_physical_precise_round(output_scale), + None, + output_scale.into(), + alpha, + None, + &mut |elem| push(map_anim(elem)), + &mut |elem| lower_elements.push(map_anim(elem)), + ); + if let Some(shadow_element) = elem.shadow_render_element( + renderer, + render_location + .as_logical() + .to_physical_precise_round(output_scale), + None, + output_scale.into(), + 1., + alpha, + ) { + push(map_anim(shadow_element)); + } + for elem in lower_elements.drain(..) { + push(elem); + } + } } pub fn snap_to_corner(&self, mapped: &CosmicMapped, corners: &TiledCorners) { diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 157df87cb..066b7fe9c 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -6,8 +6,7 @@ use crate::{ element::AsGlowRenderer, }, shell::{ - CosmicSurface, Direction, FocusResult, MoveResult, OutputNotMapped, OverviewMode, - ResizeMode, Trigger, + CosmicSurface, Direction, FocusResult, MoveResult, OverviewMode, ResizeMode, Trigger, element::{ CosmicMapped, CosmicMappedRenderElement, CosmicStack, CosmicWindow, resize_indicator::ResizeIndicator, @@ -45,10 +44,11 @@ use keyframe::{ ease, functions::{EaseInOutCubic, Linear}, }; +use smallvec::SmallVec; use smithay::{ backend::renderer::{ element::{ - AsRenderElements, Id, RenderElement, + Id, RenderElement, utils::{ ConstrainAlign, ConstrainScaleBehavior, RescaleRenderElement, constrain_render_elements, @@ -3412,8 +3412,8 @@ impl TilingLayout { None, None, self.theme.cosmic(), - ) - .0; + &mut |_| {}, + ); let mut result = None; let mut lookup = Some(root.clone()); @@ -3998,8 +3998,8 @@ impl TilingLayout { resize_indicator: Option<(ResizeMode, ResizeIndicator)>, indicator_thickness: u8, theme: &cosmic::theme::CosmicTheme, - ) -> Result>, OutputNotMapped> - where + push: &mut dyn FnMut(CosmicMappedRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -4039,8 +4039,6 @@ impl TilingLayout { }; let draw_groups = overview.0.alpha(); - let mut elements = Vec::default(); - let is_overview = !matches!(overview.0, OverviewMode::None); let is_mouse_tiling = (matches!(overview.0.trigger(), Some(Trigger::Pointer(_)))) .then(|| self.last_overview_hover.as_ref().map(|(_, zone)| zone)); @@ -4052,7 +4050,7 @@ impl TilingLayout { // all gone windows and fade them out let old_geometries = if let Some(reference_tree) = reference_tree.as_ref() { - let (geometries, _) = if let Some(transition) = draw_groups { + let geometries = if let Some(transition) = draw_groups { Some(geometries_for_groupview( reference_tree, &mut *renderer, @@ -4067,14 +4065,14 @@ impl TilingLayout { swap_desc.clone(), overview.1.as_ref().and_then(|(_, tree)| *tree), theme, + &mut |_elem| {}, )) } else { None - } - .unzip(); + }; // all old windows we want to fade out - elements.extend(render_old_tree_windows( + render_old_tree_windows( reference_tree, target_tree, renderer, @@ -4084,14 +4082,16 @@ impl TilingLayout { indicator_thickness, swap_desc.is_some(), theme, - )); + push, + ); geometries } else { None }; - let (geometries, group_elements) = if let Some(transition) = draw_groups { + let mut group_elements = SmallVec::<[_; 4]>::new_const(); + let geometries = if let Some(transition) = draw_groups { Some(geometries_for_groupview( target_tree, &mut *renderer, @@ -4105,14 +4105,14 @@ impl TilingLayout { swap_desc.clone(), overview.1.as_ref().and_then(|(_, tree)| *tree), theme, + &mut |elem| group_elements.push(elem), )) } else { None - } - .unzip(); + }; // all alive windows - elements.extend(render_new_tree_windows( + render_new_tree_windows( target_tree, reference_tree, renderer, @@ -4140,14 +4140,13 @@ impl TilingLayout { &self.swapping_stack_surface_id, &self.backdrop_id, theme, - )); + push, + ); // tiling hints - if let Some(group_elements) = group_elements { - elements.extend(group_elements); + for elem in group_elements.into_iter() { + push(elem); } - - Ok(elements) } #[profiling::function] @@ -4158,8 +4157,8 @@ impl TilingLayout { non_exclusive_zone: Rectangle, overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), theme: &cosmic::theme::CosmicTheme, - ) -> Result>, OutputNotMapped> - where + push: &mut dyn FnMut(CosmicMappedRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -4191,8 +4190,6 @@ impl TilingLayout { }; let draw_groups = overview.0.alpha(); - let mut elements = Vec::default(); - let is_mouse_tiling = (matches!(overview.0.trigger(), Some(Trigger::Pointer(_)))) .then(|| self.last_overview_hover.as_ref().map(|(_, zone)| zone)); let swap_desc = if let Some(Trigger::KeyboardSwap(_, desc)) = overview.0.trigger() { @@ -4203,7 +4200,7 @@ impl TilingLayout { // all gone windows and fade them out let old_geometries = if let Some(reference_tree) = reference_tree.as_ref() { - let (geometries, _) = if let Some(transition) = draw_groups { + let geometries = if let Some(transition) = draw_groups { Some(geometries_for_groupview( reference_tree, &mut *renderer, @@ -4218,14 +4215,14 @@ impl TilingLayout { swap_desc.clone(), overview.1.as_ref().and_then(|(_, tree)| *tree), theme, + &mut |_| {}, )) } else { None - } - .unzip(); + }; // all old windows we want to fade out - elements.extend(render_old_tree_popups( + render_old_tree_popups( reference_tree, target_tree, renderer, @@ -4233,14 +4230,15 @@ impl TilingLayout { output_scale, percentage, swap_desc.is_some(), - )); + push, + ); geometries } else { None }; - let (geometries, _) = if let Some(transition) = draw_groups { + let geometries = if let Some(transition) = draw_groups { Some(geometries_for_groupview( target_tree, &mut *renderer, @@ -4254,14 +4252,14 @@ impl TilingLayout { swap_desc.clone(), overview.1.as_ref().and_then(|(_, tree)| *tree), theme, + &mut |_| {}, )) } else { None - } - .unzip(); + }; // all alive windows - elements.extend(render_new_tree_popups( + render_new_tree_popups( target_tree, reference_tree, renderer, @@ -4272,9 +4270,8 @@ impl TilingLayout { percentage, overview, swap_desc.clone(), - )); - - Ok(elements) + push, + ); } fn gaps(&self) -> (i32, i32) { @@ -4329,10 +4326,8 @@ fn geometries_for_groupview<'a, R>( swap_desc: Option, swap_tree: Option<&Tree>, _theme: &cosmic::theme::CosmicTheme, -) -> ( - HashMap>, - Vec>, -) + push: &mut dyn FnMut(CosmicMappedRenderElement), +) -> HashMap> where R: AsGlowRenderer + 'a, R::TextureId: 'static, @@ -4355,11 +4350,18 @@ where // push bogos value, that will get ignored anyway stack.push((Rectangle::from_size((320, 240).into()), 0)); } - if root.is_some() { + + let has_root = root.is_some(); + if has_root { stack.push((non_exclusive_zone, 0)); } - let mut elements = Vec::new(); + let mut push = |elem| { + if has_root { + push(elem) + } + }; + let mut geometries: HashMap> = HashMap::new(); let alpha = alpha * transition; @@ -4514,7 +4516,7 @@ where if let Some(renderer) = renderer.as_mut() { if (render_potential_group || render_active_child) && Some(&node_id) != root { - elements.push( + push( IndicatorShader::element( *renderer, Key::Group(Arc::downgrade(alive)), @@ -4532,7 +4534,7 @@ where && pill_indicator.is_some() && Some(&node_id) != root { - elements.push( + push( IndicatorShader::element( *renderer, Key::Group(Arc::downgrade(alive)), @@ -4596,7 +4598,7 @@ where }; if draw_outline { - elements.push( + push( IndicatorShader::element( *renderer, Key::Group(Arc::downgrade(alive)), @@ -4657,7 +4659,7 @@ where }; if let Some(renderer) = renderer.as_mut() { - elements.push( + push( BackdropShader::element( *renderer, backdrop_id.clone(), @@ -4675,7 +4677,7 @@ where if matches!(swap_desc, Some(ref desc) if desc.node == node_id) { if let Some(renderer) = renderer.as_mut() { - elements.push( + push( BackdropShader::element( *renderer, Key::Group(Arc::downgrade(alive)), @@ -4760,7 +4762,7 @@ where .unwrap_or(false) { if let Some(renderer) = renderer.as_mut() { - elements.push( + push( BackdropShader::element( *renderer, backdrop_id.clone(), @@ -4802,7 +4804,7 @@ where .unwrap_or(false) { if let Some(renderer) = renderer.as_mut() { - elements.push( + push( BackdropShader::element( *renderer, backdrop_id.clone(), @@ -4833,7 +4835,7 @@ where if let Some(renderer) = renderer.as_mut() { if render_potential_group { - elements.push( + push( IndicatorShader::element( *renderer, Key::Window(Usage::PotentialGroupIndicator, mapped.key()), @@ -4873,7 +4875,7 @@ where geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into(); geo.size -= (WINDOW_BACKDROP_BORDER * 2, WINDOW_BACKDROP_BORDER * 2).into(); - elements.push( + push( BackdropShader::element( *renderer, Key::Window(Usage::OverviewBackdrop, mapped.key()), @@ -4930,7 +4932,7 @@ where if let Some(renderer) = renderer.as_mut() { geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into(); geo.size -= (WINDOW_BACKDROP_BORDER * 2, WINDOW_BACKDROP_BORDER * 2).into(); - elements.push( + push( BackdropShader::element( *renderer, id.clone(), @@ -4949,11 +4951,7 @@ where } } - if root.is_none() { - elements.clear(); - } - - (geometries, elements) + geometries } fn render_old_tree_popups( @@ -4964,16 +4962,14 @@ fn render_old_tree_popups( output_scale: f64, percentage: f32, is_swap_mode: bool, -) -> Vec> -where + push: &mut dyn FnMut(CosmicMappedRenderElement), +) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, CosmicStackRenderElement: RenderElement, { - let mut elements = Vec::default(); - render_old_tree( reference_tree, target_tree, @@ -4982,19 +4978,15 @@ where percentage, is_swap_mode, |mapped, elem_geometry, geo, alpha, _| { - elements.extend( - mapped.popup_render_elements::>( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale) - - elem_geometry.loc, - Scale::from(output_scale), - alpha, - ), - ); + mapped.push_popup_render_elements( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, + Scale::from(output_scale), + alpha, + push, + ) }, - ); - - elements + ) } fn render_old_tree_windows( @@ -5007,8 +4999,8 @@ fn render_old_tree_windows( indicator_thickness: u8, is_swap_mode: bool, theme: &cosmic::theme::CosmicTheme, -) -> Vec> -where + push: &mut dyn FnMut(CosmicMappedRenderElement), +) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -5016,8 +5008,35 @@ where CosmicStackRenderElement: RenderElement, { let window_hint = crate::theme::active_window_hint(theme); - let mut elements = Vec::default(); - let mut shadow_elements = Vec::default(); + let mut lower_elements = Vec::default(); + let mut shadow_elements = SmallVec::<[_; 4]>::new_const(); + + let window_map = + |elem, geo: Rectangle, elem_geometry: Rectangle| match elem { + CosmicMappedRenderElement::Stack(elem) => constrain_render_elements( + std::iter::once(elem), + geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, + geo.as_logical().to_physical_precise_round(output_scale), + elem_geometry, + ConstrainScaleBehavior::Stretch, + ConstrainAlign::CENTER, + output_scale, + ) + .next() + .map(CosmicMappedRenderElement::TiledStack), + CosmicMappedRenderElement::Window(elem) => constrain_render_elements( + std::iter::once(elem), + geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, + geo.as_logical().to_physical_precise_round(output_scale), + elem_geometry, + ConstrainScaleBehavior::Stretch, + ConstrainAlign::CENTER, + output_scale, + ) + .next() + .map(CosmicMappedRenderElement::TiledWindow), + x => Some(x), + }; render_old_tree( reference_tree, @@ -5027,56 +5046,9 @@ where percentage, is_swap_mode, |mapped, elem_geometry, geo, alpha, is_minimizing| { - shadow_elements.extend(mapped.shadow_render_element( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, - Some(geo.size.as_logical()), - Scale::from(output_scale), - 1., - alpha, - )); - - let window_elements = mapped.render_elements::>( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, - Some(geo.size.as_logical()), - Scale::from(output_scale), - alpha, - None, - ); - - elements.extend(window_elements.into_iter().flat_map(|element| { - match element { - CosmicMappedRenderElement::Stack(elem) => constrain_render_elements( - std::iter::once(elem), - geo.loc.as_logical().to_physical_precise_round(output_scale) - - elem_geometry.loc, - geo.as_logical().to_physical_precise_round(output_scale), - elem_geometry, - ConstrainScaleBehavior::Stretch, - ConstrainAlign::CENTER, - output_scale, - ) - .next() - .map(CosmicMappedRenderElement::TiledStack), - CosmicMappedRenderElement::Window(elem) => constrain_render_elements( - std::iter::once(elem), - geo.loc.as_logical().to_physical_precise_round(output_scale) - - elem_geometry.loc, - geo.as_logical().to_physical_precise_round(output_scale), - elem_geometry, - ConstrainScaleBehavior::Stretch, - ConstrainAlign::CENTER, - output_scale, - ) - .next() - .map(CosmicMappedRenderElement::TiledWindow), - x => Some(x), - } - })); let radius = mapped.corner_radius(geo.size.as_logical(), indicator_thickness); if is_minimizing && indicator_thickness > 0 { - elements.push(CosmicMappedRenderElement::FocusIndicator( + push(CosmicMappedRenderElement::FocusIndicator( IndicatorShader::focus_element( renderer, Key::Window(Usage::FocusIndicator, mapped.clone().key()), @@ -5089,10 +5061,43 @@ where ), )); } + + mapped.push_render_elements( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, + Some(geo.size.as_logical()), + Scale::from(output_scale), + alpha, + None, + &mut |elem| { + if let Some(elem) = window_map(elem, geo, elem_geometry) { + push(elem); + } + }, + &mut |elem| { + if let Some(elem) = window_map(elem, geo, elem_geometry) { + lower_elements.push(elem); + } + }, + ); + + shadow_elements.extend(mapped.shadow_render_element( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, + Some(geo.size.as_logical()), + Scale::from(output_scale), + 1., + alpha, + )); }, ); - shadow_elements.into_iter().chain(elements).collect() + for elem in shadow_elements { + push(elem); + } + for elem in lower_elements { + push(elem); + } } fn render_old_tree( @@ -5187,15 +5192,14 @@ fn render_new_tree_popups( percentage: f32, overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), swap_desc: Option, -) -> Vec> -where + push: &mut dyn FnMut(CosmicMappedRenderElement), +) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, CosmicStackRenderElement: RenderElement, { - let mut popup_elements = Vec::new(); let output_scale = output.current_scale().fractional_scale(); let is_active_output = seat @@ -5218,20 +5222,17 @@ where if let Data::Mapped { mapped, .. } = data { let elem_geometry = mapped.geometry().to_physical_precise_round(output_scale); - popup_elements.extend( - mapped.popup_render_elements::>( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale) - - elem_geometry.loc, - Scale::from(output_scale), - alpha, - ), + mapped.push_popup_render_elements( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) + - elem_geometry.loc, + Scale::from(output_scale), + alpha, + push, ); } }, ); - - popup_elements } fn render_new_tree_windows( @@ -5253,8 +5254,8 @@ fn render_new_tree_windows( swapping_stack_surface_id: &Id, backdrop_id: &Id, theme: &cosmic::theme::CosmicTheme, -) -> Vec> -where + push: &mut dyn FnMut(CosmicMappedRenderElement), +) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -5294,14 +5295,18 @@ where .map(|seat| &seat.active_output() == output) .unwrap_or(false); - let mut animating_window_elements = Vec::new(); - let mut window_elements = Vec::new(); + let mut animating_window_upper_elements = Vec::new(); + let mut animating_window_lower_elements = Vec::new(); + let mut animating_shadow_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); + + let mut window_upper_elements = Vec::new(); + let mut window_lower_elements = Vec::new(); + let mut shadow_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); let mut group_backdrop = None; - let mut indicators = Vec::new(); - let mut resize_elements = None; - let mut swap_elements = Vec::new(); - let mut shadow_elements = Vec::new(); + let mut indicators = SmallVec::<[CosmicMappedRenderElement; 2]>::new_const(); + let mut resize_element = None; + let mut swap_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); let output_geo = output.geometry(); let output_scale = output.current_scale().fractional_scale(); @@ -5314,7 +5319,7 @@ where // render placeholder, if we are swapping to an empty workspace if target_tree.root_node_id().is_none() && swap_desc.is_some() { - window_elements.push( + window_upper_elements.push( BackdropShader::element( renderer, backdrop_id.clone(), @@ -5369,21 +5374,18 @@ where let render_loc = (swap_geo.loc.as_logical() - window_geo.loc).to_physical_precise_round(output_scale); - swap_elements.extend( - window - .render_elements( - renderer, - render_loc, - output_scale.into(), - 1.0, - None, - false, - [0; 4], - ) - .into_iter() - .map(|window| { - CosmicMappedRenderElement::GrabbedWindow(RescaleRenderElement::from_element( - window, + window.push_render_elements( + renderer, + render_loc, + output_scale.into(), + 1.0, + None, + false, + [0, 0, 0, 0], + &mut |elem| { + swap_elements.push(CosmicMappedRenderElement::GrabbedWindow( + RescaleRenderElement::from_element( + elem.into(), swap_geo .loc .as_logical() @@ -5394,9 +5396,11 @@ where swap_factor(window_geo.size), transition.unwrap_or(1.0), ), - )) - }), - ) + ), + )); + }, + None, + ); } // render actual tree nodes @@ -5512,15 +5516,12 @@ where { swap.resize(geo.size.as_logical()); swap.output_enter(output, output_geo.as_logical()); - swap_elements.extend( - swap.render_elements::>( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale), - output_scale.into(), - alpha * overview.0.alpha().unwrap_or(1.0), - ) - .into_iter() - .map(CosmicMappedRenderElement::from), + swap.push_render_elements( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale), + output_scale.into(), + alpha * overview.0.alpha().unwrap_or(1.0), + &mut |elem| swap_elements.push(elem.into()), ); } } @@ -5546,17 +5547,14 @@ where }) { resize.force_update(); } - resize_elements = Some( - resize - .render_elements::>( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale), - output_scale.into(), - alpha * mode.alpha().unwrap_or(1.0), - ) - .into_iter() - .map(CosmicMappedRenderElement::from) - .collect::>(), + resize.push_render_elements( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale), + output_scale.into(), + alpha * mode.alpha().unwrap_or(1.0), + &mut |elem| { + resize_element = Some(elem.into()); + }, ); } } @@ -5582,51 +5580,6 @@ where scale.x.min(scale.y), alpha, ); - let mut elements = mapped.render_elements::>( - renderer, - //original_location, - geo.loc.as_logical().to_physical_precise_round(output_scale) - - elem_geometry.loc, - max_size, - Scale::from(output_scale), - alpha, - None, - ); - - if swap_desc - .as_ref() - .filter(|swap_desc| swap_desc.node == node_id) - .and_then(|swap_desc| swap_desc.stack_window.as_ref()) - .zip(focused.as_ref()) - .map(|(stack_window, focused_id)| { - target_tree - .get(focused_id) - .ok() - .map(|focused| match focused.data() { - Data::Mapped { mapped, .. } => mapped - .stack_ref() - .map(|stack| &stack.active() == stack_window) - .unwrap_or(false), - _ => false, - }) - .unwrap_or(false) - }) - .unwrap_or(false) - { - let mut active_geo = mapped.active_window_geometry().as_local(); - active_geo.loc += geo.loc - mapped.geometry().loc.as_local(); - elements.insert( - 0, - CosmicMappedRenderElement::Overlay(BackdropShader::element( - renderer, - Key::Window(Usage::Overlay, mapped.key()), - active_geo, - 0.0, - 0.3, - group_color, - )), - ) - } let (behavior, align) = if is_overview { (ConstrainScaleBehavior::Fit, ConstrainAlign::CENTER) @@ -5636,7 +5589,7 @@ where (ConstrainScaleBehavior::CutOff, ConstrainAlign::TOP_LEFT) }; - let elements = elements.into_iter().flat_map(|element| match element { + let map_elem = |element| match element { CosmicMappedRenderElement::Stack(elem) => constrain_render_elements( std::iter::once(elem), geo.loc.as_logical().to_physical_precise_round(output_scale) @@ -5674,7 +5627,65 @@ where .next() .map(CosmicMappedRenderElement::TiledOverlay), x => Some(x), - }); + }; + + let mut upper_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); + let mut lower_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); + mapped.push_render_elements( + renderer, + //original_location, + geo.loc.as_logical().to_physical_precise_round(output_scale) + - elem_geometry.loc, + max_size, + Scale::from(output_scale), + alpha, + None, + &mut |elem| { + if let Some(elem) = map_elem(elem) { + upper_elements.push(elem) + } + }, + &mut |elem| { + if let Some(elem) = map_elem(elem) { + lower_elements.push(elem) + } + }, + ); + + if swap_desc + .as_ref() + .filter(|swap_desc| swap_desc.node == node_id) + .and_then(|swap_desc| swap_desc.stack_window.as_ref()) + .zip(focused.as_ref()) + .map(|(stack_window, focused_id)| { + target_tree + .get(focused_id) + .ok() + .map(|focused| match focused.data() { + Data::Mapped { mapped, .. } => mapped + .stack_ref() + .map(|stack| &stack.active() == stack_window) + .unwrap_or(false), + _ => false, + }) + .unwrap_or(false) + }) + .unwrap_or(false) + { + let mut active_geo = mapped.active_window_geometry().as_local(); + active_geo.loc += geo.loc - mapped.geometry().loc.as_local(); + upper_elements.insert( + 0, + CosmicMappedRenderElement::Overlay(BackdropShader::element( + renderer, + Key::Window(Usage::Overlay, mapped.key()), + active_geo, + 0.0, + 0.3, + group_color, + )), + ) + } if swap_desc .as_ref() @@ -5687,30 +5698,36 @@ where }) .unwrap_or(false) { + swap_elements.extend(upper_elements); swap_elements.extend(shadow_element); - swap_elements.extend(elements); + swap_elements.extend(lower_elements); + } else if animating { + animating_window_upper_elements.extend(upper_elements); + animating_shadow_elements.extend(shadow_element); + animating_window_lower_elements.extend(lower_elements); } else { + window_upper_elements.extend(upper_elements); shadow_elements.extend(shadow_element); - if animating { - animating_window_elements.extend(elements); - } else { - window_elements.extend(elements); - } + window_lower_elements.extend(lower_elements); } } }, ); - resize_elements + for elem in resize_element .into_iter() - .flatten() .chain(swap_elements) .chain(indicators) - .chain(window_elements) - .chain(animating_window_elements) + .chain(window_upper_elements) .chain(shadow_elements) + .chain(window_lower_elements) + .chain(animating_window_upper_elements) + .chain(animating_shadow_elements) + .chain(animating_window_lower_elements) .chain(group_backdrop.into_iter().map(Into::into)) - .collect() + { + push(elem); + } } fn render_new_tree( diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index ff4bc2b8d..248500b7c 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1,3 +1,4 @@ +use crate::backend::render::wayland::SurfaceRenderElement; use crate::shell::focus::FocusTarget; use crate::shell::layout::tiling::RestoreTilingState; use crate::wayland::handlers::xdg_activation::ActivationContext; @@ -28,14 +29,14 @@ use cosmic_protocols::workspace::v2::server::zcosmic_workspace_handle_v2::Tiling use id_tree::Tree; use indexmap::IndexSet; use keyframe::{ease, functions::EaseInOutCubic}; +use smallvec::SmallVec; use smithay::backend::renderer::element::Kind; use smithay::output::WeakOutput; use smithay::utils::user_data::UserDataMap; use smithay::{ backend::renderer::{ element::{ - Element, Id, RenderElement, surface::WaylandSurfaceRenderElement, - texture::TextureRenderElement, utils::RescaleRenderElement, + Element, Id, RenderElement, texture::TextureRenderElement, utils::RescaleRenderElement, }, gles::GlesTexture, glow::GlowRenderer, @@ -1534,8 +1535,8 @@ impl Workspace { resize_indicator: Option<(ResizeMode, ResizeIndicator)>, indicator_thickness: u8, theme: &CosmicTheme, - ) -> Result>, OutputNotMapped> - where + push: &mut dyn FnMut(WorkspaceRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -1543,16 +1544,16 @@ impl Workspace { CosmicStackRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { - let mut elements = Vec::default(); - let output_scale = self.output.current_scale().fractional_scale(); let zone = { let layer_map = layer_map_for_output(&self.output); layer_map.non_exclusive_zone().as_local() }; let focused = self.focus_stack.get(last_active_seat).last().cloned(); + let fullscreen_focused = matches!(focused, Some(FocusTarget::Fullscreen(_))); - let mut fullscreen_elements = if let Some(fullscreen) = self.fullscreen.as_ref() { + let mut fullscreen_elements = SmallVec::<[WorkspaceRenderElement; 2]>::new_const(); + if let Some(fullscreen) = self.fullscreen.as_ref() { let fullscreen_geo = self.fullscreen_geometry().unwrap(); let previous_geo = fullscreen .previous_geometry @@ -1611,29 +1612,27 @@ impl Workspace { } }; - fullscreen - .surface - .render_elements::>( - renderer, - render_loc, - output_scale.into(), - alpha, - Some(true), - false, - [0; 4], - ) - .into_iter() - .map(animation_rescale) - .collect::>() - } else { - Vec::new() - }; - - if matches!(focused, Some(FocusTarget::Fullscreen(_))) { - elements.append(&mut fullscreen_elements); + let mut fullscreen_push = |elem: SurfaceRenderElement| { + if fullscreen_focused { + push(animation_rescale(elem.into())) + } else { + fullscreen_elements.push(animation_rescale(elem.into())) + } + }; + fullscreen.surface.push_render_elements( + renderer, + render_loc, + output_scale.into(), + alpha, + Some(true), + false, + [0, 0, 0, 0], + &mut fullscreen_push, + None, + ); } - if !matches!(focused, Some(FocusTarget::Fullscreen(_))) + if !fullscreen_focused || self .fullscreen .as_ref() @@ -1659,24 +1658,20 @@ impl Workspace { OverviewMode::None => 1.0, }; - elements.extend( - self.floating_layer - .render::( - renderer, - focused.as_ref().and_then(|target| { - if let FocusTarget::Window(mapped) = target { - Some(mapped) - } else { - None - } - }), - resize_indicator.clone(), - indicator_thickness, - alpha, - theme, - ) - .into_iter() - .map(WorkspaceRenderElement::from), + self.floating_layer.render( + renderer, + focused.as_ref().and_then(|target| { + if let FocusTarget::Window(mapped) = target { + Some(mapped) + } else { + None + } + }), + resize_indicator.clone(), + indicator_thickness, + alpha, + theme, + &mut |elem| push(elem.into()), ); let alpha = match &overview.0 { @@ -1693,23 +1688,19 @@ impl Workspace { }; //tiling surfaces - elements.extend( - self.tiling_layer - .render::( - renderer, - render_focus.then_some(last_active_seat), - zone, - overview, - resize_indicator, - indicator_thickness, - theme, - )? - .into_iter() - .map(WorkspaceRenderElement::from), + self.tiling_layer.render( + renderer, + render_focus.then_some(last_active_seat), + zone, + overview, + resize_indicator, + indicator_thickness, + theme, + &mut |elem| push(elem.into()), ); if let Some(alpha) = alpha { - elements.push( + push( Into::>::into(BackdropShader::element( renderer, self.backdrop_id.clone(), @@ -1723,11 +1714,9 @@ impl Workspace { } } - if !matches!(focused, Some(FocusTarget::Fullscreen(_))) { - elements.extend(fullscreen_elements.into_iter()); + for elem in fullscreen_elements { + push(elem); } - - Ok(elements) } #[profiling::function] @@ -1738,8 +1727,8 @@ impl Workspace { render_focus: bool, overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), theme: &CosmicTheme, - ) -> Result>, OutputNotMapped> - where + push: &mut dyn FnMut(WorkspaceRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -1747,8 +1736,6 @@ impl Workspace { CosmicStackRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { - let mut elements = Vec::default(); - let output_scale = self.output.current_scale().fractional_scale(); let zone = { let layer_map = layer_map_for_output(&self.output); @@ -1799,17 +1786,12 @@ impl Workspace { .as_logical() .to_physical_precise_round(output_scale); - elements.extend( - fullscreen - .surface - .popup_render_elements::>( - renderer, - render_loc, - output_scale.into(), - alpha, - ) - .into_iter() - .map(Into::into), + fullscreen.surface.push_popup_render_elements( + renderer, + render_loc, + output_scale.into(), + alpha, + &mut |elem| push(WorkspaceRenderElement::FullscreenPopup(elem.into())), ); } @@ -1840,29 +1822,19 @@ impl Workspace { OverviewMode::None => 1.0, }; - elements.extend( - self.floating_layer - .render_popups::(renderer, alpha) - .into_iter() - .map(WorkspaceRenderElement::from), - ); + self.floating_layer + .render_popups(renderer, alpha, &mut |elem| push(elem.into())); //tiling surfaces - elements.extend( - self.tiling_layer - .render_popups::( - renderer, - render_focus.then_some(last_active_seat), - zone, - overview, - theme, - )? - .into_iter() - .map(WorkspaceRenderElement::from), + self.tiling_layer.render_popups( + renderer, + render_focus.then_some(last_active_seat), + zone, + overview, + theme, + &mut |elem| push(elem.into()), ); } - - Ok(elements) } } @@ -1883,7 +1855,7 @@ where R: AsGlowRenderer, R::TextureId: 'static, { - OverrideRedirect(WaylandSurfaceRenderElement), + OverrideRedirect(SurfaceRenderElement), Fullscreen(RescaleRenderElement>), FullscreenPopup(CosmicWindowRenderElement), Window(CosmicMappedRenderElement), @@ -2121,13 +2093,13 @@ where } } -impl From> for WorkspaceRenderElement +impl From> for WorkspaceRenderElement where R: AsGlowRenderer, R::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { - fn from(elem: WaylandSurfaceRenderElement) -> Self { + fn from(elem: SurfaceRenderElement) -> Self { WorkspaceRenderElement::OverrideRedirect(elem) } } diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index bb6815632..235c12839 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -11,7 +11,7 @@ use cosmic_comp_config::ZoomMovement; use cosmic_config::ConfigSet; use keyframe::{ease, functions::EaseInOutCubic}; use smithay::{ - backend::renderer::{ImportMem, Renderer, element::AsRenderElements}, + backend::renderer::{ImportMem, Renderer, element::memory::MemoryRenderBufferRenderElement}, desktop::space::SpaceElement, input::{ Seat, @@ -192,9 +192,12 @@ impl OutputZoomState { }); } - fn render(&mut self, renderer: &mut R, output: &Output) -> Vec - where - C: From< as AsRenderElements>::RenderElement>, + fn render( + &mut self, + renderer: &mut R, + output: &Output, + push: &mut dyn FnMut(MemoryRenderBufferRenderElement), + ) where R: Renderer + ImportMem, R::TextureId: Send + Clone + 'static, { @@ -208,8 +211,13 @@ impl OutputZoomState { .to_physical(scale.fractional_scale()) .to_i32_round(); - self.element - .render_elements(renderer, location, scale.fractional_scale().into(), 1.0) + self.element.push_render_elements( + renderer, + location, + scale.fractional_scale().into(), + 1.0, + push, + ) } } @@ -389,14 +397,16 @@ impl ZoomState { None } - pub fn render(renderer: &mut R, output: &Output) -> Vec - where - C: From< as AsRenderElements>::RenderElement>, + pub fn render( + renderer: &mut R, + output: &Output, + push: &mut dyn FnMut(MemoryRenderBufferRenderElement), + ) where R: Renderer + ImportMem, R::TextureId: Send + Clone + 'static, { let output_state = output.user_data().get::>().unwrap(); - output_state.lock().unwrap().render(renderer, output) + output_state.lock().unwrap().render(renderer, output, push) } } diff --git a/src/utils/iced/mod.rs b/src/utils/iced/mod.rs index 186abf04e..c6fca7743 100644 --- a/src/utils/iced/mod.rs +++ b/src/utils/iced/mod.rs @@ -35,7 +35,7 @@ use smithay::{ renderer::{ ImportMem, Renderer, element::{ - AsRenderElements, Kind, + Kind, memory::{MemoryRenderBuffer, MemoryRenderBufferRenderElement}, }, }, @@ -895,21 +895,18 @@ impl SpaceElement for IcedElement

{ } } -impl AsRenderElements for IcedElement

-where - P: Program + Send + 'static, - R: Renderer + ImportMem, - R::TextureId: Send + Clone + 'static, -{ - type RenderElement = MemoryRenderBufferRenderElement; - - fn render_elements>( +impl IcedElement

{ + pub fn push_render_elements( &self, renderer: &mut R, location: Point, mut scale: Scale, alpha: f32, - ) -> Vec { + push: &mut dyn FnMut(MemoryRenderBufferRenderElement), + ) where + R: Renderer + ImportMem, + R::TextureId: Send + Clone + 'static, + { let mut internal = self.0.lock().unwrap(); // makes partial borrows easier let internal_ref = &mut *internal; @@ -1040,11 +1037,10 @@ where Kind::Unspecified, ) { Ok(buffer) => { - return vec![C::from(buffer)]; + push(buffer); } Err(err) => tracing::warn!("What? {:?}", err), } } - Vec::new() } } diff --git a/src/utils/screenshot.rs b/src/utils/screenshot.rs index 149227870..399c5720f 100644 --- a/src/utils/screenshot.rs +++ b/src/utils/screenshot.rs @@ -14,7 +14,7 @@ use smithay::{ use tracing::warn; use crate::{ - backend::render::{RendererRef, element::AsGlowRenderer, wayland::SurfaceRenderElement}, + backend::render::{RendererRef, element::AsGlowRenderer}, shell::element::CosmicSurface, state::{State, advertised_node_for_surface}, }; @@ -27,7 +27,8 @@ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) { R::Error: Send + Sync + 'static, { let bbox = bbox_from_surface_tree(&window.wl_surface().unwrap(), (0, 0)); - let elements = window.render_elements::>( + let mut elements = Vec::new(); + window.push_render_elements( renderer, (-bbox.loc.x, -bbox.loc.y).into(), Scale::from(1.0), @@ -35,6 +36,8 @@ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) { None, false, [0; 4], + &mut |elem| elements.push(elem), + None, ); // TODO: 10-bit diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index e7bb36ba2..163b00bd2 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only use calloop::LoopHandle; +use smallvec::SmallVec; use smithay::{ backend::{ allocator::{Buffer, Fourcc, format::get_transparent}, @@ -39,7 +40,8 @@ use tracing::warn; use crate::{ backend::render::{ - CursorMode, ElementFilter, RendererRef, cursor, + CursorMode, ElementFilter, RendererRef, + cursor::{self, CursorRenderElement}, element::{AsGlowRenderer, CosmicElement, DamageElement}, render_workspace, wayland::SurfaceRenderElement, @@ -591,44 +593,49 @@ pub fn render_window_to_buffer( if let Some(location) = location { if draw_cursor { - elements.extend( - cursor::draw_cursor( - renderer, - &seat, - location, - 1.0.into(), - 1.0, - common.clock.now(), - true, - ) - .into_iter() - .map(|(elem, hotspot)| { - WindowCaptureElement::CursorElement(RelocateRenderElement::from_element( - elem, - Point::from((-hotspot.x, -hotspot.y)), - Relocate::Relative, - )) - }), + cursor::draw_cursor( + renderer, + &seat, + location, + 1.0.into(), + 1.0, + common.clock.now(), + true, + &mut |elem, hotspot| { + elements.push(WindowCaptureElement::CursorElement( + RelocateRenderElement::from_element( + elem, + Point::from((-hotspot.x, -hotspot.y)), + Relocate::Relative, + ), + )); + }, ); } // TODO cosmic-workspaces wants to omit, but metadata cursor capture in portal should // still include dnd surface in window capture buffer? if draw_cursor && let Some(dnd_icon) = get_dnd_icon(&seat) { - elements.extend( - cursor::draw_dnd_icon( - renderer, - &dnd_icon.surface, - (location + dnd_icon.offset.to_f64()).to_i32_round(), - 1.0, - ) - .into_iter() - .map(WindowCaptureElement::from), + cursor::draw_dnd_icon( + renderer, + &dnd_icon.surface, + (location + dnd_icon.offset.to_f64()).to_i32_round(), + 1.0, + &mut |elem| { + elements.push( + RelocateRenderElement::from_element( + CursorRenderElement::Surface(elem), + Point::new(0, 0), + Relocate::Relative, + ) + .into(), + ) + }, ); } } - elements.extend(toplevel.render_elements::>( + toplevel.push_render_elements( renderer, (-geometry.loc.x, -geometry.loc.y).into(), Scale::from(1.0), @@ -636,7 +643,9 @@ pub fn render_window_to_buffer( None, false, [0; 4], - )); + &mut |elem| elements.push(elem.into()), + None, + ); if let Ok(dmabuf) = get_dmabuf(buffer) { let mut dmabuf_clone = dmabuf.clone(); @@ -798,7 +807,8 @@ pub fn render_cursor_to_buffer( .collect(); dt.damage_output(age, &additional_damage_elements)?; - let elements = cursor::draw_cursor( + let mut elements = SmallVec::<[WindowCaptureElement; 4]>::new_const(); + cursor::draw_cursor( renderer, seat, Point::from((0.0, 0.0)), @@ -806,11 +816,12 @@ pub fn render_cursor_to_buffer( 1.0, common.clock.now(), true, - ) - .into_iter() - .map(|(elem, _)| RelocateRenderElement::from_element(elem, (0, 0), Relocate::Relative)) - .map(WindowCaptureElement::from) - .collect::>(); + &mut |elem, _| { + elements.push( + RelocateRenderElement::from_element(elem, (0, 0), Relocate::Relative).into(), + ) + }, + ); if let Ok(dmabuf) = get_dmabuf(buffer) { let mut dmabuf_clone = dmabuf.clone(); From 81560eb5bed4bbcf03dd3a18219457f1af086411 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 4 Mar 2026 19:02:29 +0100 Subject: [PATCH 06/16] element/wayland: Add blur background-effect --- src/backend/render/mod.rs | 4 + .../render/shaders/blur_downsample.frag | 26 + src/backend/render/shaders/blur_upsample.frag | 27 + .../render/shaders/clipped_surface.frag | 30 +- src/backend/render/wayland/blur_effect.rs | 578 ++++++++++++++++++ src/backend/render/wayland/clipped_surface.rs | 1 + src/backend/render/wayland/mod.rs | 19 +- src/state.rs | 5 + src/wayland/handlers/background_effect.rs | 62 ++ src/wayland/handlers/mod.rs | 1 + 10 files changed, 749 insertions(+), 4 deletions(-) create mode 100644 src/backend/render/shaders/blur_downsample.frag create mode 100644 src/backend/render/shaders/blur_upsample.frag create mode 100644 src/backend/render/wayland/blur_effect.rs create mode 100644 src/wayland/handlers/background_effect.rs diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 395431331..6f186af15 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -19,6 +19,7 @@ use crate::{ shadow::{SHADOW_SHADER, ShadowShader}, wayland::{ SurfaceRenderElement, + blur_effect::BlurShaders, clipped_surface::{CLIPPING_SHADER, ClippingShader}, push_render_elements_from_surface_tree, }, @@ -425,6 +426,7 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> { UniformName::new("geo_size", UniformType::_2f), UniformName::new("corner_radius", UniformType::_4f), UniformName::new("input_to_geo", UniformType::Matrix3x3), + UniformName::new("noise", UniformType::_1f), ], )?; let shadow_shader = renderer.compile_custom_pixel_shader( @@ -440,6 +442,7 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> { UniformName::new("window_corner_radius", UniformType::_4f), ], )?; + let blur_shaders = BlurShaders::compile(renderer)?; let egl_context = renderer.egl_context(); egl_context @@ -457,6 +460,7 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> { egl_context .user_data() .insert_if_missing(|| ShadowShader(shadow_shader)); + egl_context.user_data().insert_if_missing(|| blur_shaders); Ok(()) } diff --git a/src/backend/render/shaders/blur_downsample.frag b/src/backend/render/shaders/blur_downsample.frag new file mode 100644 index 000000000..1fa24beb8 --- /dev/null +++ b/src/backend/render/shaders/blur_downsample.frag @@ -0,0 +1,26 @@ +#version 100 + +//_DEFINES_ + +precision highp float; + +varying vec2 v_coords; + +uniform sampler2D tex; +uniform vec2 half_pixel; +uniform float offset; + +uniform float alpha; +#if defined(DEBUG_FLAGS) +uniform float tint; +#endif + +void main() { + vec4 sum = texture2D(tex, v_coords) * 4.0; + sum += texture2D(tex, v_coords - half_pixel * offset); + sum += texture2D(tex, v_coords + half_pixel * offset); + sum += texture2D(tex, v_coords + vec2(half_pixel.x, -half_pixel.y) * offset); + sum += texture2D(tex, v_coords - vec2(half_pixel.x, -half_pixel.y) * offset); + + gl_FragColor = sum / sum.a; +} diff --git a/src/backend/render/shaders/blur_upsample.frag b/src/backend/render/shaders/blur_upsample.frag new file mode 100644 index 000000000..ac026d198 --- /dev/null +++ b/src/backend/render/shaders/blur_upsample.frag @@ -0,0 +1,27 @@ +#version 100 + +precision highp float; + +varying vec2 v_coords; + +uniform sampler2D tex; +uniform vec2 half_pixel; +uniform float offset; + +uniform float alpha; +#if defined(DEBUG_FLAGS) +uniform float tint; +#endif + +void main() { + vec4 sum = texture2D(tex, v_coords + vec2(-half_pixel.x * 2.0, 0.0) * offset); + sum += texture2D(tex, v_coords + vec2(-half_pixel.x, half_pixel.y) * offset) * 2.0; + sum += texture2D(tex, v_coords + vec2(0.0, half_pixel.y * 2.0) * offset); + sum += texture2D(tex, v_coords + vec2(half_pixel.x, half_pixel.y) * offset) * 2.0; + sum += texture2D(tex, v_coords + vec2(half_pixel.x * 2.0, 0.0) * offset); + sum += texture2D(tex, v_coords + vec2(half_pixel.x, -half_pixel.y) * offset) * 2.0; + sum += texture2D(tex, v_coords + vec2(0.0, -half_pixel.y * 2.0) * offset); + sum += texture2D(tex, v_coords + vec2(-half_pixel.x, -half_pixel.y) * offset) * 2.0; + + gl_FragColor = sum / sum.a; +} diff --git a/src/backend/render/shaders/clipped_surface.frag b/src/backend/render/shaders/clipped_surface.frag index dd863d232..d99d1a569 100644 --- a/src/backend/render/shaders/clipped_surface.frag +++ b/src/backend/render/shaders/clipped_surface.frag @@ -25,6 +25,7 @@ uniform float tint; uniform vec2 geo_size; uniform vec4 corner_radius; uniform mat3 input_to_geo; +uniform float noise; float rounding_alpha(vec2 coords, vec2 size) { vec2 center; @@ -51,15 +52,26 @@ float rounding_alpha(vec2 coords, vec2 size) { return 1.0 - smoothstep(radius - half_px, radius + half_px, dist); } +float hash(vec2 p) { + vec3 p3 = fract(vec3(p.xyx) * 727.727); + p3 += dot(p3, p3.xyz + 33.33); + return fract((p3.x + p3.y) * p3.z); +} + void main() { - vec3 coords_geo = input_to_geo * vec3(v_coords, 1.0); + if (alpha <= 0.0) { + discard; + } - // Sample the texture. vec4 color = texture2D(tex, v_coords); #if defined(NO_ALPHA) color = vec4(color.rgb, 1.0); #endif + if (color.a <= 0.0) { + discard; + } + vec3 coords_geo = input_to_geo * vec3(v_coords, 1.0); if (coords_geo.x < 0.0 || 1.0 < coords_geo.x || coords_geo.y < 0.0 || 1.0 < coords_geo.y) { // Clip outside geometry. color = vec4(0.0); @@ -68,8 +80,20 @@ void main() { color = color * rounding_alpha(coords_geo.xy * geo_size, geo_size); } + if (color.a <= 0.0) { + discard; + } + + if (noise > 0.0) { + // Add noise fx + // This can be used to achieve a glass look + float noiseHash = hash(v_coords); + float noiseAmount = (mod(noiseHash, 1.0) - 0.5); + color.rgb += noiseAmount * noise; + } + // Apply final alpha and tint. - color = color * alpha; + color *= alpha; #if defined(DEBUG_FLAGS) if (tint == 1.0) diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs new file mode 100644 index 000000000..895cd19e1 --- /dev/null +++ b/src/backend/render/wayland/blur_effect.rs @@ -0,0 +1,578 @@ +use std::{ + borrow::{Borrow, BorrowMut}, + sync::{ + Arc, Mutex, + atomic::{AtomicBool, Ordering}, + }, +}; + +use cgmath::{Matrix3, SquareMatrix, Vector2}; +use smithay::{ + backend::{ + allocator::Fourcc, + renderer::{ + Bind, BlitFrame, Color32F, ContextId, Frame, FrameContext, ImportAll, Offscreen, + Renderer, Texture, TextureFilter, + element::{Element, Id, Kind, RenderElement}, + gles::{ + GlesError, GlesFrame, GlesRenderer, GlesTexProgram, GlesTexture, Uniform, + UniformName, UniformType, UniformValue, ffi, + }, + sync::SyncPoint, + utils::{CommitCounter, DamageSet}, + }, + }, + utils::{ + Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform, user_data::UserDataMap, + }, + wayland::compositor::SurfaceData, +}; + +use crate::{ + backend::render::{element::AsGlowRenderer, wayland::clipped_surface::ClippingShader}, + wayland::handlers::background_effect::ComputedBlurRegionCachedState, +}; + +pub static BLUR_DOWNSAMPLE_SHADER: &str = include_str!("../shaders/blur_downsample.frag"); +pub static BLUR_UPSAMPLE_SHADER: &str = include_str!("../shaders/blur_upsample.frag"); + +const PASSES: usize = 3; +const OFFSET: f64 = 1.1; +const NOISE: f32 = 0.03; + +#[derive(Debug, Clone)] +pub struct BlurShaders { + down: GlesTexProgram, + up: GlesTexProgram, +} + +impl BlurShaders { + pub fn compile(renderer: &mut GlesRenderer) -> Result { + let up = renderer.compile_custom_texture_shader( + BLUR_UPSAMPLE_SHADER, + &[ + UniformName::new("half_pixel", UniformType::_2f), + UniformName::new("offset", UniformType::_1f), + ], + )?; + let down = renderer.compile_custom_texture_shader( + BLUR_DOWNSAMPLE_SHADER, + &[ + UniformName::new("half_pixel", UniformType::_2f), + UniformName::new("offset", UniformType::_1f), + ], + )?; + + Ok(BlurShaders { up, down }) + } + + pub fn get(renderer: &R) -> Self { + Borrow::::borrow(renderer.glow_renderer()) + .egl_context() + .user_data() + .get::() + .expect("Custom Shaders not initialized") + .clone() + } +} + +type BlurTexture = Mutex>; + +struct BlurState { + id: Id, + renderer_id: Option>, + src: Size, + offset: f64, + passes: usize, + region: Vec>, + dirty: Arc, +} + +unsafe impl Send for BlurState {} +unsafe impl Sync for BlurState {} + +impl BlurState { + fn new() -> Self { + BlurState { + id: Id::new(), + renderer_id: None, + src: Size::new(0., 0.), + offset: 0., + passes: 0, + region: Vec::new(), + dirty: Arc::new(AtomicBool::new(true)), + } + } +} + +pub struct BlurElement { + id: Id, + src: Size, + geometry: Rectangle, + scaling_shaders: BlurShaders, + render_shader: GlesTexProgram, + dirty: Arc, + region: Vec>, + offset: f64, + passes: usize, + uniforms: Vec>, +} + +impl BlurElement { + pub fn from_surface( + renderer: &mut R, + states: &SurfaceData, + geometry: Rectangle, + output_scale: f64, + radii: [u8; 4], + ) -> Result, R::Error> { + let mut blur_region_state = states.cached_state.get::(); + let Some(region) = blur_region_state.current().blur_region.as_ref() else { + return Ok(None); + }; + + let geo = geometry.to_physical_precise_round(output_scale); + let mut extended_geo = geo; + let radius = OFFSET * 2.0f64.powf(PASSES as f64); + extended_geo.loc -= Point::::new(radius, radius); + extended_geo.size += Size::::new(radius, radius).upscale(2.); + + // compute input_to_geo so that it crops the extended capture radius + let geo_scale = { + let Scale { x, y } = geo.size / extended_geo.size; + Matrix3::from_nonuniform_scale(x as f32, y as f32) + .invert() + .unwrap() + }; + let geo_translation = { + let offset = geo.loc - extended_geo.loc; + Matrix3::from_translation(Vector2::new( + (offset.x / extended_geo.size.w) as f32, + (offset.y / extended_geo.size.h) as f32, + )) + .invert() + .unwrap() + }; + let input_to_geo = geo_scale * geo_translation; + + let uniforms = vec![ + Uniform::new("geo_size", (geometry.size.w as f32, geometry.size.h as f32)), + Uniform::new( + "corner_radius", + [ + radii[3] as f32, + radii[1] as f32, + radii[0] as f32, + radii[2] as f32, + ], + ), + Uniform::new( + "input_to_geo", + UniformValue::Matrix3x3 { + matrices: vec![*AsRef::<[f32; 9]>::as_ref(&input_to_geo)], + transpose: false, + }, + ), + Uniform::new("noise", UniformValue::_1f(NOISE)), + ]; + + let state = states + .data_map + .get_or_insert_threadsafe::, _>(|| Mutex::new(BlurState::new())); + + Ok(Some(Self::from_state( + renderer, + extended_geo.to_logical(output_scale), + ®ion, + output_scale, + &mut *state.lock().unwrap(), + uniforms, + )?)) + } + + fn from_state( + renderer: &mut R, + geometry: Rectangle, + region: &Vec>, + output_scale: f64, + state: &mut BlurState, + uniforms: Vec>, + ) -> Result { + let renderer_id = renderer.glow_renderer().context_id(); + let src = geometry.size.to_buffer(output_scale, Transform::Normal); + + let dirty = !(state + .renderer_id + .as_ref() + .is_some_and(|id| id == &renderer_id) + && &state.region == region + && state.src == src); + state.renderer_id = Some(renderer_id); + state.offset = OFFSET * output_scale; // we want the blur result to be stable across scaling; + state.passes = PASSES; + state.region = region.clone(); + state.src = src; + if dirty { + state.dirty.store(dirty, Ordering::Release); + } + + Ok(BlurElement { + id: state.id.clone(), + src, + geometry, + scaling_shaders: BlurShaders::get(renderer), + render_shader: ClippingShader::get(renderer), + dirty: state.dirty.clone(), + offset: state.offset, + passes: state.passes, + region: region + .iter() + .map(|rect| rect.to_physical_precise_round(output_scale)) + .collect(), + uniforms, + }) + } +} + +impl Element for BlurElement { + fn id(&self) -> &Id { + &self.id + } + + fn current_commit(&self) -> CommitCounter { + Default::default() + } + + fn src(&self) -> Rectangle { + Rectangle::from_size(self.src) + } + + fn geometry(&self, scale: Scale) -> Rectangle { + self.geometry.to_physical_precise_round(scale) + } + + fn transform(&self) -> Transform { + Transform::Normal + } + + fn damage_since( + &self, + scale: Scale, + commit: Option, + ) -> DamageSet { + if commit.is_none() || self.dirty.load(Ordering::Acquire) { + DamageSet::from_slice(&[self.geometry.to_physical_precise_round(scale)]) + } else { + DamageSet::default() + } + } + + fn alpha(&self) -> f32 { + 1.0 + } + + fn kind(&self) -> Kind { + Kind::default() + } + + fn is_framebuffer_effect(&self) -> bool { + true + } +} + +impl RenderElement for BlurElement { + fn capture_framebuffer( + &self, + frame: &mut ::Frame<'_, '_>, + src: Rectangle, + dst: Rectangle, + cache: &UserDataMap, + ) -> Result<(), ::Error> { + let glow_frame = ::glow_frame_mut(frame); + let gles_frame = BorrowMut::>::borrow_mut(glow_frame); + let transform = gles_frame.transformation(); + let tex_size = self.src.to_i32_round(); + + let texture_ref = cache.get_or_insert_threadsafe(BlurTexture::default); + let mut texture_entry = texture_ref.lock().unwrap(); + if self.dirty.swap(false, Ordering::Acquire) { + texture_entry.take(); + } + + let mut renderer = gles_frame.renderer(); + if texture_entry.is_none() { + *texture_entry = Some( + renderer + .as_mut() + .create_buffer(Fourcc::Abgr8888, tex_size) + .map_err(R::from_gles_error)?, + ); + } + let texture = texture_entry.as_mut().unwrap(); + let mut off_texture = renderer + .as_mut() + .create_buffer(Fourcc::Abgr8888, tex_size) + .map_err(R::from_gles_error)?; + std::mem::drop(renderer); + + let sync = blit_from_active_fb(gles_frame, src, dst, transform, texture) + .map_err(R::from_gles_error)?; + gles_frame.wait(&sync).map_err(R::from_gles_error)?; + + let mut textures = [texture, &mut off_texture]; + render_blur( + gles_frame.renderer().as_mut(), + &self.scaling_shaders, + &mut textures, + self.offset, + self.passes, + ) + .map_err(R::from_gles_error)?; + + Ok(()) + } + + fn draw( + &self, + frame: &mut R::Frame<'_, '_>, + src: Rectangle, + dst: Rectangle, + damage: &[Rectangle], + opaque_regions: &[Rectangle], + cache: Option<&UserDataMap>, + ) -> Result<(), R::Error> { + let glow_frame = ::glow_frame_mut(frame); + let damage = self + .region + .iter() + .flat_map(|rect| damage.iter().flat_map(|r| r.intersection(*rect))) + .collect::>(); + let cache = cache.expect("Framebuffer element without cache?"); + let Some(texture) = cache.get::() else { + return Err(R::from_gles_error(GlesError::BlitError)); + }; + let texture_ref = texture.lock().unwrap(); + + BorrowMut::>::borrow_mut(glow_frame) + .render_texture_from_to( + texture_ref + .as_ref() + .ok_or(R::from_gles_error(GlesError::BlitError))?, + src, + dst, + &damage, + opaque_regions, + Transform::Normal, + 1.0, + Some(&self.render_shader), + &self.uniforms, + ) + .map_err(R::from_gles_error) + } +} + +fn blit_from_active_fb( + frame: &mut GlesFrame<'_, '_>, + src: Rectangle, + dst: Rectangle, + transform: Transform, + to_texture: &mut GlesTexture, +) -> Result { + if transform != Transform::Normal { + let tex_size = to_texture + .size() + .to_logical(1, Transform::Normal) + .to_physical(1); + let mut renderer = frame.renderer(); + let mut tmp_texture = renderer.as_mut().create_buffer( + Fourcc::Abgr8888, + dst.size.to_logical(1).to_buffer(1, Transform::Normal), + )?; + let mut fb_tmp = renderer.as_mut().bind(&mut tmp_texture)?; + let mut fb = renderer.as_mut().bind(to_texture)?; + std::mem::drop(renderer); + + frame.blit_to( + &mut fb_tmp, + dst, + Rectangle::from_size(dst.size), + TextureFilter::Linear, + )?; + std::mem::drop(fb_tmp); + + let mut renderer = frame.renderer(); + let mut frame = renderer + .as_mut() + .render(&mut fb, tex_size, Transform::Normal)?; + Frame::render_texture_from_to( + &mut frame, + &tmp_texture, + Rectangle::from_size( + dst.size + .to_logical(1) + .to_buffer(1, Transform::Normal) + .to_f64(), + ), + src.to_logical(1., Transform::Normal, &src.size) + .to_physical(1.) + .to_i32_round(), + &[Rectangle::from_size(dst.size)], + &[Rectangle::from_size(dst.size)], + transform, + 1.0, + )?; + std::mem::drop(tmp_texture); + frame.finish() + } else { + let mut fb = frame.renderer().as_mut().bind(to_texture)?; + + frame + .blit_to( + &mut fb, + dst, + src.to_logical(1., Transform::Normal, &src.size) + .to_physical(1.) + .to_i32_round(), + TextureFilter::Linear, + ) + .map(|_| SyncPoint::signaled()) + } +} + +fn render_blur( + renderer: &mut GlesRenderer, + shaders: &BlurShaders, + textures: &mut [&mut GlesTexture; 2], + offset: f64, + passes: usize, +) -> Result<(), GlesError> { + for i in 0..passes { + let tex_size = textures[0].size(); + let [src_tex, target_tex] = textures; + let mut fb = renderer.bind(*target_tex)?; + + let adjusted_tex_size = tex_size.downscale(1 << i); + let target_tex_size = tex_size + .downscale(1 << i + 1) + .to_logical(1, Transform::Normal) + .to_physical(1); + let half_pixel = [ + 0.5 / (adjusted_tex_size.w as f32), + 0.5 / (adjusted_tex_size.h as f32), + ]; + + let mut frame = renderer.render( + &mut fb, + tex_size.to_logical(1, Transform::Normal).to_physical(1), + Transform::Normal, + )?; + frame.clear( + Color32F::new(0., 0., 0., 0.), + &[Rectangle::from_size( + tex_size.to_logical(1, Transform::Normal).to_physical(1), + )], + )?; + frame.with_context(|gl| unsafe { + gl.TexParameteri( + ffi::TEXTURE_2D, + ffi::TEXTURE_WRAP_S, + ffi::CLAMP_TO_EDGE as i32, + ); + gl.TexParameteri( + ffi::TEXTURE_2D, + ffi::TEXTURE_WRAP_T, + ffi::CLAMP_TO_EDGE as i32, + ); + })?; + frame.render_texture_from_to( + src_tex, + Rectangle::from_size(adjusted_tex_size.to_f64()), + Rectangle::from_size(target_tex_size), + &[Rectangle::from_size(target_tex_size)], + &[Rectangle::from_size(target_tex_size)], + Transform::Normal, + 1.0, + Some(&shaders.down), + &[ + Uniform::new("half_pixel", half_pixel), + Uniform::new("offset", (offset / (1 << i) as f64) as f32), + ], + )?; + frame.with_context(|gl| unsafe { + gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::REPEAT as i32); + gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::REPEAT as i32); + })?; + let sync = frame.finish()?; + std::mem::drop(fb); + renderer.wait(&sync)?; + + textures.swap(0, 1); + } + + for i in 0..passes { + let tex_size = textures[0].size(); + let [src_tex, target_tex] = textures; + let mut fb = renderer.bind(*target_tex)?; + + let adjusted_tex_size = tex_size.downscale(1 << (passes - i)); + let target_tex_size = tex_size + .downscale(1 << (passes - i - 1)) + .to_logical(1, Transform::Normal) + .to_physical(1); + let half_pixel = [ + 0.5 / (adjusted_tex_size.w as f32), + 0.5 / (adjusted_tex_size.h as f32), + ]; + + let mut frame = renderer.render( + &mut fb, + tex_size.to_logical(1, Transform::Normal).to_physical(1), + Transform::Normal, + )?; + frame.clear( + Color32F::new(0., 0., 0., 0.), + &[Rectangle::from_size( + tex_size.to_logical(1, Transform::Normal).to_physical(1), + )], + )?; + frame.with_context(|gl| unsafe { + gl.TexParameteri( + ffi::TEXTURE_2D, + ffi::TEXTURE_WRAP_S, + ffi::CLAMP_TO_EDGE as i32, + ); + gl.TexParameteri( + ffi::TEXTURE_2D, + ffi::TEXTURE_WRAP_T, + ffi::CLAMP_TO_EDGE as i32, + ); + })?; + frame.render_texture_from_to( + src_tex, + Rectangle::from_size(adjusted_tex_size.to_f64()), + Rectangle::from_size(target_tex_size), + &[Rectangle::from_size(target_tex_size)], + &[Rectangle::from_size(target_tex_size)], + Transform::Normal, + 1.0, + Some(&shaders.up), + &[ + Uniform::new("half_pixel", half_pixel), + Uniform::new("offset", (offset / (1 << (passes - i)) as f64) as f32), + ], + )?; + frame.with_context(|gl| unsafe { + gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::REPEAT as i32); + gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::REPEAT as i32); + })?; + let sync = frame.finish()?; + std::mem::drop(fb); + renderer.wait(&sync)?; + + textures.swap(0, 1); + } + + // textures always end up the right way around with `self.texture` containing our final render, + // since we render PASSES * 2 (downscale and upscale), so the number of swaps is always even. + Ok(()) +} diff --git a/src/backend/render/wayland/clipped_surface.rs b/src/backend/render/wayland/clipped_surface.rs index 37eee7f85..92e64ad0c 100644 --- a/src/backend/render/wayland/clipped_surface.rs +++ b/src/backend/render/wayland/clipped_surface.rs @@ -111,6 +111,7 @@ where transpose: false, }, ), + Uniform::new("noise", UniformValue::_1f(0.0)), ]; Self { diff --git a/src/backend/render/wayland/mod.rs b/src/backend/render/wayland/mod.rs index d766552ad..b9e07a800 100644 --- a/src/backend/render/wayland/mod.rs +++ b/src/backend/render/wayland/mod.rs @@ -12,13 +12,16 @@ use smithay::{ use tracing::warn; use crate::backend::render::{ - element::AsGlowRenderer, wayland::clipped_surface::ClippedSurfaceRenderElement, + element::AsGlowRenderer, + wayland::{blur_effect::BlurElement, clipped_surface::ClippedSurfaceRenderElement}, }; +pub mod blur_effect; pub mod clipped_surface; render_elements! { pub SurfaceRenderElement where R: AsGlowRenderer + ImportAll; + Blur=BlurElement, Clipped=ClippedSurfaceRenderElement, Wayland=WaylandSurfaceRenderElement, } @@ -67,6 +70,7 @@ pub fn push_render_elements_from_surface_tree( let mut location = *location; let kind = kind.eval(states); let data = states.data_map.get::(); + let mut blur = Ok(None); if let Some(data) = data { let has_view = if let Some(view) = data.lock().unwrap().view() { @@ -82,6 +86,9 @@ pub fn push_render_elements_from_surface_tree( renderer, surface, states, location, alpha, kind, ) { Ok(Some(surface)) => { + blur = BlurElement::from_surface( + renderer, states, geometry, scale.x, radii, + ); let elem: SurfaceRenderElement = if radii.iter().any(|r| *r != 0) && should_clip && ClippedSurfaceRenderElement::will_clip( @@ -113,6 +120,16 @@ pub fn push_render_elements_from_surface_tree( if surface == main_surface { passed_main = true; } + + if let Ok(Some(elem)) = blur { + if let Some(push_below) = push_below.as_mut() + && passed_main + { + push_below(elem.into()); + } else { + push_above(elem.into()); + } + } }, |_, _, _| true, ); diff --git a/src/state.rs b/src/state.rs index 21dfcef0a..fc93c1fad 100644 --- a/src/state.rs +++ b/src/state.rs @@ -73,6 +73,7 @@ use smithay::{ utils::{Clock, Monotonic, Point}, wayland::{ alpha_modifier::AlphaModifierState, + background_effect::BackgroundEffectState, compositor::{CompositorClientState, CompositorState, SurfaceData}, cursor_shape::CursorShapeManagerState, dmabuf::{DmabufFeedback, DmabufGlobal, DmabufState}, @@ -279,6 +280,7 @@ pub struct Common { pub overlap_notify_state: OverlapNotifyState, pub a11y_state: A11yState, pub a11y_keyboard_monitor_state: A11yKeyboardMonitorState, + pub background_effect_state: BackgroundEffectState, // shell-related wayland state pub xdg_shell_state: XdgShellState, @@ -682,6 +684,8 @@ impl State { SinglePixelBufferState::new::(dh); FixesState::new::(dh); + let background_effect_state = BackgroundEffectState::new::(dh); + let idle_notifier_state = IdleNotifierState::::new(dh, handle.clone()); let idle_inhibit_manager_state = IdleInhibitManagerState::new::(dh); let idle_inhibiting_surfaces = HashSet::new(); @@ -792,6 +796,7 @@ impl State { xdg_activation_state, xdg_foreign_state, workspace_state, + background_effect_state, a11y_state, a11y_keyboard_monitor_state, xwayland_scale: None, diff --git a/src/wayland/handlers/background_effect.rs b/src/wayland/handlers/background_effect.rs new file mode 100644 index 000000000..6a7bb75b8 --- /dev/null +++ b/src/wayland/handlers/background_effect.rs @@ -0,0 +1,62 @@ +use smithay::{ + delegate_background_effect, + reexports::wayland_server::{DisplayHandle, protocol::wl_surface::WlSurface}, + utils::{Logical, Rectangle}, + wayland::{ + background_effect::{Capability, ExtBackgroundEffectHandler}, + compositor::{Cacheable, RectangleKind, RegionAttributes, with_states}, + }, +}; + +use crate::state::State; + +#[derive(Debug, Clone, Default)] +pub struct ComputedBlurRegionCachedState { + /// Region of the surface that will have its background blurred. + pub blur_region: Option>>, +} + +impl Cacheable for ComputedBlurRegionCachedState { + fn commit(&mut self, _dh: &DisplayHandle) -> Self { + self.clone() + } + + fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) { + *into = self; + } +} + +impl ExtBackgroundEffectHandler for State { + fn capabilities(&self) -> Capability { + Capability::Blur + } + + fn set_blur_region(&mut self, surface: WlSurface, region: RegionAttributes) { + with_states(&surface, |states| { + let mut blur_state = states.cached_state.get::(); + + blur_state.pending().blur_region = Some({ + let (added, subtracted) = region + .rects + .iter() + .cloned() + .partition::, _>(|(op, _)| matches!(op, RectangleKind::Add)); + let added = added.into_iter().map(|(_, rect)| rect).collect::>(); + Rectangle::subtract_rects_many_in_place( + added, + subtracted.into_iter().map(|(_, rect)| rect), + ) + }) + }) + } + + fn unset_blur_region(&mut self, surface: WlSurface) { + with_states(&surface, |states| { + let mut blur_state = states.cached_state.get::(); + + blur_state.pending().blur_region.take(); + }) + } +} + +delegate_background_effect!(State); diff --git a/src/wayland/handlers/mod.rs b/src/wayland/handlers/mod.rs index d3d22f005..c970c92c6 100644 --- a/src/wayland/handlers/mod.rs +++ b/src/wayland/handlers/mod.rs @@ -2,6 +2,7 @@ pub mod a11y; pub mod alpha_modifier; +pub mod background_effect; pub mod buffer; pub mod compositor; pub mod corner_radius; From 2d13f85dc1db9437a79df90d27e84c9bda080e71 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 6 Mar 2026 17:19:12 +0100 Subject: [PATCH 07/16] render: Use NamespaceElements to fix blurred lower layer-shell surfaces --- src/backend/render/mod.rs | 27 ++++++++++++++---- src/input/mod.rs | 9 ++++-- src/shell/focus/order.rs | 58 ++++++++++++++++++++++++++++++--------- src/shell/mod.rs | 3 +- src/shell/workspace.rs | 32 ++++++++++++++++++++- src/state.rs | 50 +++++++++++++++++---------------- 6 files changed, 134 insertions(+), 45 deletions(-) diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 6f186af15..cead5677a 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -55,7 +55,7 @@ use smithay::{ Color32F, Offscreen, Texture, TextureFilter, damage::{Error as RenderError, OutputDamageTracker, RenderOutputResult}, element::{ - Element, Id, Kind, RenderElement, WeakId, + Element, Id, Kind, NamespacedElement, RenderElement, WeakId, texture::{TextureRenderBuffer, TextureRenderElement}, utils::{ ConstrainAlign, ConstrainScaleBehavior, CropRenderElement, Relocate, @@ -820,7 +820,10 @@ where }) } Stage::LayerPopup { - popup, location, .. + popup, + location, + workspace_idx, + .. } => { let mut geometry = popup.geometry().as_global(); geometry.loc += location; @@ -838,11 +841,20 @@ where false, [0; 4], FRAME_TIME_FILTER, - &mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)), + &mut |elem| { + elements.extend( + crop_to_output(NamespacedElement::new(elem, workspace_idx).into()) + .map(Into::into), + ) + }, None, ) } - Stage::LayerSurface { layer, location } => { + Stage::LayerSurface { + layer, + location, + workspace_idx, + } => { let mut geometry = layer.geometry().as_global(); geometry.loc += location; @@ -859,7 +871,12 @@ where false, [0; 4], FRAME_TIME_FILTER, - &mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)), + &mut |elem| { + elements.extend( + crop_to_output(NamespacedElement::new(elem, workspace_idx).into()) + .map(Into::into), + ) + }, None, ); } diff --git a/src/input/mod.rs b/src/input/mod.rs index b5dbd3b2c..50713eb48 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2055,6 +2055,7 @@ impl State { layer, popup, location, + .. } => { if layer.can_receive_keyboard_focus() { let surface = popup.wl_surface(); @@ -2072,7 +2073,9 @@ impl State { } } } - Stage::LayerSurface { layer, location } => { + Stage::LayerSurface { + layer, location, .. + } => { if under_from_surface_tree( layer.wl_surface(), global_pos.as_logical(), @@ -2224,7 +2227,9 @@ impl State { )))); } } - Stage::LayerSurface { layer, location } => { + Stage::LayerSurface { + layer, location, .. + } => { let surface = layer.wl_surface(); if let Some((surface, surface_loc)) = under_from_surface_tree( surface, diff --git a/src/shell/focus/order.rs b/src/shell/focus/order.rs index 6a2488fe7..8a37c2c5f 100644 --- a/src/shell/focus/order.rs +++ b/src/shell/focus/order.rs @@ -32,10 +32,12 @@ pub enum Stage<'a> { layer: LayerSurface, popup: &'a PopupKind, location: Point, + workspace_idx: usize, }, LayerSurface { layer: LayerSurface, location: Point, + workspace_idx: usize, }, OverrideRedirect { surface: &'a X11Surface, @@ -95,10 +97,15 @@ fn render_input_order_internal( layer, popup: &popup, location, + workspace_idx: current.1, })?; } for (layer, location) in layer_surfaces(output, Layer::Overlay, element_filter) { - callback(Stage::LayerSurface { layer, location })?; + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: current.1, + })?; } // calculate a bunch of stuff for workspace transitions @@ -187,7 +194,7 @@ fn render_input_order_internal( }); ( - Some((previous, has_fullscreen, offset)), + Some((previous, previous_idx, has_fullscreen, offset)), Point::::from(match (layout, forward) { (WorkspaceLayout::Vertical, true) => (0, output_size.h + offset.y), (WorkspaceLayout::Vertical, false) => (0, -(output_size.h - offset.y)), @@ -206,6 +213,7 @@ fn render_input_order_internal( layer, popup: &popup, location, + workspace_idx: current.1, })?; } } @@ -238,7 +246,7 @@ fn render_input_order_internal( if element_filter != ElementFilter::LayerShellOnly { // previous workspace popups - if let Some((previous_handle, _, offset)) = previous.as_ref() { + if let Some((previous_handle, _, _, offset)) = previous.as_ref() { let Some(workspace) = shell.workspaces.space_for_handle(previous_handle) else { return ControlFlow::Break(Err(OutputNoMode)); }; @@ -267,11 +275,12 @@ fn render_input_order_internal( layer, popup: &popup, location, + workspace_idx: current.1, })?; } } - if let Some((_, has_fullscreen, offset)) = previous.as_ref() + if let Some((_, idx, has_fullscreen, offset)) = previous.as_ref() && !has_fullscreen { // previous bottom layer popups @@ -280,6 +289,7 @@ fn render_input_order_internal( layer, popup: &popup, location: location + offset.as_global(), + workspace_idx: **idx, })?; } } @@ -291,11 +301,12 @@ fn render_input_order_internal( layer, popup: &popup, location, + workspace_idx: current.1, })?; } } - if let Some((_, has_fullscreen, offset)) = previous.as_ref() + if let Some((_, idx, has_fullscreen, offset)) = previous.as_ref() && !has_fullscreen { // previous background layer popups @@ -304,6 +315,7 @@ fn render_input_order_internal( layer, popup: &popup, location: location + offset.as_global(), + workspace_idx: **idx, })?; } } @@ -311,7 +323,11 @@ fn render_input_order_internal( if !has_focused_fullscreen { // top-layer shell for (layer, location) in layer_surfaces(output, Layer::Top, element_filter) { - callback(Stage::LayerSurface { layer, location })?; + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: current.1, + })?; } // sticky windows @@ -328,7 +344,7 @@ fn render_input_order_internal( })?; // previous workspace windows - if let Some((previous_handle, _, offset)) = previous.as_ref() { + if let Some((previous_handle, _, _, offset)) = previous.as_ref() { let Some(workspace) = shell.workspaces.space_for_handle(previous_handle) else { return ControlFlow::Break(Err(OutputNoMode)); }; @@ -343,17 +359,25 @@ fn render_input_order_internal( // bottom layer for (layer, mut location) in layer_surfaces(output, Layer::Bottom, element_filter) { location += current_offset.as_global(); - callback(Stage::LayerSurface { layer, location })?; + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: current.1, + })?; } } - if let Some((_, has_fullscreen, offset)) = previous.as_ref() + if let Some((_, idx, has_fullscreen, offset)) = previous.as_ref() && !has_fullscreen { // previous bottom layer for (layer, mut location) in layer_surfaces(output, Layer::Bottom, element_filter) { location += offset.as_global(); - callback(Stage::LayerSurface { layer, location })?; + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: **idx, + })?; } } @@ -361,17 +385,25 @@ fn render_input_order_internal( // background layer for (layer, mut location) in layer_surfaces(output, Layer::Background, element_filter) { location += current_offset.as_global(); - callback(Stage::LayerSurface { layer, location })?; + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: current.1, + })?; } } - if let Some((_, has_fullscreen, offset)) = previous.as_ref() + if let Some((_, idx, has_fullscreen, offset)) = previous.as_ref() && !has_fullscreen { // previous background layer for (layer, mut location) in layer_surfaces(output, Layer::Background, element_filter) { location += offset.as_global(); - callback(Stage::LayerSurface { layer, location })?; + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: **idx, + })?; } } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 4dde0f82b..5a378774c 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -4886,13 +4886,14 @@ impl Shell { let map = smithay::desktop::layer_map_for_output(output); for layer_surface in map.layers() { + let namespace = self.workspaces.active_num(output).1; layer_surface.take_presentation_feedback( &mut output_presentation_feedback, surface_primary_scanout_output, |surface, _| { surface_presentation_feedback_flags_from_states( surface, - None, + Some(namespace), render_element_states, ) }, diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 248500b7c..8e6d1e939 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -30,7 +30,7 @@ use id_tree::Tree; use indexmap::IndexSet; use keyframe::{ease, functions::EaseInOutCubic}; use smallvec::SmallVec; -use smithay::backend::renderer::element::Kind; +use smithay::backend::renderer::element::{Kind, NamespacedElement}; use smithay::output::WeakOutput; use smithay::utils::user_data::UserDataMap; use smithay::{ @@ -1856,6 +1856,7 @@ where R::TextureId: 'static, { OverrideRedirect(SurfaceRenderElement), + LowerLayerShell(NamespacedElement>), Fullscreen(RescaleRenderElement>), FullscreenPopup(CosmicWindowRenderElement), Window(CosmicMappedRenderElement), @@ -1870,6 +1871,7 @@ where fn id(&self) -> &smithay::backend::renderer::element::Id { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.id(), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.id(), WorkspaceRenderElement::Fullscreen(elem) => elem.id(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.id(), WorkspaceRenderElement::Window(elem) => elem.id(), @@ -1880,6 +1882,7 @@ where fn current_commit(&self) -> smithay::backend::renderer::utils::CommitCounter { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.current_commit(), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.current_commit(), WorkspaceRenderElement::Fullscreen(elem) => elem.current_commit(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.current_commit(), WorkspaceRenderElement::Window(elem) => elem.current_commit(), @@ -1890,6 +1893,7 @@ where fn src(&self) -> Rectangle { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.src(), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.src(), WorkspaceRenderElement::Fullscreen(elem) => elem.src(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.src(), WorkspaceRenderElement::Window(elem) => elem.src(), @@ -1900,6 +1904,7 @@ where fn geometry(&self, scale: Scale) -> Rectangle { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.geometry(scale), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.geometry(scale), WorkspaceRenderElement::Fullscreen(elem) => elem.geometry(scale), WorkspaceRenderElement::FullscreenPopup(elem) => elem.geometry(scale), WorkspaceRenderElement::Window(elem) => elem.geometry(scale), @@ -1910,6 +1915,7 @@ where fn location(&self, scale: Scale) -> Point { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.location(scale), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.location(scale), WorkspaceRenderElement::Fullscreen(elem) => elem.location(scale), WorkspaceRenderElement::FullscreenPopup(elem) => elem.location(scale), WorkspaceRenderElement::Window(elem) => elem.location(scale), @@ -1920,6 +1926,7 @@ where fn transform(&self) -> smithay::utils::Transform { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.transform(), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.transform(), WorkspaceRenderElement::Fullscreen(elem) => elem.transform(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.transform(), WorkspaceRenderElement::Window(elem) => elem.transform(), @@ -1934,6 +1941,7 @@ where ) -> DamageSet { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.damage_since(scale, commit), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.damage_since(scale, commit), WorkspaceRenderElement::Fullscreen(elem) => elem.damage_since(scale, commit), WorkspaceRenderElement::FullscreenPopup(elem) => elem.damage_since(scale, commit), WorkspaceRenderElement::Window(elem) => elem.damage_since(scale, commit), @@ -1944,6 +1952,7 @@ where fn opaque_regions(&self, scale: Scale) -> OpaqueRegions { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.opaque_regions(scale), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.opaque_regions(scale), WorkspaceRenderElement::Fullscreen(elem) => elem.opaque_regions(scale), WorkspaceRenderElement::FullscreenPopup(elem) => elem.opaque_regions(scale), WorkspaceRenderElement::Window(elem) => elem.opaque_regions(scale), @@ -1954,6 +1963,7 @@ where fn alpha(&self) -> f32 { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.alpha(), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.alpha(), WorkspaceRenderElement::Fullscreen(elem) => elem.alpha(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.alpha(), WorkspaceRenderElement::Window(elem) => elem.alpha(), @@ -1964,6 +1974,7 @@ where fn kind(&self) -> Kind { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.kind(), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.kind(), WorkspaceRenderElement::Fullscreen(elem) => elem.kind(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.kind(), WorkspaceRenderElement::Window(elem) => elem.kind(), @@ -1974,6 +1985,7 @@ where fn is_framebuffer_effect(&self) -> bool { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.is_framebuffer_effect(), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.is_framebuffer_effect(), WorkspaceRenderElement::Fullscreen(elem) => elem.is_framebuffer_effect(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.is_framebuffer_effect(), WorkspaceRenderElement::Window(elem) => elem.is_framebuffer_effect(), @@ -2000,6 +2012,9 @@ where WorkspaceRenderElement::OverrideRedirect(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } + WorkspaceRenderElement::LowerLayerShell(elem) => { + elem.draw(frame, src, dst, damage, opaque_regions, cache) + } WorkspaceRenderElement::Fullscreen(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } @@ -2028,6 +2043,7 @@ where ) -> Option> { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.underlying_storage(renderer), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.underlying_storage(renderer), WorkspaceRenderElement::Fullscreen(elem) => elem.underlying_storage(renderer), WorkspaceRenderElement::FullscreenPopup(elem) => elem.underlying_storage(renderer), WorkspaceRenderElement::Window(elem) => elem.underlying_storage(renderer), @@ -2048,6 +2064,9 @@ where WorkspaceRenderElement::OverrideRedirect(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } + WorkspaceRenderElement::LowerLayerShell(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } WorkspaceRenderElement::Fullscreen(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } @@ -2104,6 +2123,17 @@ where } } +impl From>> for WorkspaceRenderElement +where + R: AsGlowRenderer, + R::TextureId: 'static, + CosmicMappedRenderElement: RenderElement, +{ + fn from(elem: NamespacedElement>) -> Self { + WorkspaceRenderElement::LowerLayerShell(elem) + } +} + impl From> for WorkspaceRenderElement where R: AsGlowRenderer, diff --git a/src/state.rs b/src/state.rs index fc93c1fad..338b39ef5 100644 --- a/src/state.rs +++ b/src/state.rs @@ -940,19 +940,22 @@ impl Common { render_element_states: &RenderElementStates, ) { let shell = self.shell.read(); - let processor = |surface: &WlSurface, states: &SurfaceData| { - let primary_scanout_output = update_surface_primary_scanout_output( - surface, - output, - states, - None, - render_element_states, - primary_scanout_output_compare, - ); - if let Some(output) = primary_scanout_output { - with_fractional_scale(states, |fraction_scale| { - fraction_scale.set_preferred_scale(output.current_scale().fractional_scale()); - }); + let processor = |namespace: Option| { + move |surface: &WlSurface, states: &SurfaceData| { + let primary_scanout_output = update_surface_primary_scanout_output( + surface, + output, + states, + namespace, + render_element_states, + primary_scanout_output_compare, + ); + if let Some(output) = primary_scanout_output { + with_fractional_scale(states, |fraction_scale| { + fraction_scale + .set_preferred_scale(output.current_scale().fractional_scale()); + }); + } } }; @@ -960,7 +963,7 @@ impl Common { if let Some(session_lock) = shell.session_lock.as_ref() && let Some(lock_surface) = session_lock.surfaces.get(output) { - with_surfaces_surface_tree(lock_surface.wl_surface(), processor) + with_surfaces_surface_tree(lock_surface.wl_surface(), processor(None)) } for seat in shell @@ -972,7 +975,7 @@ impl Common { // cursor ... if let CursorImageStatus::Surface(wl_surface) = cursor_status { - with_surfaces_surface_tree(&wl_surface, processor); + with_surfaces_surface_tree(&wl_surface, processor(None)); } // grabs @@ -980,12 +983,12 @@ impl Common { && let Some(grab_state) = move_grab.lock().unwrap().as_ref() { for (window, _) in grab_state.element().windows() { - window.with_surfaces(processor); + window.with_surfaces(processor(None)); } } if let Some(icon) = get_dnd_icon(seat) { - with_surfaces_surface_tree(&icon.surface, processor); + with_surfaces_surface_tree(&icon.surface, processor(None)); } } @@ -993,7 +996,7 @@ impl Common { for set in shell.workspaces.sets.values() { set.sticky_layer.mapped().for_each(|mapped| { for (window, _) in mapped.windows() { - window.with_surfaces(processor); + window.with_surfaces(processor(None)); } }); } @@ -1001,16 +1004,16 @@ impl Common { // normal windows for space in shell.workspaces.spaces() { if let Some(window) = space.get_fullscreen() { - window.with_surfaces(processor); + window.with_surfaces(processor(None)); } space.mapped().for_each(|mapped| { for (window, _) in mapped.windows() { - window.with_surfaces(processor); + window.with_surfaces(processor(None)); } }); space.minimized_windows.iter().for_each(|m| { for window in m.windows() { - window.with_surfaces(processor); + window.with_surfaces(processor(None)); } }) } @@ -1018,15 +1021,16 @@ impl Common { // OR windows shell.override_redirect_windows.iter().for_each(|or| { if let Some(wl_surface) = or.wl_surface() { - with_surfaces_surface_tree(&wl_surface, processor); + with_surfaces_surface_tree(&wl_surface, processor(None)); } }); // layer surfaces for o in shell.outputs() { + let namespace = shell.workspaces.active_num(o).1; let map = smithay::desktop::layer_map_for_output(o); for layer_surface in map.layers() { - layer_surface.with_surfaces(processor); + layer_surface.with_surfaces(processor(Some(namespace))); } } } From 5089076bc75efd1afe571f773fb012efbbf99765 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 10 Mar 2026 17:10:38 +0100 Subject: [PATCH 08/16] render: Don't use separate `damage_output` calls --- src/backend/render/element.rs | 30 +++++++++++++++++++ .../handlers/image_copy_capture/render.rs | 13 ++++---- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index 34a351c90..103fa52f9 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -43,6 +43,7 @@ where CropRenderElement>>, ), Zoom(MemoryRenderBufferRenderElement), + Damage(DamageElement), #[cfg(feature = "debug")] Egui(TextureRenderElement), } @@ -61,6 +62,7 @@ where CosmicElement::MoveGrab(elem) => elem.id(), CosmicElement::Postprocess(elem) => elem.id(), CosmicElement::Zoom(elem) => elem.id(), + CosmicElement::Damage(elem) => elem.id(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.id(), } @@ -74,6 +76,7 @@ where CosmicElement::MoveGrab(elem) => elem.current_commit(), CosmicElement::Postprocess(elem) => elem.current_commit(), CosmicElement::Zoom(elem) => elem.current_commit(), + CosmicElement::Damage(elem) => elem.current_commit(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.current_commit(), } @@ -87,6 +90,7 @@ where CosmicElement::MoveGrab(elem) => elem.src(), CosmicElement::Postprocess(elem) => elem.src(), CosmicElement::Zoom(elem) => elem.src(), + CosmicElement::Damage(elem) => elem.src(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.src(), } @@ -100,6 +104,7 @@ where CosmicElement::MoveGrab(elem) => elem.geometry(scale), CosmicElement::Postprocess(elem) => elem.geometry(scale), CosmicElement::Zoom(elem) => elem.geometry(scale), + CosmicElement::Damage(elem) => elem.geometry(scale), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.geometry(scale), } @@ -113,6 +118,7 @@ where CosmicElement::MoveGrab(elem) => elem.location(scale), CosmicElement::Postprocess(elem) => elem.location(scale), CosmicElement::Zoom(elem) => elem.location(scale), + CosmicElement::Damage(elem) => elem.location(scale), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.location(scale), } @@ -126,6 +132,7 @@ where CosmicElement::MoveGrab(elem) => elem.transform(), CosmicElement::Postprocess(elem) => elem.transform(), CosmicElement::Zoom(elem) => elem.transform(), + CosmicElement::Damage(elem) => elem.transform(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.transform(), } @@ -143,6 +150,7 @@ where CosmicElement::MoveGrab(elem) => elem.damage_since(scale, commit), CosmicElement::Postprocess(elem) => elem.damage_since(scale, commit), CosmicElement::Zoom(elem) => elem.damage_since(scale, commit), + CosmicElement::Damage(elem) => elem.damage_since(scale, commit), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.damage_since(scale, commit), } @@ -156,6 +164,7 @@ where CosmicElement::MoveGrab(elem) => elem.opaque_regions(scale), CosmicElement::Postprocess(elem) => elem.opaque_regions(scale), CosmicElement::Zoom(elem) => elem.opaque_regions(scale), + CosmicElement::Damage(elem) => elem.opaque_regions(scale), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.opaque_regions(scale), } @@ -169,6 +178,7 @@ where CosmicElement::MoveGrab(elem) => elem.alpha(), CosmicElement::Postprocess(elem) => elem.alpha(), CosmicElement::Zoom(elem) => elem.alpha(), + CosmicElement::Damage(elem) => elem.alpha(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.alpha(), } @@ -182,6 +192,7 @@ where CosmicElement::MoveGrab(elem) => elem.kind(), CosmicElement::Postprocess(elem) => elem.kind(), CosmicElement::Zoom(elem) => elem.kind(), + CosmicElement::Damage(elem) => elem.kind(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.kind(), } @@ -195,6 +206,7 @@ where CosmicElement::MoveGrab(elem) => elem.is_framebuffer_effect(), CosmicElement::Postprocess(elem) => elem.is_framebuffer_effect(), CosmicElement::Zoom(elem) => elem.is_framebuffer_effect(), + CosmicElement::Damage(elem) => elem.is_framebuffer_effect(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.is_framebuffer_effect(), } @@ -241,6 +253,9 @@ where .map_err(R::from_gles_error) } CosmicElement::Zoom(elem) => elem.draw(frame, src, dst, damage, opaque_regions, cache), + CosmicElement::Damage(elem) => { + RenderElement::::draw(elem, frame, src, dst, damage, opaque_regions, cache) + } #[cfg(feature = "debug")] CosmicElement::Egui(elem) => { let glow_frame = R::glow_frame_mut(frame); @@ -269,6 +284,7 @@ where elem.underlying_storage(glow_renderer) } CosmicElement::Zoom(elem) => elem.underlying_storage(renderer), + CosmicElement::Damage(elem) => elem.underlying_storage(renderer), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => { let glow_renderer = renderer.glow_renderer_mut(); @@ -297,6 +313,9 @@ where .map_err(R::from_gles_error) } CosmicElement::Zoom(elem) => elem.capture_framebuffer(frame, src, dst, cache), + CosmicElement::Damage(elem) => { + RenderElement::::capture_framebuffer(elem, frame, src, dst, cache) + } #[cfg(feature = "debug")] CosmicElement::Egui(elem) => { let glow_frame = R::glow_frame_mut(frame); @@ -336,6 +355,17 @@ where } } +impl From for CosmicElement +where + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + R::TextureId: 'static, + CosmicMappedRenderElement: RenderElement, +{ + fn from(value: DamageElement) -> Self { + Self::Damage(value) + } +} + #[cfg(feature = "debug")] impl From> for CosmicElement where diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index 163b00bd2..bc0891e08 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only use calloop::LoopHandle; -use smallvec::SmallVec; use smithay::{ backend::{ allocator::{Buffer, Fourcc, format::get_transparent}, @@ -506,6 +505,7 @@ smithay::render_elements! { pub WindowCaptureElement where R: ImportAll + ImportMem + AsGlowRenderer; WaylandElement=SurfaceRenderElement, CursorElement=RelocateRenderElement>, + DamageElement=DamageElement, } pub fn render_window_to_buffer( @@ -556,7 +556,7 @@ pub fn render_window_to_buffer( CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, { - let additional_damage_elements: Vec<_> = additional_damage + let mut elements: Vec<_> = additional_damage .into_iter() .filter_map(|rect| { let logical_rect = rect.to_logical( @@ -567,8 +567,8 @@ pub fn render_window_to_buffer( logical_rect.intersection(Rectangle::from_size(geometry.size)) }) .map(DamageElement::new) + .map(WindowCaptureElement::::from) .collect(); - dt.damage_output(age, &additional_damage_elements)?; let shell = common.shell.read(); let seat = shell.seats.last_active().clone(); @@ -589,8 +589,6 @@ pub fn render_window_to_buffer( }; std::mem::drop(shell); - let mut elements = Vec::new(); - if let Some(location) = location { if draw_cursor { cursor::draw_cursor( @@ -797,17 +795,16 @@ pub fn render_cursor_to_buffer( CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, { - let additional_damage_elements: Vec<_> = additional_damage + let mut elements: Vec<_> = additional_damage .into_iter() .filter_map(|rect| { let logical_rect = rect.to_logical(1, Transform::Normal, &Size::from((64, 64))); logical_rect.intersection(Rectangle::from_size((64, 64).into())) }) .map(DamageElement::new) + .map(WindowCaptureElement::from) .collect(); - dt.damage_output(age, &additional_damage_elements)?; - let mut elements = SmallVec::<[WindowCaptureElement; 4]>::new_const(); cursor::draw_cursor( renderer, seat, From 9d5971794cfa80b6dc9a9b8d2f114b5b6973e402 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 10 Mar 2026 17:16:48 +0100 Subject: [PATCH 09/16] DROP ME: Temporary config value for blur_strength --- cosmic-comp-config/src/lib.rs | 2 + src/backend/render/cursor.rs | 7 ++- src/backend/render/mod.rs | 35 +++++++---- src/backend/render/wayland/blur_effect.rs | 61 +++++++++++++++++-- src/backend/render/wayland/mod.rs | 8 ++- src/shell/element/stack.rs | 2 + src/shell/element/surface.rs | 5 ++ src/shell/element/window.rs | 13 ++-- src/shell/layout/tiling/mod.rs | 2 + src/shell/workspace.rs | 2 + src/utils/screenshot.rs | 1 + .../handlers/image_copy_capture/render.rs | 21 ++++--- 12 files changed, 128 insertions(+), 31 deletions(-) diff --git a/cosmic-comp-config/src/lib.rs b/cosmic-comp-config/src/lib.rs index 6063d4764..2c36658a6 100644 --- a/cosmic-comp-config/src/lib.rs +++ b/cosmic-comp-config/src/lib.rs @@ -54,6 +54,7 @@ pub struct AppearanceConfig { pub clip_floating_windows: bool, pub clip_tiled_windows: bool, pub shadow_tiled_windows: bool, + pub blur_strength: u8, } impl Default for AppearanceConfig { @@ -62,6 +63,7 @@ impl Default for AppearanceConfig { clip_floating_windows: true, clip_tiled_windows: true, shadow_tiled_windows: false, + blur_strength: 9, } } } diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index 0951ebda6..c219e1473 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -135,6 +135,7 @@ pub fn draw_surface_cursor( surface: &wl_surface::WlSurface, location: Point, scale: impl Into>, + blur_strength: usize, push: &mut dyn FnMut(CursorRenderElement, Point), ) where R: Renderer + ImportAll + AsGlowRenderer, @@ -161,6 +162,7 @@ pub fn draw_surface_cursor( 1.0, false, [0; 4], + blur_strength, Kind::Cursor, &mut |elem| push(elem.into(), h), None, @@ -173,6 +175,7 @@ pub fn draw_dnd_icon( surface: &wl_surface::WlSurface, location: Point, scale: impl Into>, + blur_strength: usize, push: &mut dyn FnMut(SurfaceRenderElement), ) where R: Renderer + ImportAll + AsGlowRenderer, @@ -194,6 +197,7 @@ pub fn draw_dnd_icon( 1.0, false, [0; 4], + blur_strength, FRAME_TIME_FILTER, push, None, @@ -272,6 +276,7 @@ pub fn draw_cursor( scale: Scale, buffer_scale: f64, time: Time, + blur_strength: usize, draw_default: bool, push: &mut dyn FnMut(CursorRenderElement, Point), ) where @@ -344,6 +349,6 @@ pub fn draw_cursor( hotspot.to_physical_precise_round(scale), ); } else if let CursorImageStatus::Surface(ref wl_surface) = cursor_status { - draw_surface_cursor(renderer, wl_surface, location, scale, push); + draw_surface_cursor(renderer, wl_surface, location, scale, blur_strength, push); } } diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index cead5677a..bd7aa4922 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -478,6 +478,7 @@ pub fn cursor_elements<'a, 'frame, R>( seats: impl Iterator>, zoom_state: Option<&ZoomState>, theme: &Theme, + blur_strength: usize, now: Time, output: &Output, mode: CursorMode, @@ -513,6 +514,7 @@ pub fn cursor_elements<'a, 'frame, R>( scale.into(), zoom_scale, now, + blur_strength, mode != CursorMode::NotDefault, &mut |elem, hotspot| { push(CosmicElement::Cursor(RescaleRenderElement::from_element( @@ -537,6 +539,7 @@ pub fn cursor_elements<'a, 'frame, R>( &dnd_icon.surface, (location + dnd_icon.offset.to_f64()).to_i32_round(), scale, + blur_strength, &mut |elem| push(CosmicElement::Dnd(elem)), ); } @@ -719,6 +722,7 @@ where return Ok(Vec::new()); } let theme = shell_ref.theme().clone(); + let blur_strength = shell_ref.appearance_config().blur_strength as usize; let scale = output.current_scale().fractional_scale(); // we don't want to hold a shell lock across `cursor_elements`, // that is prone to deadlock with the main-thread on some grabs. @@ -729,6 +733,7 @@ where seats.iter(), zoom_level, &theme, + blur_strength, now, output, cursor_mode, @@ -840,6 +845,7 @@ where 1.0, false, [0; 4], + blur_strength, FRAME_TIME_FILTER, &mut |elem| { elements.extend( @@ -870,6 +876,7 @@ where 1.0, false, [0; 4], + blur_strength, FRAME_TIME_FILTER, &mut |elem| { elements.extend( @@ -897,6 +904,7 @@ where 1.0, false, [0; 4], + blur_strength, FRAME_TIME_FILTER, &mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)), None, @@ -1040,6 +1048,7 @@ fn session_lock_elements( 1.0, false, [0; 4], + 0, FRAME_TIME_FILTER, push, None, @@ -1499,7 +1508,19 @@ where CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { - let elements: Vec> = workspace_elements( + let mut elements: Vec> = if let Some(additional_damage) = additional_damage { + let output_geo = output.geometry().to_local(output).as_logical(); + additional_damage + .into_iter() + .filter_map(|rect| rect.intersection(output_geo)) + .map(DamageElement::new) + .map(CosmicElement::from) + .collect() + } else { + Vec::new() + }; + + elements.extend(workspace_elements( gpu, renderer, shell, @@ -1510,17 +1531,7 @@ where current, cursor_mode, element_filter, - )?; - - if let Some(additional_damage) = additional_damage { - let output_geo = output.geometry().to_local(output).as_logical(); - let additional_damage_elements: Vec<_> = additional_damage - .into_iter() - .filter_map(|rect| rect.intersection(output_geo)) - .map(DamageElement::new) - .collect(); - damage_tracker.damage_output(age, &additional_damage_elements)?; - } + )?); let res = damage_tracker.render_output( renderer, diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index 895cd19e1..2a0be977a 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -1,7 +1,7 @@ use std::{ borrow::{Borrow, BorrowMut}, sync::{ - Arc, Mutex, + Arc, LazyLock, Mutex, atomic::{AtomicBool, Ordering}, }, }; @@ -27,6 +27,7 @@ use smithay::{ }, wayland::compositor::SurfaceData, }; +use tracing::info; use crate::{ backend::render::{element::AsGlowRenderer, wayland::clipped_surface::ClippingShader}, @@ -36,9 +37,50 @@ use crate::{ pub static BLUR_DOWNSAMPLE_SHADER: &str = include_str!("../shaders/blur_downsample.frag"); pub static BLUR_UPSAMPLE_SHADER: &str = include_str!("../shaders/blur_upsample.frag"); -const PASSES: usize = 3; -const OFFSET: f64 = 1.1; const NOISE: f32 = 0.03; +const MAX_STEPS: usize = 15; + +#[derive(Debug, Clone, Copy, PartialEq)] +struct BlurParameters { + passes: usize, + offset: f64, + extended_radius: i32, +} + +static BLUR_PARAMS: LazyLock> = LazyLock::new(|| { + let mut params = Vec::new(); + + let mut remaining_steps = MAX_STEPS as isize; + let offsets = [ + // min offset, max offset, extended radius to avoid artifacts + (1.0, 2.0, 10), + (2.0, 3.0, 20), + (2.0, 5.0, 50), + (3.0, 8.0, 150), + ]; + + let sum = offsets.iter().map(|(min, max, _)| *max - *min).sum::(); + for (i, (min, max, extended_radius)) in offsets.into_iter().enumerate() { + let mut iter_num = f64::ceil((max - min) / sum * (MAX_STEPS as f64)) as usize; + remaining_steps -= iter_num as isize; + + if remaining_steps < 0 { + iter_num = iter_num.saturating_add_signed(remaining_steps); + } + + let diff = max - min; + for j in 1..=iter_num { + params.push(BlurParameters { + passes: i + 1, + offset: min + (diff / iter_num as f64) * j as f64, + extended_radius, + }); + } + } + + info!("Computed blur values: {:#?}", ¶ms); + params +}); #[derive(Debug, Clone)] pub struct BlurShaders { @@ -125,6 +167,7 @@ impl BlurElement { geometry: Rectangle, output_scale: f64, radii: [u8; 4], + strength: usize, ) -> Result, R::Error> { let mut blur_region_state = states.cached_state.get::(); let Some(region) = blur_region_state.current().blur_region.as_ref() else { @@ -133,7 +176,7 @@ impl BlurElement { let geo = geometry.to_physical_precise_round(output_scale); let mut extended_geo = geo; - let radius = OFFSET * 2.0f64.powf(PASSES as f64); + let radius = BLUR_PARAMS[strength.min(MAX_STEPS - 1)].extended_radius as f64; extended_geo.loc -= Point::::new(radius, radius); extended_geo.size += Size::::new(radius, radius).upscale(2.); @@ -187,6 +230,7 @@ impl BlurElement { output_scale, &mut *state.lock().unwrap(), uniforms, + strength, )?)) } @@ -197,19 +241,24 @@ impl BlurElement { output_scale: f64, state: &mut BlurState, uniforms: Vec>, + strength: usize, ) -> Result { let renderer_id = renderer.glow_renderer().context_id(); let src = geometry.size.to_buffer(output_scale, Transform::Normal); + let params = &BLUR_PARAMS[strength.min(MAX_STEPS - 1)]; let dirty = !(state .renderer_id .as_ref() .is_some_and(|id| id == &renderer_id) + && state.offset == params.offset + && state.passes == params.passes && &state.region == region && state.src == src); + state.renderer_id = Some(renderer_id); - state.offset = OFFSET * output_scale; // we want the blur result to be stable across scaling; - state.passes = PASSES; + state.offset = params.offset; + state.passes = params.passes; state.region = region.clone(); state.src = src; if dirty { diff --git a/src/backend/render/wayland/mod.rs b/src/backend/render/wayland/mod.rs index b9e07a800..9ac1109ca 100644 --- a/src/backend/render/wayland/mod.rs +++ b/src/backend/render/wayland/mod.rs @@ -35,6 +35,7 @@ pub fn push_render_elements_from_surface_tree( alpha: f32, should_clip: bool, radii: [u8; 4], + blur_strength: usize, kind: impl Into, push_above: &mut dyn FnMut(SurfaceRenderElement), mut push_below: Option<&mut dyn FnMut(SurfaceRenderElement)>, @@ -87,7 +88,12 @@ pub fn push_render_elements_from_surface_tree( ) { Ok(Some(surface)) => { blur = BlurElement::from_surface( - renderer, states, geometry, scale.x, radii, + renderer, + states, + geometry, + scale.x, + radii, + blur_strength, ); let elem: SurfaceRenderElement = if radii.iter().any(|r| *r != 0) && should_clip diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 25e27a7d3..e0962a34e 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -648,6 +648,7 @@ impl CosmicStack { window_loc, scale, alpha, + p.appearance_conf.lock().unwrap().blur_strength as usize, &mut |elem| push(elem.into()), ) }) @@ -803,6 +804,7 @@ impl CosmicStack { scanout_override, radii.is_some(), radii.unwrap_or([0; 4]), + appearance.blur_strength as usize, &mut |elem| push_above(elem.into()), Some(&mut |elem| push_below(elem.into())), ); diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index cd599f0c1..687aa4585 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -766,6 +766,7 @@ impl CosmicSurface { location: Point, scale: Scale, alpha: f32, + blur_strength: usize, push: &mut dyn FnMut(SurfaceRenderElement), ) where R: Renderer + ImportAll + AsGlowRenderer, @@ -789,6 +790,7 @@ impl CosmicSurface { alpha, false, [0; 4], + blur_strength, FRAME_TIME_FILTER, push, None, @@ -808,6 +810,7 @@ impl CosmicSurface { scanout_override: Option, should_clip: bool, radii: [u8; 4], + blur_strength: usize, push_above: &mut dyn FnMut(SurfaceRenderElement), push_below: Option<&mut dyn FnMut(SurfaceRenderElement)>, ) where @@ -830,6 +833,7 @@ impl CosmicSurface { alpha, should_clip, radii, + blur_strength, scanout_override .map(|val| { if val { @@ -858,6 +862,7 @@ impl CosmicSurface { alpha, should_clip, radii, + blur_strength, scanout_override .map(|val| { if val { diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 6fbf65911..0df6988ea 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -344,10 +344,14 @@ impl CosmicWindow { }; self.0.with_program(|p| { - p.window - .push_popup_render_elements(renderer, window_loc, scale, alpha, &mut |elem| { - push(elem.into()) - }) + p.window.push_popup_render_elements( + renderer, + window_loc, + scale, + alpha, + p.appearance_conf.lock().unwrap().blur_strength as usize, + &mut |elem| push(elem.into()), + ) }) } @@ -525,6 +529,7 @@ impl CosmicWindow { scanout_override, clip, radii, + appearance.blur_strength as usize, &mut |elem| push_above(elem.into()), Some(&mut |elem| push_below(elem.into())), ) diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 066b7fe9c..73cf2b516 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -5382,6 +5382,8 @@ fn render_new_tree_windows( None, false, [0, 0, 0, 0], + // TODO + 0, &mut |elem| { swap_elements.push(CosmicMappedRenderElement::GrabbedWindow( RescaleRenderElement::from_element( diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 8e6d1e939..e28fcd617 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1627,6 +1627,7 @@ impl Workspace { Some(true), false, [0, 0, 0, 0], + 0, &mut fullscreen_push, None, ); @@ -1791,6 +1792,7 @@ impl Workspace { render_loc, output_scale.into(), alpha, + 0, &mut |elem| push(WorkspaceRenderElement::FullscreenPopup(elem.into())), ); } diff --git a/src/utils/screenshot.rs b/src/utils/screenshot.rs index 399c5720f..fbcd1bd78 100644 --- a/src/utils/screenshot.rs +++ b/src/utils/screenshot.rs @@ -36,6 +36,7 @@ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) { None, false, [0; 4], + 0, &mut |elem| elements.push(elem), None, ); diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index bc0891e08..e114122c3 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -571,6 +571,7 @@ pub fn render_window_to_buffer( .collect(); let shell = common.shell.read(); + let blur_strength = shell.appearance_config().blur_strength as usize; let seat = shell.seats.last_active().clone(); let pointer = seat.get_pointer().unwrap(); let pointer_loc = pointer.current_location().to_i32_round().as_global(); @@ -598,6 +599,7 @@ pub fn render_window_to_buffer( 1.0.into(), 1.0, common.clock.now(), + blur_strength, true, &mut |elem, hotspot| { elements.push(WindowCaptureElement::CursorElement( @@ -619,15 +621,18 @@ pub fn render_window_to_buffer( &dnd_icon.surface, (location + dnd_icon.offset.to_f64()).to_i32_round(), 1.0, + blur_strength, &mut |elem| { - elements.push( - RelocateRenderElement::from_element( - CursorRenderElement::Surface(elem), - Point::new(0, 0), - Relocate::Relative, + elements + .push( + RelocateRenderElement::from_element( + CursorRenderElement::Surface(elem), + Point::new(0, 0), + Relocate::Relative, + ) + .into(), ) - .into(), - ) + .into() }, ); } @@ -641,6 +646,7 @@ pub fn render_window_to_buffer( None, false, [0; 4], + blur_strength, &mut |elem| elements.push(elem.into()), None, ); @@ -812,6 +818,7 @@ pub fn render_cursor_to_buffer( 1.0.into(), 1.0, common.clock.now(), + 0, true, &mut |elem, _| { elements.push( From 5c3cbedc583bd182700eb166e30005f71bd35296 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Mon, 30 Mar 2026 14:15:08 +0200 Subject: [PATCH 10/16] shell: Fix fullscreen deadlock --- src/shell/workspace.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index e28fcd617..3c6e5578b 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1597,15 +1597,15 @@ impl Workspace { .as_logical() .to_physical_precise_round(output_scale); + let fullscreen_geo = fullscreen.surface.0.geometry(); + let scale = Scale { + x: target_geo.size.w as f64 / fullscreen_geo.size.w as f64, + y: target_geo.size.h as f64 / fullscreen_geo.size.h as f64, + }; + // Only rescale geometry when animating let animation_rescale = |elem| { if fullscreen.is_animating() { - let fullscreen_geo = fullscreen.surface.0.geometry(); - let scale = Scale { - x: target_geo.size.w as f64 / fullscreen_geo.size.w as f64, - y: target_geo.size.h as f64 / fullscreen_geo.size.h as f64, - }; - RescaleRenderElement::from_element(elem, render_loc, scale).into() } else { Into::>::into(elem) From 8ded84e0a597fe25d0f68419f115e41478296f7b Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Mon, 30 Mar 2026 14:17:22 +0200 Subject: [PATCH 11/16] chore: Fix clippy lints --- src/backend/render/wayland/blur_effect.rs | 6 +++--- .../handlers/image_copy_capture/render.rs | 16 +++++++--------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index 2a0be977a..a2937b7b4 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -226,9 +226,9 @@ impl BlurElement { Ok(Some(Self::from_state( renderer, extended_geo.to_logical(output_scale), - ®ion, + region, output_scale, - &mut *state.lock().unwrap(), + &mut state.lock().unwrap(), uniforms, strength, )?)) @@ -502,7 +502,7 @@ fn render_blur( let adjusted_tex_size = tex_size.downscale(1 << i); let target_tex_size = tex_size - .downscale(1 << i + 1) + .downscale(1 << (i + 1)) .to_logical(1, Transform::Normal) .to_physical(1); let half_pixel = [ diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index e114122c3..3f4aac975 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -623,16 +623,14 @@ pub fn render_window_to_buffer( 1.0, blur_strength, &mut |elem| { - elements - .push( - RelocateRenderElement::from_element( - CursorRenderElement::Surface(elem), - Point::new(0, 0), - Relocate::Relative, - ) - .into(), + elements.push( + RelocateRenderElement::from_element( + CursorRenderElement::Surface(elem), + Point::new(0, 0), + Relocate::Relative, ) - .into() + .into(), + ) }, ); } From 6121d03a42ca1c917f3622dff9d703347015754a Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 22 Jan 2026 15:42:22 -0800 Subject: [PATCH 12/16] WIP smithay branch --- Cargo.lock | 74 +++++++++++++++++------------------ Cargo.toml | 3 +- cosmic-comp-config/Cargo.toml | 2 +- src/backend/kms/mod.rs | 2 +- src/config/input_config.rs | 9 ++++- src/config/mod.rs | 2 +- 6 files changed, 49 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 165360725..697817968 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1230,7 +1230,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1252,9 +1252,9 @@ dependencies = [ [[package]] name = "dlib" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" dependencies = [ "libloading", ] @@ -1540,7 +1540,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2645,9 +2645,9 @@ dependencies = [ [[package]] name = "input" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbdc09524a91f9cacd26f16734ff63d7dc650daffadd2b6f84d17a285bd875a9" +checksum = "f9793345a65d71317763a33066b5d8351f8760dde8d4930fe9e39b5f14a7959d" dependencies = [ "bitflags 2.11.0", "input-sys", @@ -2658,9 +2658,9 @@ dependencies = [ [[package]] name = "input-sys" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd4f5b4d1c00331c5245163aacfe5f20be75b564c7112d45893d4ae038119eb0" +checksum = "36eee07d8e02bd95bf52b2e642cf13d33701b94c6e4b04fbf1d1fb07e9cb19e7" [[package]] name = "intl-memoizer" @@ -2710,7 +2710,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2823,7 +2823,7 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "770919970f7d2f74fea948900d35e2ef64f44129e8ae4015f59de1f0aca7c2a5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3433,7 +3433,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -4260,9 +4260,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.38.4" +version = "0.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" dependencies = [ "memchr", ] @@ -4629,7 +4629,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4924,7 +4924,7 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=f6d1070#f6d10709db2bb68c46b9c128a4163d035356572c" +source = "git+https://github.com/ids1024/smithay.git?branch=touch-grab#54701ec26b79513ffed42aebc8abcea214da9cfa" dependencies = [ "aliasable", "appendlist", @@ -5254,7 +5254,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5970,9 +5970,9 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.12" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee64194ccd96bf648f42a65a7e589547096dfa702f7cadef84347b66ad164f9" +checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" dependencies = [ "cc", "downcast-rs", @@ -5984,9 +5984,9 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.12" +version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e6faa537fbb6c186cb9f1d41f2f811a4120d1b57ec61f50da451a0c5122bec" +checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" dependencies = [ "bitflags 2.11.0", "rustix 1.1.4", @@ -6007,9 +6007,9 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.31.12" +version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5864c4b5b6064b06b1e8b74ead4a98a6c45a285fe7a0e784d24735f011fdb078" +checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d" dependencies = [ "rustix 1.1.4", "wayland-client", @@ -6018,9 +6018,9 @@ dependencies = [ [[package]] name = "wayland-egl" -version = "0.32.9" +version = "0.32.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2c9ef35f3b2e11d5709d8e75dc1e7e064fd8eec5d705d56073fafca85de854" +checksum = "9b97bdb7c49e5bd9b7562f38ff84f0dad47079fdc9e926f691a787f6dbc05451" dependencies = [ "wayland-backend", "wayland-sys", @@ -6028,9 +6028,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.10" +version = "0.32.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baeda9ffbcfc8cd6ddaade385eaf2393bd2115a69523c735f12242353c3df4f3" +checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" dependencies = [ "bitflags 2.11.0", "wayland-backend", @@ -6054,9 +6054,9 @@ dependencies = [ [[package]] name = "wayland-protocols-misc" -version = "0.3.10" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791c58fdeec5406aa37169dd815327d1e47f334219b523444bc26d70ceb4c34e" +checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" dependencies = [ "bitflags 2.11.0", "wayland-backend", @@ -6081,9 +6081,9 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.10" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9597cdf02cf0c34cd5823786dce6b5ae8598f05c2daf5621b6e178d4f7345f3" +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ "bitflags 2.11.0", "wayland-backend", @@ -6095,9 +6095,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.8" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5423e94b6a63e68e439803a3e153a9252d5ead12fd853334e2ad33997e3889e3" +checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" dependencies = [ "proc-macro2", "quick-xml", @@ -6106,9 +6106,9 @@ dependencies = [ [[package]] name = "wayland-server" -version = "0.31.11" +version = "0.31.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9297ab90f8d1f597711d36455c5b1b2290eca59b8134485e377a296b80b118c9" +checksum = "cc1846eb04c49182e04f4a099e2a830a2b745610bbc1d61246e206f29c7000a0" dependencies = [ "bitflags 2.11.0", "downcast-rs", @@ -6119,9 +6119,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.8" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6dbfc3ac5ef974c92a2235805cc0114033018ae1290a72e474aa8b28cbbdfd" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" dependencies = [ "dlib", "log", @@ -6273,7 +6273,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 08c158c0c..6ae60816f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -143,4 +143,5 @@ cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch cosmic-client-toolkit = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } [patch.crates-io] -smithay = { git = "https://github.com/smithay/smithay.git", rev = "f6d1070" } +# smithay = { git = "https://github.com/smithay/smithay.git", rev = "f6d1070" } +smithay = { git = "https://github.com/ids1024/smithay.git", branch = "touch-grab" } diff --git a/cosmic-comp-config/Cargo.toml b/cosmic-comp-config/Cargo.toml index 66cdbfff7..f01a4bb39 100644 --- a/cosmic-comp-config/Cargo.toml +++ b/cosmic-comp-config/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] cosmic-config = { git = "https://github.com/pop-os/libcosmic/" } cosmic-randr-shell = { git = "https://github.com/pop-os/cosmic-randr/", optional = true } -input = "0.9.1" +input = "0.10.0" libdisplay-info = { version = "0.3.0", optional = true } serde = { version = "1", features = ["derive"] } ron = { version = "0.12", optional = true } diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index eb3a25f16..b97ec07c8 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -200,7 +200,7 @@ fn init_libinput( .input_devices .insert(device.name().into(), device.clone()); } else if let InputEvent::DeviceRemoved { device } = &event { - state.backend.kms().input_devices.remove(device.name()); + state.backend.kms().input_devices.remove(&*device.name()); } state.process_input_event(event); diff --git a/src/config/input_config.rs b/src/config/input_config.rs index 679d6825d..2ad4a3f92 100644 --- a/src/config/input_config.rs +++ b/src/config/input_config.rs @@ -1,5 +1,5 @@ use smithay::reexports::input::{ - Device as InputDevice, DeviceConfigError, ScrollMethod, SendEventsMode, + Device as InputDevice, DeviceConfigError, DragLockState, ScrollMethod, SendEventsMode, }; use tracing::warn; @@ -210,7 +210,12 @@ pub fn update_device( if let Err(err) = device.config_tap_set_drag_enabled(tap.drag) { config_set_error(device, "tap-drag", tap.drag, err, is_default); } - if let Err(err) = device.config_tap_set_drag_lock_enabled(tap.drag_lock) { + let drag_lock = if tap.drag_lock { + DragLockState::EnabledTimeout // XXX ? + } else { + DragLockState::Disabled + }; + if let Err(err) = device.config_tap_set_drag_lock_enabled(drag_lock) { config_set_error(device, "tap-drag-lock", tap.drag_lock, err, is_default); } } diff --git a/src/config/mod.rs b/src/config/mod.rs index 59cc69f49..d58782872 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -646,7 +646,7 @@ impl Config { &self.cosmic_conf.input_default }; - let mut device_config = self.cosmic_conf.input_devices.get(device.name()).cloned(); + let mut device_config = self.cosmic_conf.input_devices.get(&*device.name()).cloned(); if is_touchpad && self.cosmic_conf.input_touchpad_override == TouchpadOverride::ForceDisable { device_config = Some({ From 493585a8683d96e82d90d85f9063d377dbffb0df Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 22 Jan 2026 15:49:14 -0800 Subject: [PATCH 13/16] input: Set keyboard focus on touch down event --- src/input/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/input/mod.rs b/src/input/mod.rs index 50713eb48..659db1158 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1271,6 +1271,12 @@ impl State { time: event.time_msec(), }, ); + + let shell = self.common.shell.write(); + if let Some(target) = State::element_under(position, &output, &shell, &seat) { + drop(shell); + Shell::set_focus(self, Some(&target), &seat, Some(serial), false); + } } } InputEvent::TouchMotion { event, .. } => { From 257db57944a3fc1af75fa46f477c11d8e7dbb0c1 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 22 Jan 2026 15:44:02 -0800 Subject: [PATCH 14/16] wayland/xdg_shell: Use touch popup grab --- src/wayland/handlers/xdg_shell/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index 75ca1ab95..564572c44 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -8,7 +8,7 @@ use smithay::desktop::layer_map_for_output; use smithay::{ delegate_xdg_shell, desktop::{ - PopupGrab, PopupKeyboardGrab, PopupKind, PopupPointerGrab, PopupUngrabStrategy, + PopupGrab, PopupKeyboardGrab, PopupKind, PopupPointerGrab, PopupTouchGrab, PopupUngrabStrategy, WindowSurfaceType, find_popup_root_surface, }, input::{Seat, pointer::Focus}, @@ -137,6 +137,10 @@ impl XdgShellHandler for State { pointer.set_grab(self, PopupPointerGrab::new(&grab), serial, Focus::Keep); } + if let Some(touch) = seat.get_touch() { + touch.set_grab(self, PopupTouchGrab::new(&grab), serial); + } + seat.user_data() .insert_if_missing(|| PopupGrabData::new(None)); seat.user_data() From 2b4d5367ba6b7e8d7d71a1df903d3e9b2ea39c85 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 9 Apr 2026 19:17:15 -0700 Subject: [PATCH 15/16] WIP unset handle arg --- src/shell/grabs/delay.rs | 4 ++-- src/shell/grabs/menu/mod.rs | 4 ++-- src/shell/grabs/mod.rs | 24 +++++++++++------------ src/shell/grabs/moving.rs | 4 ++-- src/shell/layout/floating/grabs/resize.rs | 4 ++-- src/shell/layout/tiling/grabs/resize.rs | 4 ++-- src/shell/layout/tiling/grabs/swap.rs | 2 +- src/wayland/handlers/xdg_shell/mod.rs | 4 ++-- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/shell/grabs/delay.rs b/src/shell/grabs/delay.rs index dbf98d911..0bd4591d2 100644 --- a/src/shell/grabs/delay.rs +++ b/src/shell/grabs/delay.rs @@ -192,7 +192,7 @@ impl> PointerGrab for DelayGrab { } } - fn unset(&mut self, _data: &mut State) {} + fn unset(&mut self, _data: &mut State, _handle: &mut PointerInnerHandle<'_, State>) {} } impl> TouchGrab for DelayGrab { @@ -281,5 +281,5 @@ impl> TouchGrab for DelayGrab { } } - fn unset(&mut self, _data: &mut State) {} + fn unset(&mut self, _data: &mut State, _handle: &mut TouchInnerHandle<'_, State>) {} } diff --git a/src/shell/grabs/menu/mod.rs b/src/shell/grabs/menu/mod.rs index 1292510b2..e87e622e8 100644 --- a/src/shell/grabs/menu/mod.rs +++ b/src/shell/grabs/menu/mod.rs @@ -704,7 +704,7 @@ impl PointerGrab for MenuGrab { } } - fn unset(&mut self, _data: &mut State) {} + fn unset(&mut self, _data: &mut State, _handle: &mut PointerInnerHandle<'_, State>) {} } impl TouchGrab for MenuGrab { @@ -839,7 +839,7 @@ impl TouchGrab for MenuGrab { } } - fn unset(&mut self, _data: &mut State) {} + fn unset(&mut self, _data: &mut State, _handle: &mut TouchInnerHandle<'_, State>) {} } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/src/shell/grabs/mod.rs b/src/shell/grabs/mod.rs index 0c5448c19..b0deba8b2 100644 --- a/src/shell/grabs/mod.rs +++ b/src/shell/grabs/mod.rs @@ -372,10 +372,10 @@ impl PointerGrab for ResizeGrab { } } - fn unset(&mut self, data: &mut State) { + fn unset(&mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>) { match self { - ResizeGrab::Floating(grab) => PointerGrab::unset(grab, data), - ResizeGrab::Tiling(grab) => PointerGrab::unset(grab, data), + ResizeGrab::Floating(grab) => PointerGrab::unset(grab, data, handle), + ResizeGrab::Tiling(grab) => PointerGrab::unset(grab, data, handle), } } } @@ -469,10 +469,10 @@ impl TouchGrab for ResizeGrab { } } - fn unset(&mut self, data: &mut State) { + fn unset(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { match self { - ResizeGrab::Floating(grab) => TouchGrab::unset(grab, data), - ResizeGrab::Tiling(grab) => TouchGrab::unset(grab, data), + ResizeGrab::Floating(grab) => TouchGrab::unset(grab, data, handle), + ResizeGrab::Tiling(grab) => TouchGrab::unset(grab, data, handle), } } } @@ -714,10 +714,10 @@ impl PointerGrab for MoveGrab { } } - fn unset(&mut self, data: &mut State) { + fn unset(&mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>) { match self { - MoveGrab::Move(grab) => PointerGrab::unset(grab, data), - MoveGrab::Delayed(grab) => PointerGrab::unset(grab, data), + MoveGrab::Move(grab) => PointerGrab::unset(grab, data, handle), + MoveGrab::Delayed(grab) => PointerGrab::unset(grab, data, handle), } } } @@ -811,10 +811,10 @@ impl TouchGrab for MoveGrab { } } - fn unset(&mut self, data: &mut State) { + fn unset(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) { match self { - MoveGrab::Move(grab) => TouchGrab::unset(grab, data), - MoveGrab::Delayed(grab) => TouchGrab::unset(grab, data), + MoveGrab::Move(grab) => TouchGrab::unset(grab, data, handle), + MoveGrab::Delayed(grab) => TouchGrab::unset(grab, data, handle), } } } diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index 81e4c38fe..d7b63ffcd 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -638,7 +638,7 @@ impl PointerGrab for MoveGrab { } } - fn unset(&mut self, _data: &mut State) {} + fn unset(&mut self, _data: &mut State, _handle: &mut PointerInnerHandle<'_, State>) {} } impl TouchGrab for MoveGrab { @@ -717,7 +717,7 @@ impl TouchGrab for MoveGrab { } } - fn unset(&mut self, _data: &mut State) {} + fn unset(&mut self, _data: &mut State, _handle: &mut TouchInnerHandle<'_, State>) {} } impl MoveGrab { diff --git a/src/shell/layout/floating/grabs/resize.rs b/src/shell/layout/floating/grabs/resize.rs index 043632074..89149079a 100644 --- a/src/shell/layout/floating/grabs/resize.rs +++ b/src/shell/layout/floating/grabs/resize.rs @@ -318,7 +318,7 @@ impl PointerGrab for ResizeSurfaceGrab { } } - fn unset(&mut self, _data: &mut State) { + fn unset(&mut self, _data: &mut State, _handle: &mut PointerInnerHandle<'_, State>) { self.ungrab(); } } @@ -401,7 +401,7 @@ impl TouchGrab for ResizeSurfaceGrab { } } - fn unset(&mut self, _data: &mut State) { + fn unset(&mut self, _data: &mut State, _handle: &mut TouchInnerHandle<'_, State>) { self.ungrab(); } } diff --git a/src/shell/layout/tiling/grabs/resize.rs b/src/shell/layout/tiling/grabs/resize.rs index 51d5c9a2f..e6625bdd9 100644 --- a/src/shell/layout/tiling/grabs/resize.rs +++ b/src/shell/layout/tiling/grabs/resize.rs @@ -496,7 +496,7 @@ impl PointerGrab for ResizeForkGrab { } } - fn unset(&mut self, data: &mut State) { + fn unset(&mut self, data: &mut State, _handle: &mut PointerInnerHandle<'_, State>) { self.update_location(data, self.last_loc.as_logical(), true); } } @@ -579,7 +579,7 @@ impl TouchGrab for ResizeForkGrab { handle.orientation(data, event, seq) } - fn unset(&mut self, data: &mut State) { + fn unset(&mut self, data: &mut State, _handle: &mut TouchInnerHandle<'_, State>) { self.update_location(data, self.last_loc.as_logical(), true); } } diff --git a/src/shell/layout/tiling/grabs/swap.rs b/src/shell/layout/tiling/grabs/swap.rs index 4c22209f7..dec808987 100644 --- a/src/shell/layout/tiling/grabs/swap.rs +++ b/src/shell/layout/tiling/grabs/swap.rs @@ -103,5 +103,5 @@ impl KeyboardGrab for SwapWindowGrab { &KeyboardGrabStartData { focus: None } } - fn unset(&mut self, _state: &mut State) {} + fn unset(&mut self, _state: &mut State, _handle: &mut KeyboardInnerHandle<'_, State>) {} } diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index 564572c44..c37b10355 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -8,8 +8,8 @@ use smithay::desktop::layer_map_for_output; use smithay::{ delegate_xdg_shell, desktop::{ - PopupGrab, PopupKeyboardGrab, PopupKind, PopupPointerGrab, PopupTouchGrab, PopupUngrabStrategy, - WindowSurfaceType, find_popup_root_surface, + PopupGrab, PopupKeyboardGrab, PopupKind, PopupPointerGrab, PopupTouchGrab, + PopupUngrabStrategy, WindowSurfaceType, find_popup_root_surface, }, input::{Seat, pointer::Focus}, output::Output, From 5f18a9287109d479bf157a64e0acc215b24161ce Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 9 Apr 2026 19:17:37 -0700 Subject: [PATCH 16/16] WIP smithay commit --- Cargo.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 697817968..6f8ef64ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1230,7 +1230,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1540,7 +1540,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2710,7 +2710,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2823,7 +2823,7 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "770919970f7d2f74fea948900d35e2ef64f44129e8ae4015f59de1f0aca7c2a5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3433,7 +3433,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4629,7 +4629,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4924,7 +4924,7 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/ids1024/smithay.git?branch=touch-grab#54701ec26b79513ffed42aebc8abcea214da9cfa" +source = "git+https://github.com/ids1024/smithay.git?branch=touch-grab#fc065c7679989539133dbee855ba3abe0d26ec04" dependencies = [ "aliasable", "appendlist", @@ -5254,7 +5254,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6273,7 +6273,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]]