diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index a5d7060a..fd4c7654 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -136,6 +136,7 @@ pub enum Usage { FocusIndicator, PotentialGroupIndicator, SnappingIndicator, + Border, } #[derive(Clone)] diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 96144717..b6d8df2e 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -162,6 +162,7 @@ impl PartialEq for CosmicMappedKey { } } } +impl Eq for CosmicMappedKey {} impl PartialEq for CosmicMapped { fn eq(&self, other: &Self) -> bool { @@ -311,13 +312,10 @@ impl CosmicMapped { } pub fn set_tiled(&self, tiled: bool) { - if let Some(window) = match &self.element { - // we use the tiled state of stack windows anyway to get rid of decorations - CosmicMappedInternal::Stack(_) => None, - CosmicMappedInternal::Window(w) => Some(w.surface()), + match &self.element { + CosmicMappedInternal::Stack(s) => s.set_tiled(tiled), + CosmicMappedInternal::Window(w) => w.set_tiled(tiled), _ => unreachable!(), - } { - window.set_tiled(tiled) } } @@ -594,6 +592,34 @@ impl CosmicMapped { .collect() } + pub fn shadow_render_element( + &self, + renderer: &mut R, + location: smithay::utils::Point, + scale: smithay::utils::Scale, + alpha: f32, + ) -> Option + where + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + R::TextureId: Send + Clone + 'static, + CosmicMappedRenderElement: RenderElement, + C: From>, + { + match &self.element { + CosmicMappedInternal::Stack(s) => s + .shadow_render_element::>( + renderer, location, scale, alpha, + ) + .map(Into::into), + CosmicMappedInternal::Window(w) => w + .shadow_render_element::>( + renderer, location, scale, alpha, + ) + .map(Into::into), + _ => unreachable!(), + } + } + pub fn render_elements( &self, renderer: &mut R, @@ -852,9 +878,7 @@ impl CosmicMapped { pub fn corner_radius(&self, geometry_size: Size, default_radius: u8) -> [u8; 4] { match &self.element { - CosmicMappedInternal::Window(w) => w - .corner_radius(geometry_size) - .unwrap_or([default_radius; 4]), + CosmicMappedInternal::Window(w) => w.corner_radius(geometry_size, default_radius), CosmicMappedInternal::Stack(s) => s.corner_radius(geometry_size, default_radius), _ => unreachable!(), } @@ -1018,7 +1042,7 @@ impl From for CosmicMapped { pub enum CosmicMappedRenderElement where - R: Renderer + ImportAll + ImportMem, + R: Renderer + AsGlowRenderer + ImportAll + ImportMem, R::TextureId: 'static, { Stack(self::stack::CosmicStackRenderElement), @@ -1053,7 +1077,7 @@ where impl Element for CosmicMappedRenderElement where - R: Renderer + ImportAll + ImportMem, + R: Renderer + AsGlowRenderer + ImportAll + ImportMem, R::TextureId: 'static, { fn id(&self) -> &smithay::backend::renderer::element::Id { diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 4be26878..bb7f784e 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -3,9 +3,16 @@ use super::{ window::{Focus, RESIZE_BORDER}, }; use crate::{ - backend::render::cursor::CursorState, + backend::render::{ + IndicatorShader, Key, Usage, + clipped_surface::ClippedSurfaceRenderElement, + cursor::CursorState, + element::{AsGlowRenderer, FromGlesError}, + shadow::ShadowShader, + }, hooks::{Decorations, HOOKS}, shell::{ + element::{CosmicMappedKey, CosmicMappedKeyInner}, focus::target::PointerFocusTarget, grabs::{ReleaseMode, ResizeEdge}, layout::tiling::NodeDesc, @@ -34,9 +41,13 @@ use smithay::{ renderer::{ ImportAll, ImportMem, Renderer, element::{ - AsRenderElements, memory::MemoryRenderBufferRenderElement, + AsRenderElements, Element, Id as RendererId, Kind, RenderElement, + UnderlyingStorage, memory::MemoryRenderBufferRenderElement, surface::WaylandSurfaceRenderElement, }, + gles::element::PixelShaderElement, + glow::GlowRenderer, + utils::{CommitCounter, DamageSet, OpaqueRegions}, }, }, desktop::{WindowSurfaceType, space::SpaceElement}, @@ -56,8 +67,7 @@ use smithay::{ }, output::Output, reexports::wayland_server::protocol::wl_surface::WlSurface, - render_elements, - utils::{Buffer, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size}, + utils::{Buffer, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform}, wayland::seat::WaylandFocus, }; use std::{ @@ -65,7 +75,7 @@ use std::{ fmt, hash::Hash, sync::{ - LazyLock, Mutex, + Arc, LazyLock, Mutex, atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering}, }, }; @@ -107,6 +117,8 @@ pub struct CosmicStackInternal { override_alive: AtomicBool, geometry: Mutex>>, mask: Mutex>, + tiled: AtomicBool, + theme: Mutex, appearance_conf: Mutex, } @@ -162,6 +174,8 @@ impl CosmicStack { override_alive: AtomicBool::new(true), geometry: Mutex::new(None), mask: Mutex::new(None), + tiled: AtomicBool::new(false), + theme: Mutex::new(theme.clone()), appearance_conf: Mutex::new(appearance), }, (width, TAB_HEIGHT), @@ -479,6 +493,11 @@ impl CosmicStack { self.0.force_redraw() } + pub fn set_tiled(&self, tiled: bool) { + self.0 + .with_program(|p| p.tiled.store(tiled, Ordering::Release)); + } + pub fn surfaces(&self) -> impl Iterator { self.0.with_program(|p| { p.windows @@ -618,7 +637,7 @@ impl CosmicStack { alpha: f32, ) -> Vec where - R: Renderer + ImportAll + ImportMem, + R: Renderer + AsGlowRenderer + ImportAll + ImportMem, R::TextureId: Send + Clone + 'static, C: From>, { @@ -637,6 +656,61 @@ impl CosmicStack { }) } + pub fn shadow_render_element( + &self, + renderer: &mut R, + location: Point, + scale: Scale, + alpha: f32, + ) -> Option + where + R: Renderer + AsGlowRenderer + ImportAll + ImportMem, + R::TextureId: Send + Clone + 'static, + C: From>, + { + self.0.with_program(|p| { + let windows = p.windows.lock().unwrap(); + let active = p.active.load(Ordering::SeqCst); + let activated = p.activated.load(Ordering::Acquire); + let theme = p.theme.lock().unwrap(); + let appearance = p.appearance_conf.lock().unwrap(); + let tiled = p.tiled.load(Ordering::Acquire); + + let round = appearance.clip_tiled_windows || !tiled; + if tiled && !appearance.shadow_tiled_windows { + return None; + } + let radii = round + .then(|| { + theme + .cosmic() + .radius_s() + .map(|x| if x < 4.0 { x } else { x + 4.0 }) + .map(|x| x.round() as u8) + }) + .unwrap_or([0, 0, 0, 0]); + + let mut geo = SpaceElement::geometry(&windows[active]).to_f64(); + geo.loc += location.to_f64().to_logical(scale); + geo.size.h += TAB_HEIGHT as f64; + + let window_key = + CosmicMappedKey(CosmicMappedKeyInner::Stack(Arc::downgrade(&self.0.0))); + + Some( + CosmicStackRenderElement::Shadow(ShadowShader::element( + renderer, + window_key, + geo.to_i32_round().as_local(), + radii, + if activated { alpha } else { alpha * 0.75 }, + scale.x, + )) + .into(), + ) + }) + } + pub fn render_elements( &self, renderer: &mut R, @@ -646,35 +720,85 @@ impl CosmicStack { scanout_override: Option, ) -> Vec where - R: Renderer + ImportAll + ImportMem, + R: Renderer + AsGlowRenderer + ImportAll + ImportMem, R::TextureId: Send + Clone + 'static, C: From>, { - let offset = self + let geometry = self .0 - .with_program(|p| { - p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)] - .geometry() - .loc - }) + .with_program(|p| p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry()) .to_physical_precise_round(scale); - let stack_loc = location + offset; + 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, ); + if !self.0.with_program(|p| p.tiled.load(Ordering::Acquire)) {} + elements.extend(self.0.with_program(|p| { let windows = p.windows.lock().unwrap(); let active = p.active.load(Ordering::SeqCst); + let theme = p.theme.lock().unwrap(); + let appearance = p.appearance_conf.lock().unwrap(); + let tiled = p.tiled.load(Ordering::Acquire); - windows[active].render_elements::>( - renderer, - window_loc, - scale, - alpha, - scanout_override, + let round = appearance.clip_tiled_windows || !tiled; + let radii = round.then(|| { + theme + .cosmic() + .radius_s() + .map(|x| if x < 4.0 { x } else { x + 4.0 }) + .map(|x| x.round() as u8) + }); + + let mut geo = SpaceElement::geometry(&windows[active]).to_f64(); + geo.loc += location.to_f64().to_logical(scale); + geo.size.h += TAB_HEIGHT as f64; + + let window_key = + CosmicMappedKey(CosmicMappedKeyInner::Stack(Arc::downgrade(&self.0.0))); + + let border = { + let (r, g, b, a) = theme.cosmic().bg_divider().into_components(); + CosmicStackRenderElement::Border(IndicatorShader::element( + renderer, + Key::Window(Usage::Border, window_key.clone()), + geo.to_i32_round().as_local(), + 1, + radii.unwrap_or([0, 0, 0, 0]), + a * alpha, + [r, g, b], + )) + }; + + std::iter::once(border).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) + } + }), ) })); @@ -682,6 +806,9 @@ impl CosmicStack { } pub(crate) fn set_theme(&self, theme: cosmic::Theme) { + self.0.with_program(|p| { + *p.theme.lock().unwrap() = theme.clone(); + }); self.0.set_theme(theme); } @@ -799,14 +926,37 @@ impl CosmicStack { pub fn corner_radius(&self, geometry_size: Size, default_radius: u8) -> [u8; 4] { self.0.with_program(|p| { let active_window = &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]; - let mut corners = active_window - .corner_radius(geometry_size) - .unwrap_or([default_radius; 4]); + let is_tiled = p.tiled.load(Ordering::Acquire); + let appearance = p.appearance_conf.lock().unwrap(); + let round = appearance.clip_tiled_windows || !is_tiled; + let radii = p + .theme + .lock() + .unwrap() + .cosmic() + .radius_s() + .map(|x| if x < 4.0 { x } else { x + 4.0 }) + .map(|val| val.round() as u8); - corners[1] = 8; - corners[3] = 8; + if !round { + let mut corners = active_window + .corner_radius(geometry_size) + .unwrap_or([default_radius; 4]); - corners + corners[1] = 0; + corners[3] = 0; + + corners + } else { + let mut corners = active_window.corner_radius(geometry_size).unwrap_or(radii); + + corners[0] = radii[0].max(corners[0]); + corners[1] = radii[1]; + corners[2] = radii[2].max(corners[2]); + corners[3] = radii[3]; + + corners + } }) } } @@ -1138,10 +1288,20 @@ impl Decorations for DefaultDecorations { .into(), ]; - let radius = if windows[active].is_maximized(false) { + let radius = if windows[active].is_maximized(false) + || (stack.tiled.load(Ordering::Acquire) + && !stack.appearance_conf.lock().unwrap().clip_tiled_windows) + { Radius::from(0.0) } else { - Radius::from([8.0, 8.0, 0.0, 0.0]) + let radii = stack + .theme + .lock() + .unwrap() + .cosmic() + .radius_s() + .map(|x| if x < 4.0 { x } else { x + 4.0 }); + Radius::from([radii[0], radii[1], 0., 0.]) }; let group_focused = stack.group_focused.load(Ordering::SeqCst); @@ -1667,8 +1827,193 @@ impl TouchTarget for CosmicStack { } } -render_elements! { - pub CosmicStackRenderElement where R: ImportAll + ImportMem; - Header = MemoryRenderBufferRenderElement, - Window = WaylandSurfaceRenderElement, +pub enum CosmicStackRenderElement { + Header(MemoryRenderBufferRenderElement), + Shadow(PixelShaderElement), + Border(PixelShaderElement), + Window(WaylandSurfaceRenderElement), + Clipped(ClippedSurfaceRenderElement), +} + +impl From> + for CosmicStackRenderElement +{ + fn from(value: MemoryRenderBufferRenderElement) -> Self { + Self::Header(value) + } +} + +impl From> + for CosmicStackRenderElement +{ + fn from(value: WaylandSurfaceRenderElement) -> 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, +{ + fn id(&self) -> &RendererId { + match self { + CosmicStackRenderElement::Header(elem) => elem.id(), + CosmicStackRenderElement::Shadow(elem) => elem.id(), + CosmicStackRenderElement::Border(elem) => elem.id(), + CosmicStackRenderElement::Window(elem) => elem.id(), + CosmicStackRenderElement::Clipped(elem) => elem.id(), + } + } + + fn current_commit(&self) -> CommitCounter { + match self { + CosmicStackRenderElement::Header(elem) => elem.current_commit(), + CosmicStackRenderElement::Shadow(elem) => elem.current_commit(), + CosmicStackRenderElement::Border(elem) => elem.current_commit(), + CosmicStackRenderElement::Window(elem) => elem.current_commit(), + CosmicStackRenderElement::Clipped(elem) => elem.current_commit(), + } + } + + fn src(&self) -> Rectangle { + match self { + CosmicStackRenderElement::Header(elem) => elem.src(), + CosmicStackRenderElement::Shadow(elem) => elem.src(), + CosmicStackRenderElement::Border(elem) => elem.src(), + CosmicStackRenderElement::Window(elem) => elem.src(), + CosmicStackRenderElement::Clipped(elem) => elem.src(), + } + } + + fn geometry(&self, scale: Scale) -> Rectangle { + match self { + CosmicStackRenderElement::Header(elem) => elem.geometry(scale), + CosmicStackRenderElement::Shadow(elem) => elem.geometry(scale), + CosmicStackRenderElement::Border(elem) => elem.geometry(scale), + CosmicStackRenderElement::Window(elem) => elem.geometry(scale), + CosmicStackRenderElement::Clipped(elem) => elem.geometry(scale), + } + } + + fn location(&self, scale: Scale) -> Point { + match self { + CosmicStackRenderElement::Header(elem) => elem.location(scale), + CosmicStackRenderElement::Shadow(elem) => elem.location(scale), + CosmicStackRenderElement::Border(elem) => elem.location(scale), + CosmicStackRenderElement::Window(elem) => elem.location(scale), + CosmicStackRenderElement::Clipped(elem) => elem.location(scale), + } + } + + fn transform(&self) -> Transform { + match self { + CosmicStackRenderElement::Header(elem) => elem.transform(), + CosmicStackRenderElement::Shadow(elem) => elem.transform(), + CosmicStackRenderElement::Border(elem) => elem.transform(), + CosmicStackRenderElement::Window(elem) => elem.transform(), + CosmicStackRenderElement::Clipped(elem) => elem.transform(), + } + } + + fn damage_since( + &self, + scale: Scale, + commit: Option, + ) -> DamageSet { + match self { + CosmicStackRenderElement::Header(elem) => elem.damage_since(scale, commit), + 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), + } + } + + fn opaque_regions(&self, scale: Scale) -> OpaqueRegions { + match self { + CosmicStackRenderElement::Header(elem) => elem.opaque_regions(scale), + 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), + } + } + + fn alpha(&self) -> f32 { + match self { + CosmicStackRenderElement::Header(elem) => elem.alpha(), + CosmicStackRenderElement::Shadow(elem) => elem.alpha(), + CosmicStackRenderElement::Border(elem) => elem.alpha(), + CosmicStackRenderElement::Window(elem) => elem.alpha(), + CosmicStackRenderElement::Clipped(elem) => elem.alpha(), + } + } + + fn kind(&self) -> Kind { + match self { + CosmicStackRenderElement::Header(elem) => elem.kind(), + CosmicStackRenderElement::Shadow(elem) => elem.kind(), + CosmicStackRenderElement::Border(elem) => elem.kind(), + CosmicStackRenderElement::Window(elem) => elem.kind(), + CosmicStackRenderElement::Clipped(elem) => elem.kind(), + } + } +} + +impl RenderElement for CosmicStackRenderElement +where + R: Renderer + AsGlowRenderer + ImportAll + ImportMem, + R::TextureId: 'static, + R::Error: FromGlesError, +{ + fn draw( + &self, + frame: &mut ::Frame<'_, '_>, + src: Rectangle, + dst: Rectangle, + damage: &[Rectangle], + opaque_regions: &[Rectangle], + ) -> Result<(), ::Error> { + match self { + CosmicStackRenderElement::Header(elem) => { + elem.draw(frame, src, dst, damage, opaque_regions) + } + CosmicStackRenderElement::Shadow(elem) | CosmicStackRenderElement::Border(elem) => { + RenderElement::::draw( + elem, + R::glow_frame_mut(frame), + src, + dst, + damage, + opaque_regions, + ) + .map_err(FromGlesError::from_gles_error) + } + CosmicStackRenderElement::Window(elem) => { + elem.draw(frame, src, dst, damage, opaque_regions) + } + CosmicStackRenderElement::Clipped(elem) => { + elem.draw(frame, src, dst, damage, opaque_regions) + } + } + } + + fn underlying_storage(&self, renderer: &mut R) -> Option> { + match self { + CosmicStackRenderElement::Header(elem) => elem.underlying_storage(renderer), + CosmicStackRenderElement::Shadow(elem) | CosmicStackRenderElement::Border(elem) => { + elem.underlying_storage(renderer.glow_renderer_mut()) + } + CosmicStackRenderElement::Window(elem) => elem.underlying_storage(renderer), + CosmicStackRenderElement::Clipped(elem) => elem.underlying_storage(renderer), + } + } } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 563335df..15ae315d 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1,7 +1,14 @@ use crate::{ - backend::render::cursor::CursorState, + backend::render::{ + IndicatorShader, Key, Usage, + clipped_surface::ClippedSurfaceRenderElement, + cursor::CursorState, + element::{AsGlowRenderer, FromGlesError}, + shadow::ShadowShader, + }, hooks::{Decorations, HOOKS}, shell::{ + element::{CosmicMappedKey, CosmicMappedKeyInner}, focus::target::PointerFocusTarget, grabs::{ReleaseMode, ResizeEdge}, }, @@ -20,9 +27,13 @@ use smithay::{ renderer::{ ImportAll, ImportMem, Renderer, element::{ - AsRenderElements, memory::MemoryRenderBufferRenderElement, + AsRenderElements, Element, Id as RendererId, Kind, RenderElement, + UnderlyingStorage, memory::MemoryRenderBufferRenderElement, surface::WaylandSurfaceRenderElement, }, + gles::element::PixelShaderElement, + glow::GlowRenderer, + utils::{CommitCounter, DamageSet, OpaqueRegions}, }, }, desktop::{WindowSurfaceType, space::SpaceElement}, @@ -42,8 +53,7 @@ use smithay::{ }, output::Output, reexports::wayland_server::protocol::wl_surface::WlSurface, - render_elements, - utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size}, + utils::{Buffer, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform}, wayland::seat::WaylandFocus, }; use std::{ @@ -51,7 +61,7 @@ use std::{ fmt, hash::Hash, sync::{ - Mutex, + Arc, Mutex, atomic::{AtomicBool, AtomicU8, Ordering}, }, }; @@ -80,6 +90,8 @@ pub struct CosmicWindowInternal { /// TODO: This needs to be per seat pointer_entered: AtomicU8, last_title: Mutex, + tiled: AtomicBool, + theme: Mutex, appearance_conf: Mutex, } @@ -165,9 +177,9 @@ impl CosmicWindowInternal { !self.window.is_decorated(pending) } - /// returns if the window is currently or pending tiled - pub fn is_tiled(&self, pending: bool) -> bool { - self.window.is_tiled(pending).unwrap_or(false) + /// returns if the window is currently tiled + pub fn is_tiled(&self) -> bool { + self.tiled.load(Ordering::Acquire) } } @@ -181,12 +193,19 @@ impl CosmicWindow { let window = window.into(); let width = window.geometry().size.w; let last_title = window.title(); + + if appearance.clip_floating_windows { + window.set_tiled(true); + } + CosmicWindow(IcedElement::new( CosmicWindowInternal { window, activated: AtomicBool::new(false), pointer_entered: AtomicU8::new(0), last_title: Mutex::new(last_title), + tiled: AtomicBool::new(false), + theme: Mutex::new(theme.clone()), appearance_conf: Mutex::new(appearance), }, (width, SSD_HEIGHT), @@ -241,8 +260,7 @@ impl CosmicWindow { let mut offset = Point::from((0., 0.)); let mut window_ui = None; let has_ssd = p.has_ssd(false); - if (has_ssd || p.is_tiled(false)) && surface_type.contains(WindowSurfaceType::TOPLEVEL) - { + if (has_ssd || p.is_tiled()) && surface_type.contains(WindowSurfaceType::TOPLEVEL) { let geo = p.window.geometry(); let point_i32 = relative_pos.to_i32_round::(); @@ -330,6 +348,77 @@ impl CosmicWindow { }) } + pub fn shadow_render_element( + &self, + renderer: &mut R, + location: Point, + scale: Scale, + alpha: f32, + ) -> Option + where + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + R::TextureId: Send + Clone + 'static, + C: From>, + { + self.0.with_program(|p| { + let has_ssd = p.has_ssd(false); + let is_tiled = p.is_tiled(); + let activated = p.window.is_activated(false); + let appearance = p.appearance_conf.lock().unwrap(); + + let clip = (!is_tiled && appearance.clip_floating_windows) + || (is_tiled && appearance.clip_tiled_windows); + let should_draw_shadow = if is_tiled { + appearance.shadow_tiled_windows + } else { + appearance.clip_floating_windows || has_ssd + }; + + if !should_draw_shadow { + return None; + } + let mut radii = p + .theme + .lock() + .unwrap() + .cosmic() + .radius_s() + .map(|x| if x < 4.0 { x } else { x + 4.0 }) + .map(|x| x.round() as u8); + if has_ssd && !clip { + // bottom corners + radii[1] = 0; + radii[3] = 0; + if is_tiled { + // top corners + radii[0] = 0; + radii[2] = 0; + } + } + + let mut geo = SpaceElement::geometry(&p.window).to_f64(); + geo.loc += location.to_f64().to_logical(scale); + if has_ssd { + geo.size.h += SSD_HEIGHT as f64; + } + + let window_key = + CosmicMappedKey(CosmicMappedKeyInner::Window(Arc::downgrade(&self.0.0))); + + Some( + CosmicWindowRenderElement::Shadow(ShadowShader::element( + renderer, + window_key, + geo.to_i32_round().as_local(), + radii, + if activated { alpha } else { alpha * 0.75 }, + scale.x, + )) + .into(), + ) + }) + } + pub fn render_elements( &self, renderer: &mut R, @@ -339,11 +428,36 @@ impl CosmicWindow { scanout_override: Option, ) -> Vec where - R: Renderer + ImportAll + ImportMem, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, R::TextureId: Send + Clone + 'static, C: From>, { - let has_ssd = self.0.with_program(|p| p.has_ssd(false)); + let (has_ssd, is_tiled, mut radii, appearance) = self.0.with_program(|p| { + ( + p.has_ssd(false), + p.is_tiled(), + p.theme + .lock() + .unwrap() + .cosmic() + .radius_s() + .map(|x| if x < 4.0 { x } else { x + 4.0 }) + .map(|x| x.round() as u8), + *p.appearance_conf.lock().unwrap(), + ) + }); + let clip = (!is_tiled && appearance.clip_floating_windows) + || (is_tiled && appearance.clip_tiled_windows); + if has_ssd && !clip { + // bottom corners + radii[1] = 0; + radii[3] = 0; + if is_tiled { + // top corners + radii[0] = 0; + radii[2] = 0; + } + } let window_loc = if has_ssd { location + Point::from((0, (SSD_HEIGHT as f64 * scale.y) as i32)) @@ -353,14 +467,60 @@ impl CosmicWindow { let mut elements = Vec::new(); - elements.extend(self.0.with_program(|p| { - p.window.render_elements::>( - renderer, - window_loc, - scale, - alpha, - scanout_override, + let (mut geo, bg_divider) = self.0.with_program(|p| { + ( + SpaceElement::geometry(&p.window).to_f64(), + p.theme.lock().unwrap().cosmic().bg_divider(), ) + }); + geo.loc += location.to_f64().to_logical(scale); + if has_ssd { + geo.size.h += SSD_HEIGHT as f64; + } + + if has_ssd || clip { + let window_key = + CosmicMappedKey(CosmicMappedKeyInner::Window(Arc::downgrade(&self.0.0))); + + let (r, g, b, a) = bg_divider.into_components(); + let elem = CosmicWindowRenderElement::Border(IndicatorShader::element( + renderer, + Key::Window(Usage::Border, window_key.clone()), + geo.to_i32_round().as_local(), + 1, + radii, + a * alpha, + [r, g, b], + )); + elements.push(elem); + } + + elements.extend(self.0.with_program(|p| { + p.window + .render_elements::>( + renderer, + window_loc, + scale, + alpha, + scanout_override, + ) + .into_iter() + .map(|elem| { + if has_ssd { + radii[0] = 0; + radii[2] = 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) + } + }) })); if has_ssd { @@ -378,6 +538,9 @@ impl CosmicWindow { } pub(crate) fn set_theme(&self, theme: cosmic::Theme) { + self.0.with_program(|p| { + *p.theme.lock().unwrap() = theme.clone(); + }); self.0.set_theme(theme); } @@ -424,9 +587,62 @@ impl CosmicWindow { }) } - pub fn corner_radius(&self, geometry_size: Size) -> Option<[u8; 4]> { - self.0 - .with_program(|p| p.window.corner_radius(geometry_size)) + pub fn set_tiled(&self, tiled: bool) { + self.0.with_program(|p| { + p.tiled.store(tiled, Ordering::Release); + if !p.appearance_conf.lock().unwrap().clip_floating_windows { + p.window.set_tiled(tiled); + } + }); + } + + pub fn corner_radius(&self, geometry_size: Size, default_radius: u8) -> [u8; 4] { + self.0.with_program(|p| { + let has_ssd = p.has_ssd(false); + let is_tiled = p.is_tiled(); + let appearance = p.appearance_conf.lock().unwrap(); + + let round = (!is_tiled && appearance.clip_floating_windows) + || (is_tiled && appearance.clip_tiled_windows); + let radii = p + .theme + .lock() + .unwrap() + .cosmic() + .radius_s() + .map(|x| if x < 4.0 { x } else { x + 4.0 }) + .map(|x| x.round() as u8); + + match (has_ssd, round) { + (has_ssd, true) => { + let mut corners = p.window.corner_radius(geometry_size).unwrap_or(radii); + + corners[0] = radii[0].max(corners[0]); + corners[1] = if has_ssd { + radii[1] + } else { + radii[1].max(corners[1]) + }; + corners[2] = radii[2].max(corners[2]); + corners[3] = if has_ssd { + radii[3] + } else { + radii[3].max(corners[3]) + }; + + corners + } + (true, false) => p + .window + .corner_radius(geometry_size) + .map(|[a, _, c, _]| [a, radii[1], c, radii[3]]) + .unwrap_or([default_radius, radii[1], default_radius, radii[3]]), + (false, false) => p + .window + .corner_radius(geometry_size) + .unwrap_or([default_radius; 4]), + } + }) } } @@ -597,7 +813,7 @@ impl SpaceElement for CosmicWindow { let mut bbox = SpaceElement::bbox(&p.window); let has_ssd = p.has_ssd(false); - if has_ssd || p.is_tiled(false) { + if has_ssd || p.is_tiled() { bbox.loc -= Point::from((RESIZE_BORDER, RESIZE_BORDER)); bbox.size += Size::from((RESIZE_BORDER * 2, RESIZE_BORDER * 2)); } @@ -716,7 +932,7 @@ impl PointerTarget for CosmicWindow { let mut event = event.clone(); self.0.with_program(|p| { let has_ssd = p.has_ssd(false); - if has_ssd || p.is_tiled(false) { + if has_ssd || p.is_tiled() { let Some(next) = Focus::under( &p.window, if has_ssd { SSD_HEIGHT } else { 0 }, @@ -742,7 +958,7 @@ impl PointerTarget for CosmicWindow { let mut event = event.clone(); self.0.with_program(|p| { let has_ssd = p.has_ssd(false); - if has_ssd || p.is_tiled(false) { + if has_ssd || p.is_tiled() { let Some(next) = Focus::under( &p.window, if has_ssd { SSD_HEIGHT } else { 0 }, @@ -955,8 +1171,193 @@ impl WaylandFocus for CosmicWindow { } } -render_elements! { - pub CosmicWindowRenderElement where R: ImportAll + ImportMem; - Header = MemoryRenderBufferRenderElement, - Window = WaylandSurfaceRenderElement, +pub enum CosmicWindowRenderElement { + Header(MemoryRenderBufferRenderElement), + Shadow(PixelShaderElement), + Border(PixelShaderElement), + Window(WaylandSurfaceRenderElement), + Clipped(ClippedSurfaceRenderElement), +} + +impl From> + for CosmicWindowRenderElement +{ + fn from(value: MemoryRenderBufferRenderElement) -> Self { + Self::Header(value) + } +} + +impl From> + for CosmicWindowRenderElement +{ + fn from(value: WaylandSurfaceRenderElement) -> 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, +{ + fn id(&self) -> &RendererId { + match self { + CosmicWindowRenderElement::Header(elem) => elem.id(), + CosmicWindowRenderElement::Shadow(elem) => elem.id(), + CosmicWindowRenderElement::Border(elem) => elem.id(), + CosmicWindowRenderElement::Window(elem) => elem.id(), + CosmicWindowRenderElement::Clipped(elem) => elem.id(), + } + } + + fn current_commit(&self) -> CommitCounter { + match self { + CosmicWindowRenderElement::Header(elem) => elem.current_commit(), + CosmicWindowRenderElement::Shadow(elem) => elem.current_commit(), + CosmicWindowRenderElement::Border(elem) => elem.current_commit(), + CosmicWindowRenderElement::Window(elem) => elem.current_commit(), + CosmicWindowRenderElement::Clipped(elem) => elem.current_commit(), + } + } + + fn src(&self) -> Rectangle { + match self { + CosmicWindowRenderElement::Header(elem) => elem.src(), + CosmicWindowRenderElement::Shadow(elem) => elem.src(), + CosmicWindowRenderElement::Border(elem) => elem.src(), + CosmicWindowRenderElement::Window(elem) => elem.src(), + CosmicWindowRenderElement::Clipped(elem) => elem.src(), + } + } + + fn geometry(&self, scale: Scale) -> Rectangle { + match self { + CosmicWindowRenderElement::Header(elem) => elem.geometry(scale), + CosmicWindowRenderElement::Shadow(elem) => elem.geometry(scale), + CosmicWindowRenderElement::Border(elem) => elem.geometry(scale), + CosmicWindowRenderElement::Window(elem) => elem.geometry(scale), + CosmicWindowRenderElement::Clipped(elem) => elem.geometry(scale), + } + } + + fn location(&self, scale: Scale) -> Point { + match self { + CosmicWindowRenderElement::Header(elem) => elem.location(scale), + CosmicWindowRenderElement::Shadow(elem) => elem.location(scale), + CosmicWindowRenderElement::Border(elem) => elem.location(scale), + CosmicWindowRenderElement::Window(elem) => elem.location(scale), + CosmicWindowRenderElement::Clipped(elem) => elem.location(scale), + } + } + + fn transform(&self) -> Transform { + match self { + CosmicWindowRenderElement::Header(elem) => elem.transform(), + CosmicWindowRenderElement::Shadow(elem) => elem.transform(), + CosmicWindowRenderElement::Border(elem) => elem.transform(), + CosmicWindowRenderElement::Window(elem) => elem.transform(), + CosmicWindowRenderElement::Clipped(elem) => elem.transform(), + } + } + + fn damage_since( + &self, + scale: Scale, + commit: Option, + ) -> DamageSet { + match self { + CosmicWindowRenderElement::Header(elem) => elem.damage_since(scale, commit), + 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), + } + } + + fn opaque_regions(&self, scale: Scale) -> OpaqueRegions { + match self { + CosmicWindowRenderElement::Header(elem) => elem.opaque_regions(scale), + 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), + } + } + + fn alpha(&self) -> f32 { + match self { + CosmicWindowRenderElement::Header(elem) => elem.alpha(), + CosmicWindowRenderElement::Shadow(elem) => elem.alpha(), + CosmicWindowRenderElement::Border(elem) => elem.alpha(), + CosmicWindowRenderElement::Window(elem) => elem.alpha(), + CosmicWindowRenderElement::Clipped(elem) => elem.alpha(), + } + } + + fn kind(&self) -> Kind { + match self { + CosmicWindowRenderElement::Header(elem) => elem.kind(), + CosmicWindowRenderElement::Shadow(elem) => elem.kind(), + CosmicWindowRenderElement::Border(elem) => elem.kind(), + CosmicWindowRenderElement::Window(elem) => elem.kind(), + CosmicWindowRenderElement::Clipped(elem) => elem.kind(), + } + } +} + +impl RenderElement for CosmicWindowRenderElement +where + R: Renderer + AsGlowRenderer + ImportAll + ImportMem, + R::TextureId: 'static, + R::Error: FromGlesError, +{ + fn draw( + &self, + frame: &mut ::Frame<'_, '_>, + src: Rectangle, + dst: Rectangle, + damage: &[Rectangle], + opaque_regions: &[Rectangle], + ) -> Result<(), ::Error> { + match self { + CosmicWindowRenderElement::Header(elem) => { + elem.draw(frame, src, dst, damage, opaque_regions) + } + CosmicWindowRenderElement::Shadow(elem) | CosmicWindowRenderElement::Border(elem) => { + RenderElement::::draw( + elem, + R::glow_frame_mut(frame), + src, + dst, + damage, + opaque_regions, + ) + .map_err(FromGlesError::from_gles_error) + } + CosmicWindowRenderElement::Window(elem) => { + elem.draw(frame, src, dst, damage, opaque_regions) + } + CosmicWindowRenderElement::Clipped(elem) => { + elem.draw(frame, src, dst, damage, opaque_regions) + } + } + } + + fn underlying_storage(&self, renderer: &mut R) -> Option> { + match self { + CosmicWindowRenderElement::Header(elem) => elem.underlying_storage(renderer), + CosmicWindowRenderElement::Shadow(elem) | CosmicWindowRenderElement::Border(elem) => { + elem.underlying_storage(renderer.glow_renderer_mut()) + } + CosmicWindowRenderElement::Window(elem) => elem.underlying_storage(renderer), + CosmicWindowRenderElement::Clipped(elem) => elem.underlying_storage(renderer), + } + } } diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index 43a97f65..22839862 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -205,6 +205,12 @@ impl MoveGrabState { output_scale, alpha, ); + let shadow_element = self.window.shadow_render_element( + renderer, + (render_location - self.window.geometry().loc).to_physical_precise_round(output_scale), + output_scale, + alpha, + ); self.stacking_indicator .iter() @@ -218,31 +224,36 @@ impl MoveGrabState { }) .chain(p_elements) .chain(focus_element) - .chain(w_elements.into_iter().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, - })) + .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, + }), + ) .chain(snapping_indicator) .map(I::from) .collect() diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 79aad4ec..b8be92e7 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -1501,6 +1501,14 @@ impl FloatingLayout { .unwrap_or_else(|| (self.space.element_geometry(elem).unwrap().as_local(), alpha)); let render_location = geometry.loc - elem.geometry().loc.as_local(); + let shadow_element = elem.shadow_render_element( + renderer, + render_location + .as_logical() + .to_physical_precise_round(output_scale), + output_scale.into(), + alpha, + ); let mut window_elements = elem.render_elements( renderer, render_location @@ -1622,6 +1630,7 @@ impl FloatingLayout { } elements.extend(window_elements); + elements.extend(shadow_element.into_iter()); } elements diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 6e68c2fd..f6572169 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -4034,6 +4034,7 @@ impl TilingLayout { let draw_groups = overview.0.alpha(); let mut elements = Vec::default(); + let mut shadow_elements = Vec::default(); let is_overview = !matches!(overview.0, OverviewMode::None); let is_mouse_tiling = (matches!(overview.0.trigger(), Some(Trigger::Pointer(_)))) @@ -4076,6 +4077,7 @@ impl TilingLayout { percentage, indicator_thickness, swap_desc.is_some(), + &mut shadow_elements, theme, )); @@ -4129,6 +4131,7 @@ impl TilingLayout { overview, resize_indicator, swap_desc.clone(), + &mut shadow_elements, &self.swapping_stack_surface_id, &self.backdrop_id, theme, @@ -4139,6 +4142,8 @@ impl TilingLayout { elements.extend(group_elements); } + elements.extend(shadow_elements); + Ok(elements) } @@ -4987,6 +4992,7 @@ fn render_old_tree_windows( percentage: f32, indicator_thickness: u8, is_swap_mode: bool, + shadow_elements: &mut Vec>, theme: &cosmic::theme::CosmicTheme, ) -> Vec> where @@ -5007,6 +5013,17 @@ 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, + Scale::from(output_scale), + alpha, + ) + .into_iter(), + ); let window_elements = mapped.render_elements::>( renderer, geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, @@ -5219,6 +5236,7 @@ fn render_new_tree_windows( overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), mut resize_indicator: Option<(ResizeMode, ResizeIndicator)>, swap_desc: Option, + shadow_elements: &mut Vec>, swapping_stack_surface_id: &Id, backdrop_id: &Id, theme: &cosmic::theme::CosmicTheme, @@ -5505,6 +5523,17 @@ where if let Data::Mapped { mapped, .. } = data { let elem_geometry = mapped.geometry().to_physical_precise_round(output_scale); + shadow_elements.extend( + mapped + .shadow_render_element( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) + - elem_geometry.loc, + Scale::from(output_scale), + alpha, + ) + .into_iter(), + ); let mut elements = mapped.render_elements::>( renderer, //original_location,