diff --git a/docs/wiki/Configuration:-Layer-Rules.md b/docs/wiki/Configuration:-Layer-Rules.md index 0b2ccf34de..f223d56817 100644 --- a/docs/wiki/Configuration:-Layer-Rules.md +++ b/docs/wiki/Configuration:-Layer-Rules.md @@ -193,6 +193,25 @@ layer-rule { } ``` +#### `geometry-corner-radius-exponent` + +Since: next release + +Sets the exponent used for corner rounding, using the superellipse equation `x^n + y^n = 1`. + +- 2 is the default circular rounding. +- Values greater than 2 produce "squircle" corners. +- Values closer to 1 produce chamfer-like corners. + +```kdl +layer-rule { + match namespace="^launcher$" + + geometry-corner-radius 12 + geometry-corner-radius-exponent 4 +} +``` + #### `place-within-backdrop` Since: 25.05 diff --git a/docs/wiki/Configuration:-Layout.md b/docs/wiki/Configuration:-Layout.md index 5e5e2a5c6c..c239d715ae 100644 --- a/docs/wiki/Configuration:-Layout.md +++ b/docs/wiki/Configuration:-Layout.md @@ -73,6 +73,7 @@ layout { position "right" gaps-between-tabs 2 corner-radius 8 + corner-radius-exponent 4 active-color "red" inactive-color "gray" urgent-color "blue" @@ -459,6 +460,15 @@ It can be `left`, `right`, `top`, or `bottom`. `corner-radius` sets the rounded corner radius for tabs in the indicator in logical pixels. When `gaps-between-tabs` is zero, only the first and the last tabs have rounded corners, otherwise all tabs do. +`corner-radius-exponent` Since: next release sets the shape of the rounded corners. +The default is `2` for normal circular rounding, values greater than `2` make "squircle" corners, and `1` make the corners "chamfered". + +Tab corner exponents are picked in this order: + +1. `corner-radius-exponent` from the `tab-indicator` window rule, if set. +1. `geometry-corner-radius-exponent` from the window rule, if set. +1. `corner-radius-exponent` from the `tab-indicator` layout options. + `active-color`, `inactive-color`, `urgent-color`, `active-gradient`, `inactive-gradient`, `urgent-gradient` let you override the colors for the tabs. They have the same semantics as the border and focus ring colors and gradients. diff --git a/docs/wiki/Configuration:-Window-Rules.md b/docs/wiki/Configuration:-Window-Rules.md index f8fa215ec3..0cca336fb3 100644 --- a/docs/wiki/Configuration:-Window-Rules.md +++ b/docs/wiki/Configuration:-Window-Rules.md @@ -811,6 +811,7 @@ window-rule { tab-indicator { inactive-color "darkred" + corner-radius-exponent 4 } } ``` @@ -847,6 +848,23 @@ This way, you can match GTK 3 applications which have square bottom corners: ![A screenshot showing a window with only the top corners rounded](./img/different-corner-radius.png) +#### `geometry-corner-radius-exponent` + +Since: next release + +Sets the exponent used for corner rounding, using the superellipse equation `x^n + y^n = 1`. + +- 2 is the default circular rounding. +- Values greater than 2 produce "squircle" corners. +- Values closer to 1 produce chamfer-like corners. + +```kdl +window-rule { + geometry-corner-radius 12 + geometry-corner-radius-exponent 4 +} +``` + #### `clip-to-geometry` Since: 0.1.6 @@ -994,6 +1012,7 @@ window-rule { popups { // Matches the default libadwaita pop-up corner radius. geometry-corner-radius 15 + geometry-corner-radius-exponent 2 // Note: it'll look better to set background opacity // through your GTK theme CSS and not here. diff --git a/niri-config/src/appearance.rs b/niri-config/src/appearance.rs index a1c8327149..94bf53fa81 100644 --- a/niri-config/src/appearance.rs +++ b/niri-config/src/appearance.rs @@ -148,6 +148,8 @@ pub struct CornerRadius { pub bottom_left: f32, } +pub const DEFAULT_CORNER_RADIUS_EXPONENT: f32 = 2.; + impl From for [f32; 4] { fn from(value: CornerRadius) -> Self { [ @@ -466,6 +468,7 @@ pub struct TabIndicator { pub position: TabIndicatorPosition, pub gaps_between_tabs: f64, pub corner_radius: f64, + pub corner_radius_exponent: f64, pub active_color: Option, pub inactive_color: Option, pub urgent_color: Option, @@ -488,6 +491,7 @@ impl Default for TabIndicator { position: TabIndicatorPosition::Left, gaps_between_tabs: 0., corner_radius: 0., + corner_radius_exponent: DEFAULT_CORNER_RADIUS_EXPONENT as f64, active_color: None, inactive_color: None, urgent_color: None, @@ -513,6 +517,7 @@ impl MergeWith for TabIndicator { width, gaps_between_tabs, corner_radius, + corner_radius_exponent, ); merge_clone!((self, part), length, position); @@ -548,6 +553,8 @@ pub struct TabIndicatorPart { pub gaps_between_tabs: Option>, #[knuffel(child, unwrap(argument))] pub corner_radius: Option>, + #[knuffel(child, unwrap(argument))] + pub corner_radius_exponent: Option>, #[knuffel(child)] pub active_color: Option, #[knuffel(child)] @@ -666,6 +673,8 @@ pub struct ShadowRule { #[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)] pub struct TabIndicatorRule { + #[knuffel(child, unwrap(argument))] + pub corner_radius_exponent: Option>, #[knuffel(child)] pub active_color: Option, #[knuffel(child)] @@ -713,6 +722,8 @@ impl MergeWith for ShadowRule { impl MergeWith for TabIndicatorRule { fn merge_with(&mut self, part: &Self) { + merge_clone_opt!((self, part), corner_radius_exponent); + merge_color_gradient_opt!( (self, part), (active_color, active_gradient), diff --git a/niri-config/src/layer_rule.rs b/niri-config/src/layer_rule.rs index cf099b3a6d..b8acd8cb88 100644 --- a/niri-config/src/layer_rule.rs +++ b/niri-config/src/layer_rule.rs @@ -1,6 +1,7 @@ use crate::appearance::{BackgroundEffectRule, BlockOutFrom, CornerRadius, ShadowRule}; use crate::utils::RegexEq; use crate::window_rule::PopupsRule; +use crate::FloatOrInt; #[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)] pub struct LayerRule { @@ -18,6 +19,8 @@ pub struct LayerRule { #[knuffel(child)] pub geometry_corner_radius: Option, #[knuffel(child, unwrap(argument))] + pub geometry_corner_radius_exponent: Option>, + #[knuffel(child, unwrap(argument))] pub place_within_backdrop: Option, #[knuffel(child, unwrap(argument))] pub baba_is_float: Option, diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index 909aeb80a5..29a4e5d910 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -1377,6 +1377,7 @@ mod tests { position: Top, gaps_between_tabs: 0.0, corner_radius: 0.0, + corner_radius_exponent: 2.0, active_color: None, inactive_color: None, urgent_color: None, @@ -1837,6 +1838,7 @@ mod tests { inactive_color: None, }, tab_indicator: TabIndicatorRule { + corner_radius_exponent: None, active_color: Some( Color { r: 1.0, @@ -1854,6 +1856,7 @@ mod tests { draw_border_with_background: None, opacity: None, geometry_corner_radius: None, + geometry_corner_radius_exponent: None, clip_to_geometry: None, baba_is_float: None, block_out_from: None, @@ -1883,6 +1886,7 @@ mod tests { popups: PopupsRule { opacity: None, geometry_corner_radius: None, + geometry_corner_radius_exponent: None, background_effect: BackgroundEffectRule { xray: None, blur: None, @@ -1923,6 +1927,7 @@ mod tests { inactive_color: None, }, geometry_corner_radius: None, + geometry_corner_radius_exponent: None, place_within_backdrop: None, baba_is_float: None, background_effect: BackgroundEffectRule { @@ -1934,6 +1939,7 @@ mod tests { popups: PopupsRule { opacity: None, geometry_corner_radius: None, + geometry_corner_radius_exponent: None, background_effect: BackgroundEffectRule { xray: None, blur: None, diff --git a/niri-config/src/window_rule.rs b/niri-config/src/window_rule.rs index f2bc2ad157..803f7a0759 100644 --- a/niri-config/src/window_rule.rs +++ b/niri-config/src/window_rule.rs @@ -60,6 +60,8 @@ pub struct WindowRule { #[knuffel(child)] pub geometry_corner_radius: Option, #[knuffel(child, unwrap(argument))] + pub geometry_corner_radius_exponent: Option>, + #[knuffel(child, unwrap(argument))] pub clip_to_geometry: Option, #[knuffel(child, unwrap(argument))] pub baba_is_float: Option, @@ -88,6 +90,8 @@ pub struct PopupsRule { pub opacity: Option, #[knuffel(child)] pub geometry_corner_radius: Option, + #[knuffel(child, unwrap(argument))] + pub geometry_corner_radius_exponent: Option>, #[knuffel(child, default)] pub background_effect: BackgroundEffectRule, } @@ -100,6 +104,8 @@ pub struct ResolvedPopupsRules { /// Corner radius to assume the popups have. pub geometry_corner_radius: Option, + /// Exponent to use for popup corner rounding. + pub geometry_corner_radius_exponent: Option, /// Background effect configuration for popups. pub background_effect: BackgroundEffect, @@ -113,6 +119,9 @@ impl MergeWith for ResolvedPopupsRules { if let Some(x) = part.geometry_corner_radius { self.geometry_corner_radius = Some(x); } + if let Some(x) = part.geometry_corner_radius_exponent { + self.geometry_corner_radius_exponent = Some(x.0 as f32); + } self.background_effect.merge_with(&part.background_effect); } } diff --git a/niri-visual-tests/src/cases/gradient_angle.rs b/niri-visual-tests/src/cases/gradient_angle.rs index 6cf666c141..401b6187f3 100644 --- a/niri-visual-tests/src/cases/gradient_angle.rs +++ b/niri-visual-tests/src/cases/gradient_angle.rs @@ -2,7 +2,7 @@ use std::f32::consts::{FRAC_PI_2, PI}; use std::time::Duration; use niri::render_helpers::border::BorderRenderElement; -use niri_config::{Color, CornerRadius, GradientInterpolation}; +use niri_config::{Color, CornerRadius, GradientInterpolation, DEFAULT_CORNER_RADIUS_EXPONENT}; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::GlesRenderer; use smithay::utils::{Physical, Point, Rectangle, Size}; @@ -62,6 +62,7 @@ impl TestCase for GradientAngle { Rectangle::from_size(area.size), 0., CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, 1., 1., ) diff --git a/niri-visual-tests/src/cases/gradient_area.rs b/niri-visual-tests/src/cases/gradient_area.rs index f92f850420..2e17c3892e 100644 --- a/niri-visual-tests/src/cases/gradient_area.rs +++ b/niri-visual-tests/src/cases/gradient_area.rs @@ -3,7 +3,7 @@ use std::time::Duration; use niri::layout::focus_ring::FocusRing; use niri::render_helpers::border::BorderRenderElement; -use niri_config::{Color, CornerRadius, GradientInterpolation}; +use niri_config::{Color, CornerRadius, GradientInterpolation, DEFAULT_CORNER_RADIUS_EXPONENT}; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::GlesRenderer; use smithay::utils::{Physical, Point, Rectangle, Size}; @@ -86,6 +86,7 @@ impl TestCase for GradientArea { false, Rectangle::default(), CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, 1., 1., ); @@ -103,6 +104,7 @@ impl TestCase for GradientArea { Rectangle::from_size(rect_size).to_f64(), 0., CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, 1., 1., ) diff --git a/niri-visual-tests/src/cases/gradient_oklab.rs b/niri-visual-tests/src/cases/gradient_oklab.rs index c933d278aa..dac523fec6 100644 --- a/niri-visual-tests/src/cases/gradient_oklab.rs +++ b/niri-visual-tests/src/cases/gradient_oklab.rs @@ -1,6 +1,7 @@ use niri::render_helpers::border::BorderRenderElement; use niri_config::{ Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, + DEFAULT_CORNER_RADIUS_EXPONENT, }; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::GlesRenderer; @@ -43,6 +44,7 @@ impl TestCase for GradientOklab { Rectangle::from_size(area.size), 0., CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, 1., 1., ) diff --git a/niri-visual-tests/src/cases/gradient_oklab_alpha.rs b/niri-visual-tests/src/cases/gradient_oklab_alpha.rs index a0d65f9293..158eb32f5f 100644 --- a/niri-visual-tests/src/cases/gradient_oklab_alpha.rs +++ b/niri-visual-tests/src/cases/gradient_oklab_alpha.rs @@ -1,5 +1,7 @@ use niri::render_helpers::border::BorderRenderElement; -use niri_config::{Color, CornerRadius, GradientColorSpace, GradientInterpolation}; +use niri_config::{ + Color, CornerRadius, GradientColorSpace, GradientInterpolation, DEFAULT_CORNER_RADIUS_EXPONENT, +}; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::GlesRenderer; use smithay::utils::{Physical, Point, Rectangle, Size}; @@ -41,6 +43,7 @@ impl TestCase for GradientOklabAlpha { Rectangle::from_size(area.size), 0., CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, 1., 1., ) diff --git a/niri-visual-tests/src/cases/gradient_oklch_alpha.rs b/niri-visual-tests/src/cases/gradient_oklch_alpha.rs index ebcfb09a98..2dd0a5dae9 100644 --- a/niri-visual-tests/src/cases/gradient_oklch_alpha.rs +++ b/niri-visual-tests/src/cases/gradient_oklch_alpha.rs @@ -1,6 +1,7 @@ use niri::render_helpers::border::BorderRenderElement; use niri_config::{ Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, + DEFAULT_CORNER_RADIUS_EXPONENT, }; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::GlesRenderer; @@ -43,6 +44,7 @@ impl TestCase for GradientOklchAlpha { Rectangle::from_size(area.size), 0., CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, 1., 1., ) diff --git a/niri-visual-tests/src/cases/gradient_oklch_decreasing.rs b/niri-visual-tests/src/cases/gradient_oklch_decreasing.rs index fb604fae69..4dc3e678d2 100644 --- a/niri-visual-tests/src/cases/gradient_oklch_decreasing.rs +++ b/niri-visual-tests/src/cases/gradient_oklch_decreasing.rs @@ -1,6 +1,7 @@ use niri::render_helpers::border::BorderRenderElement; use niri_config::{ Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, + DEFAULT_CORNER_RADIUS_EXPONENT, }; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::GlesRenderer; @@ -43,6 +44,7 @@ impl TestCase for GradientOklchDecreasing { Rectangle::from_size(area.size), 0., CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, 1., 1., ) diff --git a/niri-visual-tests/src/cases/gradient_oklch_increasing.rs b/niri-visual-tests/src/cases/gradient_oklch_increasing.rs index 776b0d51dc..08c7e8ac90 100644 --- a/niri-visual-tests/src/cases/gradient_oklch_increasing.rs +++ b/niri-visual-tests/src/cases/gradient_oklch_increasing.rs @@ -1,6 +1,7 @@ use niri::render_helpers::border::BorderRenderElement; use niri_config::{ Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, + DEFAULT_CORNER_RADIUS_EXPONENT, }; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::GlesRenderer; @@ -43,6 +44,7 @@ impl TestCase for GradientOklchIncreasing { Rectangle::from_size(area.size), 0., CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, 1., 1., ) diff --git a/niri-visual-tests/src/cases/gradient_oklch_longer.rs b/niri-visual-tests/src/cases/gradient_oklch_longer.rs index 870b117561..4d7fa83ac5 100644 --- a/niri-visual-tests/src/cases/gradient_oklch_longer.rs +++ b/niri-visual-tests/src/cases/gradient_oklch_longer.rs @@ -1,6 +1,7 @@ use niri::render_helpers::border::BorderRenderElement; use niri_config::{ Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, + DEFAULT_CORNER_RADIUS_EXPONENT, }; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::GlesRenderer; @@ -43,6 +44,7 @@ impl TestCase for GradientOklchLonger { Rectangle::from_size(area.size), 0., CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, 1., 1., ) diff --git a/niri-visual-tests/src/cases/gradient_oklch_shorter.rs b/niri-visual-tests/src/cases/gradient_oklch_shorter.rs index 5622ac35a6..c58d2a533d 100644 --- a/niri-visual-tests/src/cases/gradient_oklch_shorter.rs +++ b/niri-visual-tests/src/cases/gradient_oklch_shorter.rs @@ -1,6 +1,7 @@ use niri::render_helpers::border::BorderRenderElement; use niri_config::{ Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, + DEFAULT_CORNER_RADIUS_EXPONENT, }; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::GlesRenderer; @@ -43,6 +44,7 @@ impl TestCase for GradientOklchShorter { Rectangle::from_size(area.size), 0., CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, 1., 1., ) diff --git a/niri-visual-tests/src/cases/gradient_srgb.rs b/niri-visual-tests/src/cases/gradient_srgb.rs index 4afc69b6c8..4f772435df 100644 --- a/niri-visual-tests/src/cases/gradient_srgb.rs +++ b/niri-visual-tests/src/cases/gradient_srgb.rs @@ -1,6 +1,7 @@ use niri::render_helpers::border::BorderRenderElement; use niri_config::{ Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, + DEFAULT_CORNER_RADIUS_EXPONENT, }; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::GlesRenderer; @@ -43,6 +44,7 @@ impl TestCase for GradientSrgb { Rectangle::from_size(area.size), 0., CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, 1., 1., ) diff --git a/niri-visual-tests/src/cases/gradient_srgb_alpha.rs b/niri-visual-tests/src/cases/gradient_srgb_alpha.rs index 1cb602fafd..b349dcaf41 100644 --- a/niri-visual-tests/src/cases/gradient_srgb_alpha.rs +++ b/niri-visual-tests/src/cases/gradient_srgb_alpha.rs @@ -1,5 +1,7 @@ use niri::render_helpers::border::BorderRenderElement; -use niri_config::{Color, CornerRadius, GradientColorSpace, GradientInterpolation}; +use niri_config::{ + Color, CornerRadius, GradientColorSpace, GradientInterpolation, DEFAULT_CORNER_RADIUS_EXPONENT, +}; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::GlesRenderer; use smithay::utils::{Physical, Point, Rectangle, Size}; @@ -41,6 +43,7 @@ impl TestCase for GradientSrgbAlpha { Rectangle::from_size(area.size), 0., CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, 1., 1., ) diff --git a/niri-visual-tests/src/cases/gradient_srgblinear.rs b/niri-visual-tests/src/cases/gradient_srgblinear.rs index 5c24275f98..b7337e8737 100644 --- a/niri-visual-tests/src/cases/gradient_srgblinear.rs +++ b/niri-visual-tests/src/cases/gradient_srgblinear.rs @@ -1,6 +1,7 @@ use niri::render_helpers::border::BorderRenderElement; use niri_config::{ Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, + DEFAULT_CORNER_RADIUS_EXPONENT, }; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::GlesRenderer; @@ -43,6 +44,7 @@ impl TestCase for GradientSrgbLinear { Rectangle::from_size(area.size), 0., CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, 1., 1., ) diff --git a/niri-visual-tests/src/cases/gradient_srgblinear_alpha.rs b/niri-visual-tests/src/cases/gradient_srgblinear_alpha.rs index 59b8fbce36..259bb6e0c3 100644 --- a/niri-visual-tests/src/cases/gradient_srgblinear_alpha.rs +++ b/niri-visual-tests/src/cases/gradient_srgblinear_alpha.rs @@ -1,5 +1,7 @@ use niri::render_helpers::border::BorderRenderElement; -use niri_config::{Color, CornerRadius, GradientColorSpace, GradientInterpolation}; +use niri_config::{ + Color, CornerRadius, GradientColorSpace, GradientInterpolation, DEFAULT_CORNER_RADIUS_EXPONENT, +}; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::GlesRenderer; use smithay::utils::{Physical, Point, Rectangle, Size}; @@ -41,6 +43,7 @@ impl TestCase for GradientSrgbLinearAlpha { Rectangle::from_size(area.size), 0., CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, 1., 1., ) diff --git a/src/layer/mapped.rs b/src/layer/mapped.rs index fbaa6fc61f..c5077e97d9 100644 --- a/src/layer/mapped.rs +++ b/src/layer/mapped.rs @@ -1,5 +1,5 @@ use niri_config::utils::MergeWith as _; -use niri_config::{Config, LayerRule}; +use niri_config::{Config, LayerRule, DEFAULT_CORNER_RADIUS_EXPONENT}; use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement; use smithay::backend::renderer::element::Kind; use smithay::desktop::{LayerSurface, PopupKind, PopupManager}; @@ -122,8 +122,16 @@ impl MappedLayer { let radius = self.rules.geometry_corner_radius.unwrap_or_default(); // FIXME: is_active based on keyboard focus? - self.shadow - .update_render_elements(size, true, radius, self.scale, 1.); + self.shadow.update_render_elements( + size, + true, + radius, + self.rules + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), + self.scale, + 1., + ); } pub fn are_animations_ongoing(&self) -> bool { @@ -248,6 +256,9 @@ impl MappedLayer { surface_anim_scale, self.blur_config, radius, + self.rules + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), self.rules.background_effect, should_block_out, xray_pos, @@ -317,6 +328,9 @@ impl MappedLayer { surface_anim_scale, self.blur_config, popup_rules.geometry_corner_radius.unwrap_or_default(), + popup_rules + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), effect, false, xray_pos, diff --git a/src/layer/mod.rs b/src/layer/mod.rs index 767a6b0ec1..4e3a6580c7 100644 --- a/src/layer/mod.rs +++ b/src/layer/mod.rs @@ -21,6 +21,8 @@ pub struct ResolvedLayerRules { /// Corner radius to assume this layer surface has. pub geometry_corner_radius: Option, + /// Exponent to use for corner rounding. + pub geometry_corner_radius_exponent: Option, /// Whether to place this layer surface within the overview backdrop. pub place_within_backdrop: bool, @@ -69,6 +71,9 @@ impl ResolvedLayerRules { if let Some(x) = rule.geometry_corner_radius { resolved.geometry_corner_radius = Some(x); } + if let Some(x) = rule.geometry_corner_radius_exponent { + resolved.geometry_corner_radius_exponent = Some(x.0 as f32); + } if let Some(x) = rule.place_within_backdrop { resolved.place_within_backdrop = x; } diff --git a/src/layout/focus_ring.rs b/src/layout/focus_ring.rs index b53a5df8b9..49a7bc77cf 100644 --- a/src/layout/focus_ring.rs +++ b/src/layout/focus_ring.rs @@ -63,6 +63,7 @@ impl FocusRing { is_urgent: bool, view_rect: Rectangle, radius: CornerRadius, + exponent: f32, scale: f64, alpha: f32, ) { @@ -190,6 +191,7 @@ impl FocusRing { Rectangle::new(full_rect.loc - loc, full_rect.size), rounded_corner_border_width, radius, + exponent, scale as f32, alpha, ); @@ -209,6 +211,7 @@ impl FocusRing { Rectangle::new(full_rect.loc - self.locations[0], full_rect.size), rounded_corner_border_width, radius, + exponent, scale as f32, alpha, ); diff --git a/src/layout/insert_hint_element.rs b/src/layout/insert_hint_element.rs index 97bbf2d5cb..c6931f05f6 100644 --- a/src/layout/insert_hint_element.rs +++ b/src/layout/insert_hint_element.rs @@ -1,4 +1,4 @@ -use niri_config::CornerRadius; +use niri_config::{CornerRadius, DEFAULT_CORNER_RADIUS_EXPONENT}; use smithay::utils::{Logical, Point, Rectangle, Size}; use super::focus_ring::{FocusRing, FocusRingRenderElement}; @@ -51,8 +51,17 @@ impl InsertHintElement { radius: CornerRadius, scale: f64, ) { - self.inner - .update_render_elements(size, true, false, false, view_rect, radius, scale, 1.); + self.inner.update_render_elements( + size, + true, + false, + false, + view_rect, + radius, + DEFAULT_CORNER_RADIUS_EXPONENT, + scale, + 1., + ); } pub fn render( diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 5c4dd639e4..a2d8818901 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -208,6 +208,7 @@ pub trait LayoutElement { _clip_to_geometry: bool, _surface_anim_scale: Scale, _radius: CornerRadius, + _exponent: f32, _xray_pos: XrayPos, _push: &mut dyn FnMut(BackgroundEffectElement), ) { diff --git a/src/layout/shadow.rs b/src/layout/shadow.rs index 509ad728cc..8efa6f2a4e 100644 --- a/src/layout/shadow.rs +++ b/src/layout/shadow.rs @@ -37,6 +37,7 @@ impl Shadow { win_size: Size, is_active: bool, radius: CornerRadius, + exponent: f32, scale: f64, alpha: f32, ) { @@ -133,9 +134,11 @@ impl Shadow { color, sigma as f32, radius, + exponent, scale as f32, Rectangle::new(window_geo.loc - offset - rect.loc, window_geo.size), win_radius, + exponent, alpha, ); @@ -152,9 +155,11 @@ impl Shadow { color, sigma as f32, radius, + exponent, scale as f32, Rectangle::zero(), Default::default(), + exponent, alpha, ); diff --git a/src/layout/tab_indicator.rs b/src/layout/tab_indicator.rs index e7d9a3df1a..95696c8168 100644 --- a/src/layout/tab_indicator.rs +++ b/src/layout/tab_indicator.rs @@ -28,6 +28,8 @@ pub struct TabInfo { pub gradient: Gradient, /// Tab geometry in the same coordinate system as the area. pub geometry: Rectangle, + /// Corner exponent for this tab. + pub corner_exponent: f32, } niri_render_elements! { @@ -263,6 +265,7 @@ impl TabIndicator { Rectangle::from_size(rect.size), 0., radius, + tab.corner_exponent, scale as f32, 1., ); @@ -406,7 +409,19 @@ impl TabInfo { .unwrap_or_else(gradient_from_border); let geometry = Rectangle::new(position, tile.animated_tile_size()); - - TabInfo { gradient, geometry } + let corner_exponent = rule.corner_radius_exponent.map_or_else( + || { + rules + .geometry_corner_radius_exponent + .unwrap_or(config.corner_radius_exponent as f32) + }, + |x| x.0 as f32, + ); + + TabInfo { + gradient, + geometry, + corner_exponent, + } } } diff --git a/src/layout/tile.rs b/src/layout/tile.rs index 86c7ebadb1..7d9432d4e5 100644 --- a/src/layout/tile.rs +++ b/src/layout/tile.rs @@ -2,7 +2,7 @@ use core::f64; use std::rc::Rc; use niri_config::utils::MergeWith as _; -use niri_config::{Color, CornerRadius, GradientInterpolation}; +use niri_config::{Color, CornerRadius, GradientInterpolation, DEFAULT_CORNER_RADIUS_EXPONENT}; use niri_ipc::WindowLayout; use smithay::backend::renderer::element::{Element, Kind}; use smithay::backend::renderer::gles::GlesRenderer; @@ -408,6 +408,11 @@ impl Tile { .geometry_corner_radius() .fit_to(window_size.w as f32, window_size.h as f32); self.rounded_corner_damage.set_corner_radius(radius); + self.rounded_corner_damage.set_corner_exponent( + rules + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), + ); } pub fn advance_animations(&mut self) { @@ -460,7 +465,6 @@ impl Tile { let rules = self.window.rules(); let animated_tile_size = self.animated_tile_size(); let expanded_progress = self.expanded_progress(); - let draw_border_with_background = rules .draw_border_with_background .unwrap_or_else(|| !self.window.has_ssd()); @@ -500,6 +504,9 @@ impl Tile { view_rect.size, ), radius, + rules + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), self.scale, 1. - expanded_progress as f32, ); @@ -515,6 +522,9 @@ impl Tile { animated_tile_size, is_active, radius, + rules + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), self.scale, 1. - expanded_progress as f32, ); @@ -532,6 +542,9 @@ impl Tile { self.window.is_urgent(), view_rect, radius, + rules + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), self.scale, 1. - expanded_progress as f32, ); @@ -1073,7 +1086,6 @@ impl Tile { .window .geometry_corner_radius() .scaled_by(1. - expanded_progress as f32); - // Popups go on top, whether it's resize or not. self.window.render_popups( ctx.r(), @@ -1134,6 +1146,9 @@ impl Tile { resize.anim.value() as f32, resize.anim.clamped_value().clamp(0., 1.) as f32, radius, + rules + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), clip_to_geometry, win_alpha, ); @@ -1182,6 +1197,9 @@ impl Tile { geo, shader.clone(), radius, + rules + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), ) .into(); } @@ -1209,6 +1227,9 @@ impl Tile { Rectangle::from_size(geo.size), 0., radius, + rules + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), scale.x as f32, 1., ) @@ -1263,6 +1284,9 @@ impl Tile { Rectangle::from_size(size), 0., radius, + rules + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), scale.x as f32, alpha, ) @@ -1309,6 +1333,9 @@ impl Tile { clip_to_geometry, surface_anim_scale, radius, + rules + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), xray_pos, &mut |elem| push(elem.into()), ); diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 7448779722..a19959408c 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -5,6 +5,7 @@ use std::time::Duration; use niri_config::utils::MergeWith as _; use niri_config::{ CenterFocusedColumn, CornerRadius, OutputName, PresetSize, Workspace as WorkspaceConfig, + DEFAULT_CORNER_RADIUS_EXPONENT, }; use niri_ipc::{ColumnDisplay, PositionChange, SizeChange, WindowLayout}; use smithay::backend::renderer::element::Kind; @@ -387,6 +388,7 @@ impl Workspace { self.view_size, true, CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, self.scale.fractional_scale(), 1., ); diff --git a/src/render_helpers/background_effect.rs b/src/render_helpers/background_effect.rs index d148b6c150..5df859fa08 100644 --- a/src/render_helpers/background_effect.rs +++ b/src/render_helpers/background_effect.rs @@ -1,6 +1,6 @@ use std::sync::{Arc, Mutex}; -use niri_config::CornerRadius; +use niri_config::{CornerRadius, DEFAULT_CORNER_RADIUS_EXPONENT}; use smithay::backend::renderer::gles::GlesRenderer; use smithay::utils::{Logical, Point, Rectangle, Scale}; use smithay::wayland::compositor::{with_states, SurfaceData}; @@ -26,6 +26,8 @@ pub struct BackgroundEffect { /// Stored here in addition to `RenderParams` to damage when it changes. // FIXME: would be good to remove this duplication of radius. corner_radius: CornerRadius, + /// Corner exponent for clipping. + corner_exponent: f32, blur_config: niri_config::Blur, options: Options, } @@ -56,15 +58,15 @@ pub struct RenderParams { /// /// `subregion.iter()` should return `geometry`-relative rectangles. pub subregion: Option, - /// Geometry and radius for clipping in the same coordinate space as `geometry`. - pub clip: Option<(Rectangle, CornerRadius)>, + /// Geometry, radius and exponent for clipping in the same coordinate space as `geometry`. + pub clip: Option<(Rectangle, CornerRadius, f32)>, /// Scale to use for rounding to physical pixels. pub scale: f64, } impl RenderParams { fn fit_clip_radius(&mut self) { - if let Some((geo, radius)) = &mut self.clip { + if let Some((geo, radius, _)) = &mut self.clip { // HACK: increase radius to avoid slight bleed on rounded corners. *radius = radius.expanded_by(1.); @@ -87,6 +89,7 @@ impl BackgroundEffect { nonxray: FramebufferEffect::new(), damage: ExtraDamage::new(), corner_radius: CornerRadius::default(), + corner_exponent: DEFAULT_CORNER_RADIUS_EXPONENT, blur_config: niri_config::Blur::default(), options: Options::default(), } @@ -111,6 +114,7 @@ impl BackgroundEffect { pub fn update_render_elements( &mut self, corner_radius: CornerRadius, + corner_exponent: f32, effect: niri_config::BackgroundEffect, has_blur_region: bool, ) { @@ -134,12 +138,16 @@ impl BackgroundEffect { options.xray = true; } - if self.options == options && self.corner_radius == corner_radius { + if self.options == options + && self.corner_radius == corner_radius + && self.corner_exponent == corner_exponent + { return; } self.options = options; self.corner_radius = corner_radius; + self.corner_exponent = corner_exponent; self.damage.damage_all(); self.nonxray.damage(); } @@ -162,6 +170,7 @@ impl BackgroundEffect { if let Some(clip) = &mut params.clip { clip.1 = self.corner_radius; + clip.2 = self.corner_exponent; } params.fit_clip_radius(); @@ -251,7 +260,11 @@ fn render_params_for_tile( } // This corner radius is reset to self.corner_radius in render(). - let clip = clip.then_some((geometry, CornerRadius::default())); + let clip = clip.then_some(( + geometry, + CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, + )); Some(RenderParams { geometry: effect_geometry, @@ -292,6 +305,7 @@ pub fn render_for_tile( surface_anim_scale: Scale, blur_config: niri_config::Blur, radius: CornerRadius, + corner_exponent: f32, effect: niri_config::BackgroundEffect, should_block_out: bool, xray_pos: XrayPos, @@ -305,7 +319,7 @@ pub fn render_for_tile( let has_blur_region = blur_region.as_ref().is_some_and(|r| !r.is_empty()); background_effect.update_config(blur_config); - background_effect.update_render_elements(radius, effect, has_blur_region); + background_effect.update_render_elements(radius, corner_exponent, effect, has_blur_region); if !background_effect.is_visible() { return; diff --git a/src/render_helpers/border.rs b/src/render_helpers/border.rs index 923066efed..fa78f71000 100644 --- a/src/render_helpers/border.rs +++ b/src/render_helpers/border.rs @@ -4,6 +4,7 @@ use std::rc::Rc; use glam::{Mat3, Vec2}; use niri_config::{ Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation, + DEFAULT_CORNER_RADIUS_EXPONENT, }; use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage}; use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, Uniform}; @@ -41,6 +42,7 @@ struct Parameters { geometry: Rectangle, border_width: f32, corner_radius: CornerRadius, + corner_exponent: f32, // Should only be used for visual improvements, i.e. corner radius anti-aliasing. scale: f32, alpha: f32, @@ -58,6 +60,7 @@ impl BorderRenderElement { geometry: Rectangle, border_width: f32, corner_radius: CornerRadius, + corner_exponent: f32, scale: f32, alpha: f32, ) -> Self { @@ -74,6 +77,7 @@ impl BorderRenderElement { geometry, border_width, corner_radius, + corner_exponent, scale, alpha, }, @@ -96,6 +100,7 @@ impl BorderRenderElement { geometry: Default::default(), border_width: 0., corner_radius: Default::default(), + corner_exponent: DEFAULT_CORNER_RADIUS_EXPONENT, scale: 1., alpha: 1., }, @@ -118,6 +123,7 @@ impl BorderRenderElement { geometry: Rectangle, border_width: f32, corner_radius: CornerRadius, + corner_exponent: f32, scale: f32, alpha: f32, ) { @@ -131,6 +137,7 @@ impl BorderRenderElement { geometry, border_width, corner_radius, + corner_exponent, scale, alpha, }; @@ -153,6 +160,7 @@ impl BorderRenderElement { geometry, border_width, corner_radius, + corner_exponent, scale, alpha, } = self.params; @@ -212,6 +220,7 @@ impl BorderRenderElement { mat3_uniform("input_to_geo", input_to_geo), Uniform::new("geo_size", geo_size.to_array()), Uniform::new("outer_radius", <[f32; 4]>::from(corner_radius)), + Uniform::new("corner_exponent", corner_exponent), Uniform::new("border_width", border_width), ]), HashMap::new(), diff --git a/src/render_helpers/clipped_surface.rs b/src/render_helpers/clipped_surface.rs index e357895e66..e24dc90793 100644 --- a/src/render_helpers/clipped_surface.rs +++ b/src/render_helpers/clipped_surface.rs @@ -20,6 +20,7 @@ pub struct ClippedSurfaceRenderElement { inner: WaylandSurfaceRenderElement, program: GlesTexProgram, corner_radius: CornerRadius, + corner_exponent: f32, geometry: Rectangle, scale: f32, } @@ -28,6 +29,7 @@ pub struct ClippedSurfaceRenderElement { pub struct RoundedCornerDamage { damage: ExtraDamage, corner_radius: CornerRadius, + corner_exponent: f32, } impl ClippedSurfaceRenderElement { @@ -37,11 +39,13 @@ impl ClippedSurfaceRenderElement { geometry: Rectangle, program: GlesTexProgram, corner_radius: CornerRadius, + corner_exponent: f32, ) -> Self { Self { inner: elem, program, corner_radius, + corner_exponent, geometry, scale: scale.x as f32, } @@ -95,6 +99,7 @@ impl ClippedSurfaceRenderElement { Uniform::new("niri_scale", self.scale), Uniform::new("geo_size", geo_size), Uniform::new("corner_radius", <[f32; 4]>::from(self.corner_radius)), + Uniform::new("corner_exponent", self.corner_exponent), mat3_uniform("input_to_geo", input_to_geo), ] } @@ -300,6 +305,15 @@ impl RoundedCornerDamage { self.damage.damage_all(); } + pub fn set_corner_exponent(&mut self, corner_exponent: f32) { + if self.corner_exponent == corner_exponent { + return; + } + + self.corner_exponent = corner_exponent; + self.damage.damage_all(); + } + pub fn render(&self, geometry: Rectangle) -> ExtraDamage { self.damage.render(geometry) } diff --git a/src/render_helpers/framebuffer_effect.rs b/src/render_helpers/framebuffer_effect.rs index 4dc529dbb4..9958335242 100644 --- a/src/render_helpers/framebuffer_effect.rs +++ b/src/render_helpers/framebuffer_effect.rs @@ -1,7 +1,7 @@ use std::cell::RefCell; use glam::{Mat3, Vec2}; -use niri_config::CornerRadius; +use niri_config::{CornerRadius, DEFAULT_CORNER_RADIUS_EXPONENT}; use smithay::backend::allocator::Fourcc; use smithay::backend::renderer::element::{Element, Id, RenderElement}; use smithay::backend::renderer::gles::{ @@ -33,6 +33,7 @@ pub struct FramebufferEffectElement { geometry: Rectangle, clip_geo: Rectangle, corner_radius: CornerRadius, + corner_exponent: f32, subregion: Option, scale: f32, blur_options: Option, @@ -69,9 +70,11 @@ impl FramebufferEffect { noise: f32, saturation: f32, ) -> FramebufferEffectElement { - let (clip_geo, corner_radius) = params - .clip - .unwrap_or((params.geometry, CornerRadius::default())); + let (clip_geo, corner_radius, corner_exponent) = params.clip.unwrap_or(( + params.geometry, + CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, + )); let mut id = self.id.clone(); if let Some(ns) = ns { @@ -84,6 +87,7 @@ impl FramebufferEffect { geometry: params.geometry, clip_geo, corner_radius, + corner_exponent, subregion: params.subregion, scale: params.scale as f32, blur_options, @@ -98,7 +102,7 @@ impl FramebufferEffectElement { &self, crop: Rectangle, transform: Transform, - ) -> [Uniform<'static>; 7] { + ) -> [Uniform<'static>; 8] { let offset = crop.loc - (self.clip_geo.loc - self.geometry.loc); let offset = Vec2::new(offset.x as f32, offset.y as f32); let crop_size = Vec2::new(crop.size.w as f32, crop.size.h as f32); @@ -120,6 +124,7 @@ impl FramebufferEffectElement { Uniform::new("niri_scale", self.scale), Uniform::new("geo_size", clip_geo_size), Uniform::new("corner_radius", <[f32; 4]>::from(self.corner_radius)), + Uniform::new("corner_exponent", self.corner_exponent), mat3_uniform("input_to_geo", input_to_clip_geo), Uniform::new("noise", self.noise), Uniform::new("saturation", self.saturation), diff --git a/src/render_helpers/resize.rs b/src/render_helpers/resize.rs index 79b26dbf34..ff39e5095c 100644 --- a/src/render_helpers/resize.rs +++ b/src/render_helpers/resize.rs @@ -31,6 +31,7 @@ impl ResizeRenderElement { progress: f32, clamped_progress: f32, corner_radius: CornerRadius, + corner_exponent: f32, clip_to_geometry: bool, result_alpha: f32, ) -> Self { @@ -103,6 +104,7 @@ impl ResizeRenderElement { Uniform::new("niri_progress", progress), Uniform::new("niri_clamped_progress", clamped_progress), Uniform::new("niri_corner_radius", <[f32; 4]>::from(corner_radius)), + Uniform::new("niri_corner_exponent", corner_exponent), Uniform::new("niri_clip_to_geometry", clip_to_geometry), ]), HashMap::from([ diff --git a/src/render_helpers/shaders/border.frag b/src/render_helpers/shaders/border.frag index ed0e08877f..73ad307d27 100644 --- a/src/render_helpers/shaders/border.frag +++ b/src/render_helpers/shaders/border.frag @@ -21,6 +21,7 @@ uniform vec2 grad_vec; uniform mat3 input_to_geo; uniform vec2 geo_size; uniform vec4 outer_radius; +uniform float corner_exponent; uniform float border_width; vec4 premul_rect(vec4 color) { @@ -208,12 +209,13 @@ vec4 gradient_color(vec2 coords) { return color_mix(color_from, color_to, frac); } -float niri_rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius); +float niri_rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius, float corner_exponent); + void main() { vec3 coords_geo = input_to_geo * vec3(niri_v_coords, 1.0); vec4 color = gradient_color(coords_geo.xy); - color = color * niri_rounding_alpha(coords_geo.xy, geo_size, outer_radius); + color = color * niri_rounding_alpha(coords_geo.xy, geo_size, outer_radius, corner_exponent); if (border_width > 0.0) { coords_geo -= vec3(border_width); @@ -222,7 +224,7 @@ void main() { && 0.0 <= coords_geo.y && coords_geo.y <= inner_geo_size.y) { vec4 inner_radius = max(outer_radius - vec4(border_width), 0.0); - color = color * (1.0 - niri_rounding_alpha(coords_geo.xy, inner_geo_size, inner_radius)); + color = color * (1.0 - niri_rounding_alpha(coords_geo.xy, inner_geo_size, inner_radius, corner_exponent)); } } diff --git a/src/render_helpers/shaders/clipped_surface.frag b/src/render_helpers/shaders/clipped_surface.frag index e97f18c07b..34186b5e51 100644 --- a/src/render_helpers/shaders/clipped_surface.frag +++ b/src/render_helpers/shaders/clipped_surface.frag @@ -24,9 +24,10 @@ uniform float niri_scale; uniform vec2 geo_size; uniform vec4 corner_radius; +uniform float corner_exponent; uniform mat3 input_to_geo; -float niri_rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius); +float niri_rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius, float corner_exponent); vec4 postprocess(vec4 color); void main() { @@ -45,7 +46,7 @@ void main() { color = vec4(0.0); } else { // Apply corner rounding inside geometry. - color = color * niri_rounding_alpha(coords_geo.xy * geo_size, geo_size, corner_radius); + color = color * niri_rounding_alpha(coords_geo.xy * geo_size, geo_size, corner_radius, corner_exponent); } // Apply final alpha and tint. diff --git a/src/render_helpers/shaders/mod.rs b/src/render_helpers/shaders/mod.rs index 7be9b159fd..686b9ba1f7 100644 --- a/src/render_helpers/shaders/mod.rs +++ b/src/render_helpers/shaders/mod.rs @@ -10,6 +10,27 @@ use super::renderer::NiriRenderer; use super::shader_element::ShaderProgram; use crate::render_helpers::blur::BlurProgram; +const ROUNDING_ALPHA_IMPL_PLACEHOLDER: &str = "@ROUNDING_ALPHA_IMPL@"; +const EXPONENT_SHADOW_X_IMPL_PLACEHOLDER: &str = "@EXPONENT_SHADOW_X_IMPL@"; + +const ROUNDING_ALPHA_IMPL: &str = include_str!("rounding_alpha_superellipse.frag"); +const EXPONENT_SHADOW_X_IMPL: &str = " +float normalized = (-delta) / max(corner, 0.0001); +float remaining = max(0.0, 1.0 - pow(normalized, exponent)); +curved = halfSize.x - corner + corner * pow(remaining, 1.0 / exponent);"; + +const ROUNDING_ALPHA_IMPL_FALLBACK: &str = " +float niri_rounding_alpha_impl(vec2 coords, vec2 center, float radius, float corner_exponent) { + float dist = distance(coords, center); + + // Manual smoothstep() between radius - half_px and radius + half_px + // to avoid a division in clamp(). + float t = clamp((dist - radius) * niri_scale + 0.5, 0.0, 1.0); + return 1.0 - t * t * (3.0 - 2.0 * t); +}"; +const EXPONENT_SHADOW_X_IMPL_FALLBACK: &str = + "curved = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta));"; + pub struct Shaders { pub border: Option, pub shadow: Option, @@ -32,11 +53,82 @@ pub enum ProgramType { Open, } +fn try_compile_shader( + shader_name: &str, + renderer: &mut GlesRenderer, + src: &str, + additional_uniforms: &[UniformName<'_>], + texture_uniforms: &[&str], +) -> Option { + let replaced_src = src + .replace(ROUNDING_ALPHA_IMPL_PLACEHOLDER, ROUNDING_ALPHA_IMPL) + .replace(EXPONENT_SHADOW_X_IMPL_PLACEHOLDER, EXPONENT_SHADOW_X_IMPL); + match ShaderProgram::compile( + renderer, + &replaced_src, + additional_uniforms, + texture_uniforms, + ) { + Ok(p) => Some(p), + Err(err) => { + warn!("error compiling {shader_name} shader with support for corner radius exponent: {err:?}"); + let fallback_src = src + .replace( + ROUNDING_ALPHA_IMPL_PLACEHOLDER, + ROUNDING_ALPHA_IMPL_FALLBACK, + ) + .replace( + EXPONENT_SHADOW_X_IMPL_PLACEHOLDER, + EXPONENT_SHADOW_X_IMPL_FALLBACK, + ); + ShaderProgram::compile( + renderer, + &fallback_src, + additional_uniforms, + texture_uniforms, + ) + .inspect_err(|e| warn!("error compiling fallback {shader_name} shader: {e:?}")) + .ok() + } + } +} + +fn try_compile_custom_texture_shader( + shader_name: &str, + renderer: &mut GlesRenderer, + src: &str, + additional_uniforms: &[UniformName<'_>], +) -> Option { + let replaced_src = src + .replace(ROUNDING_ALPHA_IMPL_PLACEHOLDER, ROUNDING_ALPHA_IMPL) + .replace(EXPONENT_SHADOW_X_IMPL_PLACEHOLDER, EXPONENT_SHADOW_X_IMPL); + match renderer.compile_custom_texture_shader(replaced_src, additional_uniforms) { + Ok(s) => Some(s), + Err(err) => { + warn!("error compiling {shader_name} shader with support for corner radius exponent: {err:?}"); + let fallback_src = src + .replace( + ROUNDING_ALPHA_IMPL_PLACEHOLDER, + ROUNDING_ALPHA_IMPL_FALLBACK, + ) + .replace( + EXPONENT_SHADOW_X_IMPL_PLACEHOLDER, + EXPONENT_SHADOW_X_IMPL_FALLBACK, + ); + renderer + .compile_custom_texture_shader(fallback_src, additional_uniforms) + .inspect_err(|e| warn!("error compiling fallback {shader_name} shader: {e:?}")) + .ok() + } + } +} + impl Shaders { fn compile(renderer: &mut GlesRenderer) -> Self { let _span = tracy_client::span!("Shaders::compile"); - let border = ShaderProgram::compile( + let border = try_compile_shader( + "border", renderer, concat!( include_str!("border.frag"), @@ -53,16 +145,14 @@ impl Shaders { UniformName::new("input_to_geo", UniformType::Matrix3x3), UniformName::new("geo_size", UniformType::_2f), UniformName::new("outer_radius", UniformType::_4f), + UniformName::new("corner_exponent", UniformType::_1f), UniformName::new("border_width", UniformType::_1f), ], &[], - ) - .map_err(|err| { - warn!("error compiling border shader: {err:?}"); - }) - .ok(); + ); - let shadow = ShaderProgram::compile( + let shadow = try_compile_shader( + "shadow", renderer, concat!( include_str!("shadow.frag"), @@ -74,57 +164,51 @@ impl Shaders { UniformName::new("input_to_geo", UniformType::Matrix3x3), UniformName::new("geo_size", UniformType::_2f), UniformName::new("corner_radius", UniformType::_4f), + UniformName::new("corner_exponent", UniformType::_1f), UniformName::new("window_input_to_geo", UniformType::Matrix3x3), UniformName::new("window_geo_size", UniformType::_2f), UniformName::new("window_corner_radius", UniformType::_4f), + UniformName::new("window_corner_exponent", UniformType::_1f), ], &[], - ) - .map_err(|err| { - warn!("error compiling shadow shader: {err:?}"); - }) - .ok(); + ); - let clipped_surface = renderer - .compile_custom_texture_shader( - concat!( - include_str!("clipped_surface.frag"), - include_str!("rounding_alpha.frag"), - "\nvec4 postprocess(vec4 color) { return color; }", - ), - &[ - UniformName::new("niri_scale", UniformType::_1f), - UniformName::new("geo_size", UniformType::_2f), - UniformName::new("corner_radius", UniformType::_4f), - UniformName::new("input_to_geo", UniformType::Matrix3x3), - ], - ) - .map_err(|err| { - warn!("error compiling clipped surface shader: {err:?}"); - }) - .ok(); + let clipped_surface = try_compile_custom_texture_shader( + "clipped surface", + renderer, + concat!( + include_str!("clipped_surface.frag"), + include_str!("rounding_alpha.frag"), + "\nvec4 postprocess(vec4 color) { return color; }", + ), + &[ + UniformName::new("niri_scale", UniformType::_1f), + UniformName::new("geo_size", UniformType::_2f), + UniformName::new("corner_radius", UniformType::_4f), + UniformName::new("corner_exponent", UniformType::_1f), + UniformName::new("input_to_geo", UniformType::Matrix3x3), + ], + ); - let postprocess_and_clip = renderer - .compile_custom_texture_shader( - concat!( - include_str!("clipped_surface.frag"), - include_str!("rounding_alpha.frag"), - include_str!("postprocess.frag"), - ), - &[ - UniformName::new("niri_scale", UniformType::_1f), - UniformName::new("geo_size", UniformType::_2f), - UniformName::new("corner_radius", UniformType::_4f), - UniformName::new("input_to_geo", UniformType::Matrix3x3), - UniformName::new("noise", UniformType::_1f), - UniformName::new("saturation", UniformType::_1f), - UniformName::new("bg_color", UniformType::_4f), - ], - ) - .map_err(|err| { - warn!("error compiling postprocess_and_clip shader: {err:?}"); - }) - .ok(); + let postprocess_and_clip = try_compile_custom_texture_shader( + "postprocess_and_clip", + renderer, + concat!( + include_str!("clipped_surface.frag"), + include_str!("rounding_alpha.frag"), + include_str!("postprocess.frag"), + ), + &[ + UniformName::new("niri_scale", UniformType::_1f), + UniformName::new("geo_size", UniformType::_2f), + UniformName::new("corner_radius", UniformType::_4f), + UniformName::new("corner_exponent", UniformType::_1f), + UniformName::new("input_to_geo", UniformType::Matrix3x3), + UniformName::new("noise", UniformType::_1f), + UniformName::new("saturation", UniformType::_1f), + UniformName::new("bg_color", UniformType::_4f), + ], + ); let resize = compile_resize_program(renderer, include_str!("resize.frag")) .map_err(|err| { @@ -223,28 +307,53 @@ fn compile_resize_program( renderer: &mut GlesRenderer, src: &str, ) -> Result { + let additional_uniforms = &[ + UniformName::new("niri_input_to_curr_geo", UniformType::Matrix3x3), + UniformName::new("niri_curr_geo_to_prev_geo", UniformType::Matrix3x3), + UniformName::new("niri_curr_geo_to_next_geo", UniformType::Matrix3x3), + UniformName::new("niri_curr_geo_size", UniformType::_2f), + UniformName::new("niri_geo_to_tex_prev", UniformType::Matrix3x3), + UniformName::new("niri_geo_to_tex_next", UniformType::Matrix3x3), + UniformName::new("niri_progress", UniformType::_1f), + UniformName::new("niri_clamped_progress", UniformType::_1f), + UniformName::new("niri_corner_radius", UniformType::_4f), + UniformName::new("niri_corner_exponent", UniformType::_1f), + UniformName::new("niri_clip_to_geometry", UniformType::_1f), + ]; + let texture_uniforms = &["niri_tex_prev", "niri_tex_next"]; + let mut program = include_str!("resize_prelude.frag").to_string(); program.push_str(src); program.push_str(include_str!("resize_epilogue.frag")); - program.push_str(include_str!("rounding_alpha.frag")); - ShaderProgram::compile( + let replaced_program = program.clone() + + "\n".into() + + &include_str!("rounding_alpha.frag") + .replace(ROUNDING_ALPHA_IMPL_PLACEHOLDER, ROUNDING_ALPHA_IMPL); + + match ShaderProgram::compile( renderer, - &program, - &[ - UniformName::new("niri_input_to_curr_geo", UniformType::Matrix3x3), - UniformName::new("niri_curr_geo_to_prev_geo", UniformType::Matrix3x3), - UniformName::new("niri_curr_geo_to_next_geo", UniformType::Matrix3x3), - UniformName::new("niri_curr_geo_size", UniformType::_2f), - UniformName::new("niri_geo_to_tex_prev", UniformType::Matrix3x3), - UniformName::new("niri_geo_to_tex_next", UniformType::Matrix3x3), - UniformName::new("niri_progress", UniformType::_1f), - UniformName::new("niri_clamped_progress", UniformType::_1f), - UniformName::new("niri_corner_radius", UniformType::_4f), - UniformName::new("niri_clip_to_geometry", UniformType::_1f), - ], - &["niri_tex_prev", "niri_tex_next"], - ) + &replaced_program, + additional_uniforms, + texture_uniforms, + ) { + Ok(p) => Ok(p), + Err(err) => { + warn!("error compiling resize shader with support for corner radius exponent: {err:?}"); + let fallback_program = program + + "\n".into() + + &include_str!("rounding_alpha.frag").replace( + ROUNDING_ALPHA_IMPL_PLACEHOLDER, + ROUNDING_ALPHA_IMPL_FALLBACK, + ); + ShaderProgram::compile( + renderer, + &fallback_program, + additional_uniforms, + texture_uniforms, + ) + } + } } pub fn set_custom_resize_program(renderer: &mut GlesRenderer, src: Option<&str>) { diff --git a/src/render_helpers/shaders/resize_epilogue.frag b/src/render_helpers/shaders/resize_epilogue.frag index 82c20d67d6..b97b7cac36 100644 --- a/src/render_helpers/shaders/resize_epilogue.frag +++ b/src/render_helpers/shaders/resize_epilogue.frag @@ -12,7 +12,7 @@ void main() { color = vec4(0.0); } else { // Apply corner rounding inside geometry. - color = color * niri_rounding_alpha(coords_curr_geo.xy * size_curr_geo.xy, size_curr_geo.xy, niri_corner_radius); + color = color * niri_rounding_alpha(coords_curr_geo.xy * size_curr_geo.xy, size_curr_geo.xy, niri_corner_radius, niri_corner_exponent); } } diff --git a/src/render_helpers/shaders/resize_prelude.frag b/src/render_helpers/shaders/resize_prelude.frag index 6e672f423f..84247edd35 100644 --- a/src/render_helpers/shaders/resize_prelude.frag +++ b/src/render_helpers/shaders/resize_prelude.frag @@ -22,9 +22,10 @@ uniform float niri_progress; uniform float niri_clamped_progress; uniform vec4 niri_corner_radius; +uniform float niri_corner_exponent; uniform float niri_clip_to_geometry; uniform float niri_alpha; uniform float niri_scale; -float niri_rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius); +float niri_rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius, float corner_exponent); diff --git a/src/render_helpers/shaders/rounding_alpha.frag b/src/render_helpers/shaders/rounding_alpha.frag index e1c9527e2e..563f08b201 100644 --- a/src/render_helpers/shaders/rounding_alpha.frag +++ b/src/render_helpers/shaders/rounding_alpha.frag @@ -1,4 +1,6 @@ -float niri_rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius) { +@ROUNDING_ALPHA_IMPL@ + +float niri_rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius, float corner_exponent) { vec2 center; float radius; @@ -18,10 +20,5 @@ float niri_rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius) { return 1.0; } - float dist = distance(coords, center); - - // Manual smoothstep() between radius - half_px and radius + half_px - // to avoid a division in clamp(). - float t = clamp((dist - radius) * niri_scale + 0.5, 0.0, 1.0); - return 1.0 - t * t * (3.0 - 2.0 * t); + return niri_rounding_alpha_impl(coords, center, radius, corner_exponent); } diff --git a/src/render_helpers/shaders/rounding_alpha_superellipse.frag b/src/render_helpers/shaders/rounding_alpha_superellipse.frag new file mode 100644 index 0000000000..d2a4038d85 --- /dev/null +++ b/src/render_helpers/shaders/rounding_alpha_superellipse.frag @@ -0,0 +1,22 @@ +float niri_superellipse_dist(vec2 coords, vec2 center, float radius, float exponent) { + vec2 delta = abs(coords - center); + + if (abs(exponent - 2.0) < 0.001) { + return length(delta) - radius; + } + + if (abs(exponent - 1.0) < 0.001) { + return delta.x + delta.y - radius; + } + + vec2 normalized = delta / max(radius, 0.0001); + float lp_norm = pow(pow(normalized.x, exponent) + pow(normalized.y, exponent), 1.0 / exponent); + return (lp_norm - 1.0) * radius; +} + +float niri_rounding_alpha_impl(vec2 coords, vec2 center, float radius, float corner_exponent) { + float exponent = max(corner_exponent, 0.01); + float dist = niri_superellipse_dist(coords, center, radius, exponent); + float half_px = 0.5 / niri_scale; + return 1.0 - smoothstep(-half_px, half_px, dist); +} diff --git a/src/render_helpers/shaders/shadow.frag b/src/render_helpers/shaders/shadow.frag index 98d5fd60f6..0f35cdd412 100644 --- a/src/render_helpers/shaders/shadow.frag +++ b/src/render_helpers/shaders/shadow.frag @@ -16,10 +16,12 @@ uniform float sigma; uniform mat3 input_to_geo; uniform vec2 geo_size; uniform vec4 corner_radius; +uniform float corner_exponent; uniform mat3 window_input_to_geo; uniform vec2 window_geo_size; uniform vec4 window_corner_radius; +uniform float window_corner_exponent; // Based on: https://madebyevan.com/shaders/fast-rounded-rectangle-shadows/ // @@ -40,15 +42,20 @@ vec2 erf(vec2 x) { } // Return the blurred mask along the x dimension -float roundedBoxShadowX(float x, float y, float sigma, float corner, vec2 halfSize) { +float roundedBoxShadowX(float x, float y, float sigma, float corner, vec2 halfSize, float exponent) { float delta = min(halfSize.y - corner - abs(y), 0.0); - float curved = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta)); + float curved; + if (abs(exponent - 2.0) < 0.001) { + curved = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta)); + } else { + @EXPONENT_SHADOW_X_IMPL@ + } vec2 integral = 0.5 + 0.5 * erf((x + vec2(-curved, curved)) * (sqrt(0.5) / sigma)); return integral.y - integral.x; } // Return the mask for the shadow of a box from lower to upper -float roundedBoxShadow(vec2 lower, vec2 upper, vec2 point, float sigma, float corner) { +float roundedBoxShadow(vec2 lower, vec2 upper, vec2 point, float sigma, float corner, float exponent) { // Center everything to make the math easier vec2 center = (lower + upper) * 0.5; vec2 halfSize = (upper - lower) * 0.5; @@ -65,14 +72,14 @@ float roundedBoxShadow(vec2 lower, vec2 upper, vec2 point, float sigma, float co float y = start + step * 0.5; float value = 0.0; for (int i = 0; i < 4; i++) { - value += roundedBoxShadowX(point.x, point.y - y, sigma, corner, halfSize) * gaussian(y, sigma) * step; + value += roundedBoxShadowX(point.x, point.y - y, sigma, corner, halfSize, exponent) * gaussian(y, sigma) * step; y += step; } return value; } -float niri_rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius); +float niri_rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius, float corner_exponent); void main() { vec3 coords_geo = input_to_geo * vec3(niri_v_coords, 1.0); @@ -83,7 +90,7 @@ void main() { float shadow_value; if (sigma < 0.1) { // With low enough sigma just draw a rounded rectangle. - shadow_value = niri_rounding_alpha(coords_geo.xy, geo_size, corner_radius); + shadow_value = niri_rounding_alpha(coords_geo.xy, geo_size, corner_radius, corner_exponent); } else { shadow_value = roundedBoxShadow( vec2(0.0, 0.0), @@ -94,7 +101,8 @@ void main() { // // GTK seems to call blurring separately for the rect and for the 4 corners: // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-4-16/gsk/gpu/shaders/gskgpuboxshadow.glsl - corner_radius.x + corner_radius.x, + corner_exponent ); } color = color * shadow_value; @@ -103,7 +111,7 @@ void main() { if (window_geo_size != vec2(0.0, 0.0)) { if (0.0 <= coords_window_geo.x && coords_window_geo.x <= window_geo_size.x && 0.0 <= coords_window_geo.y && coords_window_geo.y <= window_geo_size.y) { - float alpha = niri_rounding_alpha(coords_window_geo.xy, window_geo_size, window_corner_radius); + float alpha = niri_rounding_alpha(coords_window_geo.xy, window_geo_size, window_corner_radius, window_corner_exponent); color = color * (1.0 - alpha); } } diff --git a/src/render_helpers/shadow.rs b/src/render_helpers/shadow.rs index 0d26201c81..619dfb8073 100644 --- a/src/render_helpers/shadow.rs +++ b/src/render_helpers/shadow.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::rc::Rc; use glam::{Mat3, Vec2}; -use niri_config::{Color, CornerRadius}; +use niri_config::{Color, CornerRadius, DEFAULT_CORNER_RADIUS_EXPONENT}; use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage}; use smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, Uniform}; use smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions}; @@ -30,12 +30,14 @@ struct Parameters { color: Color, sigma: f32, corner_radius: CornerRadius, + corner_exponent: f32, // Should only be used for visual improvements, i.e. corner radius anti-aliasing. scale: f32, alpha: f32, window_geometry: Rectangle, window_corner_radius: CornerRadius, + window_corner_exponent: f32, } impl ShadowRenderElement { @@ -46,9 +48,11 @@ impl ShadowRenderElement { color: Color, sigma: f32, corner_radius: CornerRadius, + corner_exponent: f32, scale: f32, window_geometry: Rectangle, window_corner_radius: CornerRadius, + window_corner_exponent: f32, alpha: f32, ) -> Self { let inner = ShaderRenderElement::empty(ProgramType::Shadow, Kind::Unspecified); @@ -60,10 +64,12 @@ impl ShadowRenderElement { color, sigma, corner_radius, + corner_exponent, scale, alpha, window_geometry, window_corner_radius, + window_corner_exponent, }, }; rv.update_inner(); @@ -80,10 +86,12 @@ impl ShadowRenderElement { color: Default::default(), sigma: 0., corner_radius: Default::default(), + corner_exponent: DEFAULT_CORNER_RADIUS_EXPONENT, scale: 1., alpha: 1., window_geometry: Default::default(), window_corner_radius: Default::default(), + window_corner_exponent: DEFAULT_CORNER_RADIUS_EXPONENT, }, } } @@ -100,9 +108,11 @@ impl ShadowRenderElement { color: Color, sigma: f32, corner_radius: CornerRadius, + corner_exponent: f32, scale: f32, window_geometry: Rectangle, window_corner_radius: CornerRadius, + window_corner_exponent: f32, alpha: f32, ) { let params = Parameters { @@ -112,9 +122,11 @@ impl ShadowRenderElement { sigma, alpha, corner_radius, + corner_exponent, scale, window_geometry, window_corner_radius, + window_corner_exponent, }; if self.params == params { return; @@ -132,9 +144,11 @@ impl ShadowRenderElement { sigma, alpha, corner_radius, + corner_exponent, scale, window_geometry, window_corner_radius, + window_corner_exponent, } = self.params; let area_size = Vec2::new(size.w as f32, size.h as f32); @@ -163,12 +177,14 @@ impl ShadowRenderElement { mat3_uniform("input_to_geo", input_to_geo), Uniform::new("geo_size", geo_size.to_array()), Uniform::new("corner_radius", <[f32; 4]>::from(corner_radius)), + Uniform::new("corner_exponent", corner_exponent), mat3_uniform("window_input_to_geo", window_input_to_geo), Uniform::new("window_geo_size", window_geo_size.to_array()), Uniform::new( "window_corner_radius", <[f32; 4]>::from(window_corner_radius), ), + Uniform::new("window_corner_exponent", window_corner_exponent), ]), HashMap::new(), ); diff --git a/src/render_helpers/xray.rs b/src/render_helpers/xray.rs index f4b4f861e0..7c1bbce9c7 100644 --- a/src/render_helpers/xray.rs +++ b/src/render_helpers/xray.rs @@ -3,7 +3,7 @@ use std::cell::RefCell; use std::rc::Rc; use glam::{Mat3, Vec2}; -use niri_config::CornerRadius; +use niri_config::{CornerRadius, DEFAULT_CORNER_RADIUS_EXPONENT}; use smithay::backend::renderer::element::{Element, Id, RenderElement}; use smithay::backend::renderer::gles::{ GlesError, GlesFrame, GlesRenderer, GlesTexProgram, Uniform, @@ -75,6 +75,7 @@ pub struct XrayElement { input_to_clip_geo: Mat3, clip_geo_size: Vec2, corner_radius: CornerRadius, + corner_exponent: f32, scale: f32, blur: bool, noise: f32, @@ -109,9 +110,11 @@ impl Xray { let zoom = xray_pos.zoom; let pos_in_backdrop = xray_pos.pos_in_backdrop.upscale(zoom); - let (clip_geo, corner_radius) = params - .clip - .unwrap_or((params.geometry, CornerRadius::default())); + let (clip_geo, corner_radius, corner_exponent) = params.clip.unwrap_or(( + params.geometry, + CornerRadius::default(), + DEFAULT_CORNER_RADIUS_EXPONENT, + )); let clip_offset = clip_geo.loc - params.geometry.loc; let clip_pos_in_backdrop = pos_in_backdrop + clip_offset.upscale(zoom); @@ -196,6 +199,7 @@ impl Xray { input_to_clip_geo, clip_geo_size, corner_radius, + corner_exponent, scale: params.scale as f32, blur, noise, @@ -246,6 +250,7 @@ impl Xray { input_to_clip_geo, clip_geo_size, corner_radius: corner_radius.scaled_by(zoom as f32), + corner_exponent, scale: params.scale as f32, blur, noise, @@ -259,11 +264,12 @@ impl Xray { } impl XrayElement { - fn compute_uniforms(&self) -> [Uniform<'static>; 7] { + fn compute_uniforms(&self) -> [Uniform<'static>; 8] { [ Uniform::new("niri_scale", self.scale), Uniform::new("geo_size", <[f32; 2]>::from(self.clip_geo_size)), Uniform::new("corner_radius", <[f32; 4]>::from(self.corner_radius)), + Uniform::new("corner_exponent", self.corner_exponent), mat3_uniform("input_to_geo", self.input_to_clip_geo), Uniform::new("noise", self.noise), Uniform::new("saturation", self.saturation), diff --git a/src/ui/mru.rs b/src/ui/mru.rs index d8ec6de16e..a5c8b049d2 100644 --- a/src/ui/mru.rs +++ b/src/ui/mru.rs @@ -8,7 +8,7 @@ use std::time::Duration; use anyhow::ensure; use niri_config::{ Action, Bind, Color, Config, CornerRadius, GradientInterpolation, Key, Modifiers, MruDirection, - MruFilter, MruScope, Trigger, + MruFilter, MruScope, Trigger, DEFAULT_CORNER_RADIUS_EXPONENT, }; use pango::FontDescription; use pangocairo::cairo::{self, ImageSurface}; @@ -383,8 +383,17 @@ impl Thumbnail { LayoutElementRenderElement::Wayland(elem) => { if let Some(shader) = clip_shader.clone() { if ClippedSurfaceRenderElement::will_clip(&elem, s, geo, radius) { - let elem = - ClippedSurfaceRenderElement::new(elem, s, geo, shader.clone(), radius); + let elem = ClippedSurfaceRenderElement::new( + elem, + s, + geo, + shader.clone(), + radius, + mapped + .rules() + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), + ); return ThumbnailRenderElement::ClippedSurface(elem); } } @@ -411,6 +420,10 @@ impl Thumbnail { Rectangle::from_size(geo.size), 0., radius, + mapped + .rules() + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), scale as f32, 1., ) @@ -536,6 +549,7 @@ impl Thumbnail { false, Rectangle::default(), radius, + DEFAULT_CORNER_RADIUS_EXPONENT, scale, 0.5, ); @@ -557,6 +571,7 @@ impl Thumbnail { false, Rectangle::default(), radius.expanded_by(config.width as f32), + DEFAULT_CORNER_RADIUS_EXPONENT, scale, 1., ); diff --git a/src/window/mapped.rs b/src/window/mapped.rs index 3db1f66cad..291015f15b 100644 --- a/src/window/mapped.rs +++ b/src/window/mapped.rs @@ -1,7 +1,9 @@ use std::cell::{Cell, Ref, RefCell}; use std::time::Duration; -use niri_config::{Color, Config, CornerRadius, GradientInterpolation, WindowRule}; +use niri_config::{ + Color, Config, CornerRadius, GradientInterpolation, WindowRule, DEFAULT_CORNER_RADIUS_EXPONENT, +}; use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement; use smithay::backend::renderer::element::Kind; use smithay::backend::renderer::gles::GlesRenderer; @@ -532,6 +534,9 @@ impl Mapped { Rectangle::from_size(geo.size), 0., radius, + self.rules + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), scale.x as f32, 1., ) @@ -729,6 +734,9 @@ impl LayoutElement for Mapped { surface_anim_scale, self.blur_config, popup_rules.geometry_corner_radius.unwrap_or_default(), + popup_rules + .geometry_corner_radius_exponent + .unwrap_or(DEFAULT_CORNER_RADIUS_EXPONENT), effect, false, xray_pos, @@ -745,6 +753,7 @@ impl LayoutElement for Mapped { clip_to_geometry: bool, surface_anim_scale: Scale, radius: CornerRadius, + exponent: f32, xray_pos: XrayPos, push: &mut dyn FnMut(BackgroundEffectElement), ) { @@ -760,6 +769,7 @@ impl LayoutElement for Mapped { surface_anim_scale, self.blur_config, radius, + exponent, self.rules.background_effect, should_block_out, xray_pos, diff --git a/src/window/mod.rs b/src/window/mod.rs index 14527cd242..5d6d50abd7 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -101,6 +101,8 @@ pub struct ResolvedWindowRules { /// Corner radius to assume this window has. pub geometry_corner_radius: Option, + /// Exponent to use for corner rounding. + pub geometry_corner_radius_exponent: Option, /// Whether to clip this window to its geometry, including the corner radius. pub clip_to_geometry: Option, @@ -284,6 +286,9 @@ impl ResolvedWindowRules { if let Some(x) = rule.geometry_corner_radius { resolved.geometry_corner_radius = Some(x); } + if let Some(x) = rule.geometry_corner_radius_exponent { + resolved.geometry_corner_radius_exponent = Some(x.0 as f32); + } if let Some(x) = rule.clip_to_geometry { resolved.clip_to_geometry = Some(x); }