shell/elements: Handle clipping and shadows

This commit is contained in:
Victoria Brekenfeld 2025-12-08 18:22:23 +01:00 committed by Victoria Brekenfeld
parent 2adebb5fe1
commit 59fd732982
7 changed files with 917 additions and 97 deletions

View file

@ -136,6 +136,7 @@ pub enum Usage {
FocusIndicator,
PotentialGroupIndicator,
SnappingIndicator,
Border,
}
#[derive(Clone)]

View file

@ -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<R, C>(
&self,
renderer: &mut R,
location: smithay::utils::Point<i32, smithay::utils::Physical>,
scale: smithay::utils::Scale<f64>,
alpha: f32,
) -> Option<C>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
R::TextureId: Send + Clone + 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
C: From<CosmicMappedRenderElement<R>>,
{
match &self.element {
CosmicMappedInternal::Stack(s) => s
.shadow_render_element::<R, CosmicMappedRenderElement<R>>(
renderer, location, scale, alpha,
)
.map(Into::into),
CosmicMappedInternal::Window(w) => w
.shadow_render_element::<R, CosmicMappedRenderElement<R>>(
renderer, location, scale, alpha,
)
.map(Into::into),
_ => unreachable!(),
}
}
pub fn render_elements<R, C>(
&self,
renderer: &mut R,
@ -852,9 +878,7 @@ impl CosmicMapped {
pub fn corner_radius(&self, geometry_size: Size<i32, Logical>, 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<CosmicStack> for CosmicMapped {
pub enum CosmicMappedRenderElement<R>
where
R: Renderer + ImportAll + ImportMem,
R: Renderer + AsGlowRenderer + ImportAll + ImportMem,
R::TextureId: 'static,
{
Stack(self::stack::CosmicStackRenderElement<R>),
@ -1053,7 +1077,7 @@ where
impl<R> Element for CosmicMappedRenderElement<R>
where
R: Renderer + ImportAll + ImportMem,
R: Renderer + AsGlowRenderer + ImportAll + ImportMem,
R::TextureId: 'static,
{
fn id(&self) -> &smithay::backend::renderer::element::Id {

View file

@ -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<Option<Rectangle<i32, Global>>>,
mask: Mutex<Option<tiny_skia::Mask>>,
tiled: AtomicBool,
theme: Mutex<cosmic::Theme>,
appearance_conf: Mutex<AppearanceConfig>,
}
@ -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<Item = CosmicSurface> {
self.0.with_program(|p| {
p.windows
@ -618,7 +637,7 @@ impl CosmicStack {
alpha: f32,
) -> Vec<C>
where
R: Renderer + ImportAll + ImportMem,
R: Renderer + AsGlowRenderer + ImportAll + ImportMem,
R::TextureId: Send + Clone + 'static,
C: From<CosmicStackRenderElement<R>>,
{
@ -637,6 +656,61 @@ impl CosmicStack {
})
}
pub fn shadow_render_element<R, C>(
&self,
renderer: &mut R,
location: Point<i32, Physical>,
scale: Scale<f64>,
alpha: f32,
) -> Option<C>
where
R: Renderer + AsGlowRenderer + ImportAll + ImportMem,
R::TextureId: Send + Clone + 'static,
C: From<CosmicStackRenderElement<R>>,
{
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<R, C>(
&self,
renderer: &mut R,
@ -646,35 +720,85 @@ impl CosmicStack {
scanout_override: Option<bool>,
) -> Vec<C>
where
R: Renderer + ImportAll + ImportMem,
R: Renderer + AsGlowRenderer + ImportAll + ImportMem,
R::TextureId: Send + Clone + 'static,
C: From<CosmicStackRenderElement<R>>,
{
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::<R>::render_elements::<CosmicStackRenderElement<R>>(
&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::<R, CosmicStackRenderElement<R>>(
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::<R, WaylandSurfaceRenderElement<R>>(
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<i32, Logical>, 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<CosmicStackInternal, Message> 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<State> for CosmicStack {
}
}
render_elements! {
pub CosmicStackRenderElement<R> where R: ImportAll + ImportMem;
Header = MemoryRenderBufferRenderElement<R>,
Window = WaylandSurfaceRenderElement<R>,
pub enum CosmicStackRenderElement<R: Renderer + ImportAll + ImportMem> {
Header(MemoryRenderBufferRenderElement<R>),
Shadow(PixelShaderElement),
Border(PixelShaderElement),
Window(WaylandSurfaceRenderElement<R>),
Clipped(ClippedSurfaceRenderElement<R>),
}
impl<R: Renderer + ImportAll + ImportMem> From<MemoryRenderBufferRenderElement<R>>
for CosmicStackRenderElement<R>
{
fn from(value: MemoryRenderBufferRenderElement<R>) -> Self {
Self::Header(value)
}
}
impl<R: Renderer + ImportAll + ImportMem> From<WaylandSurfaceRenderElement<R>>
for CosmicStackRenderElement<R>
{
fn from(value: WaylandSurfaceRenderElement<R>) -> Self {
Self::Window(value)
}
}
impl<R: Renderer + ImportAll + ImportMem> From<ClippedSurfaceRenderElement<R>>
for CosmicStackRenderElement<R>
{
fn from(value: ClippedSurfaceRenderElement<R>) -> Self {
Self::Clipped(value)
}
}
impl<R> Element for CosmicStackRenderElement<R>
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<f64, Buffer> {
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<f64>) -> Rectangle<i32, Physical> {
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<f64>) -> Point<i32, Physical> {
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<f64>,
commit: Option<CommitCounter>,
) -> DamageSet<i32, Physical> {
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<f64>) -> OpaqueRegions<i32, Physical> {
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<R> RenderElement<R> for CosmicStackRenderElement<R>
where
R: Renderer + AsGlowRenderer + ImportAll + ImportMem,
R::TextureId: 'static,
R::Error: FromGlesError,
{
fn draw(
&self,
frame: &mut <R>::Frame<'_, '_>,
src: Rectangle<f64, Buffer>,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
opaque_regions: &[Rectangle<i32, Physical>],
) -> Result<(), <R>::Error> {
match self {
CosmicStackRenderElement::Header(elem) => {
elem.draw(frame, src, dst, damage, opaque_regions)
}
CosmicStackRenderElement::Shadow(elem) | CosmicStackRenderElement::Border(elem) => {
RenderElement::<GlowRenderer>::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<UnderlyingStorage<'_>> {
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),
}
}
}

View file

@ -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<String>,
tiled: AtomicBool,
theme: Mutex<cosmic::Theme>,
appearance_conf: Mutex<AppearanceConfig>,
}
@ -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::<i32>();
@ -330,6 +348,77 @@ impl CosmicWindow {
})
}
pub fn shadow_render_element<R, C>(
&self,
renderer: &mut R,
location: Point<i32, Physical>,
scale: Scale<f64>,
alpha: f32,
) -> Option<C>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
R::TextureId: Send + Clone + 'static,
C: From<CosmicWindowRenderElement<R>>,
{
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<R, C>(
&self,
renderer: &mut R,
@ -339,11 +428,36 @@ impl CosmicWindow {
scanout_override: Option<bool>,
) -> Vec<C>
where
R: Renderer + ImportAll + ImportMem,
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
R::TextureId: Send + Clone + 'static,
C: From<CosmicWindowRenderElement<R>>,
{
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::<R, CosmicWindowRenderElement<R>>(
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::<R, WaylandSurfaceRenderElement<R>>(
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<i32, Logical>) -> 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<i32, Logical>, 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<State> 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<State> 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<R> where R: ImportAll + ImportMem;
Header = MemoryRenderBufferRenderElement<R>,
Window = WaylandSurfaceRenderElement<R>,
pub enum CosmicWindowRenderElement<R: Renderer + ImportAll + ImportMem> {
Header(MemoryRenderBufferRenderElement<R>),
Shadow(PixelShaderElement),
Border(PixelShaderElement),
Window(WaylandSurfaceRenderElement<R>),
Clipped(ClippedSurfaceRenderElement<R>),
}
impl<R: Renderer + ImportAll + ImportMem> From<MemoryRenderBufferRenderElement<R>>
for CosmicWindowRenderElement<R>
{
fn from(value: MemoryRenderBufferRenderElement<R>) -> Self {
Self::Header(value)
}
}
impl<R: Renderer + ImportAll + ImportMem> From<WaylandSurfaceRenderElement<R>>
for CosmicWindowRenderElement<R>
{
fn from(value: WaylandSurfaceRenderElement<R>) -> Self {
Self::Window(value)
}
}
impl<R: Renderer + ImportAll + ImportMem> From<ClippedSurfaceRenderElement<R>>
for CosmicWindowRenderElement<R>
{
fn from(value: ClippedSurfaceRenderElement<R>) -> Self {
Self::Clipped(value)
}
}
impl<R> Element for CosmicWindowRenderElement<R>
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<f64, Buffer> {
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<f64>) -> Rectangle<i32, Physical> {
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<f64>) -> Point<i32, Physical> {
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<f64>,
commit: Option<CommitCounter>,
) -> DamageSet<i32, Physical> {
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<f64>) -> OpaqueRegions<i32, Physical> {
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<R> RenderElement<R> for CosmicWindowRenderElement<R>
where
R: Renderer + AsGlowRenderer + ImportAll + ImportMem,
R::TextureId: 'static,
R::Error: FromGlesError,
{
fn draw(
&self,
frame: &mut <R>::Frame<'_, '_>,
src: Rectangle<f64, Buffer>,
dst: Rectangle<i32, Physical>,
damage: &[Rectangle<i32, Physical>],
opaque_regions: &[Rectangle<i32, Physical>],
) -> Result<(), <R>::Error> {
match self {
CosmicWindowRenderElement::Header(elem) => {
elem.draw(frame, src, dst, damage, opaque_regions)
}
CosmicWindowRenderElement::Shadow(elem) | CosmicWindowRenderElement::Border(elem) => {
RenderElement::<GlowRenderer>::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<UnderlyingStorage<'_>> {
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),
}
}
}

View file

@ -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()

View file

@ -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

View file

@ -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<R>(
percentage: f32,
indicator_thickness: u8,
is_swap_mode: bool,
shadow_elements: &mut Vec<CosmicMappedRenderElement<R>>,
theme: &cosmic::theme::CosmicTheme,
) -> Vec<CosmicMappedRenderElement<R>>
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::<R, CosmicMappedRenderElement<R>>(
renderer,
geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc,
@ -5219,6 +5236,7 @@ fn render_new_tree_windows<R>(
overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree<Data>>)>),
mut resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
swap_desc: Option<NodeDesc>,
shadow_elements: &mut Vec<CosmicMappedRenderElement<R>>,
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::<R, CosmicMappedRenderElement<R>>(
renderer,
//original_location,