mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Implement baba-is-float for layers
This commit is contained in:
@@ -17,6 +17,8 @@ pub struct LayerRule {
|
||||
pub geometry_corner_radius: Option<CornerRadius>,
|
||||
#[knuffel(child, unwrap(argument))]
|
||||
pub place_within_backdrop: Option<bool>,
|
||||
#[knuffel(child, unwrap(argument))]
|
||||
pub baba_is_float: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]
|
||||
|
||||
@@ -4985,6 +4985,7 @@ mod tests {
|
||||
},
|
||||
geometry_corner_radius: None,
|
||||
place_within_backdrop: None,
|
||||
baba_is_float: None,
|
||||
},
|
||||
],
|
||||
binds: Binds(
|
||||
|
||||
@@ -12,7 +12,7 @@ use smithay::wayland::shell::xdg::PopupSurface;
|
||||
|
||||
use crate::layer::{MappedLayer, ResolvedLayerRules};
|
||||
use crate::niri::State;
|
||||
use crate::utils::{is_mapped, send_scale_transform};
|
||||
use crate::utils::{is_mapped, output_size, send_scale_transform};
|
||||
|
||||
impl WlrLayerShellHandler for State {
|
||||
fn shell_state(&mut self) -> &mut WlrLayerShellState {
|
||||
@@ -125,10 +125,23 @@ impl State {
|
||||
// Resolve rules for newly mapped layer surfaces.
|
||||
if was_unmapped {
|
||||
let config = self.niri.config.borrow();
|
||||
|
||||
let rules = &config.layer_rules;
|
||||
let rules =
|
||||
ResolvedLayerRules::compute(rules, layer, self.niri.is_at_startup);
|
||||
let mapped = MappedLayer::new(layer.clone(), rules, &config);
|
||||
|
||||
let output_size = output_size(&output);
|
||||
let scale = output.current_scale().fractional_scale();
|
||||
|
||||
let mapped = MappedLayer::new(
|
||||
layer.clone(),
|
||||
rules,
|
||||
output_size,
|
||||
scale,
|
||||
self.niri.clock.clone(),
|
||||
&config,
|
||||
);
|
||||
|
||||
let prev = self
|
||||
.niri
|
||||
.mapped_layer_surfaces
|
||||
|
||||
+48
-5
@@ -9,12 +9,14 @@ use smithay::utils::{Logical, Point, Scale, Size};
|
||||
use smithay::wayland::shell::wlr_layer::{ExclusiveZone, Layer};
|
||||
|
||||
use super::ResolvedLayerRules;
|
||||
use crate::animation::Clock;
|
||||
use crate::layout::shadow::Shadow;
|
||||
use crate::niri_render_elements;
|
||||
use crate::render_helpers::renderer::NiriRenderer;
|
||||
use crate::render_helpers::shadow::ShadowRenderElement;
|
||||
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
|
||||
use crate::render_helpers::{RenderTarget, SplitElements};
|
||||
use crate::utils::{baba_is_float_offset, round_logical_in_physical};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MappedLayer {
|
||||
@@ -29,6 +31,15 @@ pub struct MappedLayer {
|
||||
|
||||
/// The shadow around the surface.
|
||||
shadow: Shadow,
|
||||
|
||||
/// The view size for the layer surface's output.
|
||||
view_size: Size<f64, Logical>,
|
||||
|
||||
/// Scale of the output the layer surface is on (and rounds its sizes to).
|
||||
scale: f64,
|
||||
|
||||
/// Clock for driving animations.
|
||||
clock: Clock,
|
||||
}
|
||||
|
||||
niri_render_elements! {
|
||||
@@ -40,7 +51,14 @@ niri_render_elements! {
|
||||
}
|
||||
|
||||
impl MappedLayer {
|
||||
pub fn new(surface: LayerSurface, rules: ResolvedLayerRules, config: &Config) -> Self {
|
||||
pub fn new(
|
||||
surface: LayerSurface,
|
||||
rules: ResolvedLayerRules,
|
||||
view_size: Size<f64, Logical>,
|
||||
scale: f64,
|
||||
clock: Clock,
|
||||
config: &Config,
|
||||
) -> Self {
|
||||
let mut shadow_config = config.layout.shadow;
|
||||
// Shadows for layer surfaces need to be explicitly enabled.
|
||||
shadow_config.on = false;
|
||||
@@ -50,7 +68,10 @@ impl MappedLayer {
|
||||
surface,
|
||||
rules,
|
||||
block_out_buffer: SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.]),
|
||||
view_size,
|
||||
scale,
|
||||
shadow: Shadow::new(shadow_config),
|
||||
clock,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,16 +87,27 @@ impl MappedLayer {
|
||||
self.shadow.update_shaders();
|
||||
}
|
||||
|
||||
pub fn update_render_elements(&mut self, size: Size<f64, Logical>, scale: Scale<f64>) {
|
||||
pub fn update_sizes(&mut self, view_size: Size<f64, Logical>, scale: f64) {
|
||||
self.view_size = view_size;
|
||||
self.scale = scale;
|
||||
}
|
||||
|
||||
pub fn update_render_elements(&mut self, size: Size<f64, Logical>) {
|
||||
// Round to physical pixels.
|
||||
let size = size.to_physical_precise_round(scale).to_logical(scale);
|
||||
let size = size
|
||||
.to_physical_precise_round(self.scale)
|
||||
.to_logical(self.scale);
|
||||
|
||||
self.block_out_buffer.resize(size);
|
||||
|
||||
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, scale.x, 1.);
|
||||
.update_render_elements(size, true, radius, self.scale, 1.);
|
||||
}
|
||||
|
||||
pub fn are_animations_ongoing(&self) -> bool {
|
||||
self.rules.baba_is_float
|
||||
}
|
||||
|
||||
pub fn surface(&self) -> &LayerSurface {
|
||||
@@ -114,16 +146,27 @@ impl MappedLayer {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn bob_offset(&self) -> Point<f64, Logical> {
|
||||
if !self.rules.baba_is_float {
|
||||
return Point::from((0., 0.));
|
||||
}
|
||||
|
||||
let y = baba_is_float_offset(self.clock.now(), self.view_size.h);
|
||||
let y = round_logical_in_physical(self.scale, y);
|
||||
Point::from((0., y))
|
||||
}
|
||||
|
||||
pub fn render<R: NiriRenderer>(
|
||||
&self,
|
||||
renderer: &mut R,
|
||||
location: Point<f64, Logical>,
|
||||
scale: Scale<f64>,
|
||||
target: RenderTarget,
|
||||
) -> SplitElements<LayerSurfaceRenderElement<R>> {
|
||||
let mut rv = SplitElements::default();
|
||||
|
||||
let scale = Scale::from(self.scale);
|
||||
let alpha = self.rules.opacity.unwrap_or(1.).clamp(0., 1.);
|
||||
let location = location + self.bob_offset();
|
||||
|
||||
if target.should_block_out(self.rules.block_out_from) {
|
||||
// Round to physical pixels.
|
||||
|
||||
@@ -22,6 +22,9 @@ pub struct ResolvedLayerRules {
|
||||
|
||||
/// Whether to place this layer surface within the overview backdrop.
|
||||
pub place_within_backdrop: bool,
|
||||
|
||||
/// Whether to bob this window up and down.
|
||||
pub baba_is_float: bool,
|
||||
}
|
||||
|
||||
impl ResolvedLayerRules {
|
||||
@@ -41,6 +44,7 @@ impl ResolvedLayerRules {
|
||||
},
|
||||
geometry_corner_radius: None,
|
||||
place_within_backdrop: false,
|
||||
baba_is_float: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +84,9 @@ impl ResolvedLayerRules {
|
||||
if let Some(x) = rule.place_within_backdrop {
|
||||
resolved.place_within_backdrop = x;
|
||||
}
|
||||
if let Some(x) = rule.baba_is_float {
|
||||
resolved.baba_is_float = x;
|
||||
}
|
||||
|
||||
resolved.shadow.merge_with(&rule.shadow);
|
||||
}
|
||||
|
||||
+2
-4
@@ -25,8 +25,8 @@ use crate::render_helpers::shadow::ShadowRenderElement;
|
||||
use crate::render_helpers::snapshot::RenderSnapshot;
|
||||
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
|
||||
use crate::render_helpers::RenderTarget;
|
||||
use crate::utils::round_logical_in_physical;
|
||||
use crate::utils::transaction::Transaction;
|
||||
use crate::utils::{baba_is_float_offset, round_logical_in_physical};
|
||||
|
||||
/// Toplevel window with decorations.
|
||||
#[derive(Debug)]
|
||||
@@ -800,9 +800,7 @@ impl<W: LayoutElement> Tile<W> {
|
||||
return Point::from((0., 0.));
|
||||
}
|
||||
|
||||
let now = self.clock.now().as_secs_f64();
|
||||
let amplitude = self.view_size.h / 96.;
|
||||
let y = amplitude * ((f64::consts::TAU * now / 3.6).sin() - 1.);
|
||||
let y = baba_is_float_offset(self.clock.now(), self.view_size.h);
|
||||
let y = round_logical_in_physical(self.scale, y);
|
||||
Point::from((0., y))
|
||||
}
|
||||
|
||||
+22
-13
@@ -2896,6 +2896,10 @@ impl Niri {
|
||||
layer.with_surfaces(|surface, data| {
|
||||
send_scale_transform(surface, data, scale, transform);
|
||||
});
|
||||
|
||||
if let Some(mapped) = self.mapped_layer_surfaces.get_mut(layer) {
|
||||
mapped.update_sizes(output_size, scale.fractional_scale());
|
||||
}
|
||||
}
|
||||
layer_map.arrange();
|
||||
}
|
||||
@@ -2980,8 +2984,12 @@ impl Niri {
|
||||
.layers_on(layer)
|
||||
.rev()
|
||||
.find_map(|layer| {
|
||||
let layer_pos_within_output =
|
||||
let mapped = self.mapped_layer_surfaces.get(layer)?;
|
||||
|
||||
let mut layer_pos_within_output =
|
||||
layers.layer_geometry(layer).unwrap().loc.to_f64();
|
||||
layer_pos_within_output += mapped.bob_offset();
|
||||
|
||||
let surface_type = if popup {
|
||||
WindowSurfaceType::POPUP
|
||||
} else {
|
||||
@@ -3042,6 +3050,7 @@ impl Niri {
|
||||
|
||||
let mut layer_pos_within_output =
|
||||
layers.layer_geometry(layer_surface).unwrap().loc.to_f64();
|
||||
layer_pos_within_output += mapped.bob_offset();
|
||||
|
||||
// Background and bottom layers move together with the workspaces.
|
||||
let mon = self.layout.monitor_for_output(output)?;
|
||||
@@ -3197,6 +3206,7 @@ impl Niri {
|
||||
|
||||
let mut layer_pos_within_output =
|
||||
layers.layer_geometry(layer_surface).unwrap().loc.to_f64();
|
||||
layer_pos_within_output += mapped.bob_offset();
|
||||
|
||||
// Background and bottom layers move together with the workspaces.
|
||||
if matches!(layer, Layer::Background | Layer::Bottom) {
|
||||
@@ -3924,7 +3934,7 @@ impl Niri {
|
||||
continue;
|
||||
};
|
||||
|
||||
mapped.update_render_elements(geo.size.to_f64(), scale);
|
||||
mapped.update_render_elements(geo.size.to_f64());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4077,15 +4087,7 @@ impl Niri {
|
||||
let layer_map = layer_map_for_output(output);
|
||||
let mut extend_from_layer =
|
||||
|elements: &mut SplitElements<LayerSurfaceRenderElement<R>>, layer, for_backdrop| {
|
||||
self.render_layer(
|
||||
renderer,
|
||||
target,
|
||||
output_scale,
|
||||
&layer_map,
|
||||
layer,
|
||||
elements,
|
||||
for_backdrop,
|
||||
);
|
||||
self.render_layer(renderer, target, &layer_map, layer, elements, for_backdrop);
|
||||
};
|
||||
|
||||
// The overlay layer elements go next.
|
||||
@@ -4205,7 +4207,6 @@ impl Niri {
|
||||
&self,
|
||||
renderer: &mut R,
|
||||
target: RenderTarget,
|
||||
scale: Scale<f64>,
|
||||
layer_map: &LayerMap,
|
||||
layer: Layer,
|
||||
elements: &mut SplitElements<LayerSurfaceRenderElement<R>>,
|
||||
@@ -4223,7 +4224,7 @@ impl Niri {
|
||||
Some((mapped, geo))
|
||||
});
|
||||
for (mapped, geo) in iter {
|
||||
elements.extend(mapped.render(renderer, geo.loc.to_f64(), scale, target));
|
||||
elements.extend(mapped.render(renderer, geo.loc.to_f64(), target));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4258,6 +4259,14 @@ impl Niri {
|
||||
.cursor_manager
|
||||
.is_current_cursor_animated(output.current_scale().integer_scale());
|
||||
|
||||
// Also check layer surfaces.
|
||||
if !state.unfinished_animations_remain {
|
||||
state.unfinished_animations_remain |= layer_map_for_output(output)
|
||||
.layers()
|
||||
.filter_map(|surface| self.mapped_layer_surfaces.get(surface))
|
||||
.any(|mapped| mapped.are_animations_ongoing());
|
||||
}
|
||||
|
||||
// Render.
|
||||
res = backend.render(self, output, target_presentation_time);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::cmp::{max, min};
|
||||
use std::f64;
|
||||
use std::ffi::{CString, OsStr};
|
||||
use std::io::Write;
|
||||
use std::os::unix::prelude::OsStrExt;
|
||||
@@ -396,6 +397,12 @@ pub fn center_preferring_top_left_in_area(
|
||||
area.loc + offset
|
||||
}
|
||||
|
||||
pub fn baba_is_float_offset(now: Duration, view_height: f64) -> f64 {
|
||||
let now = now.as_secs_f64();
|
||||
let amplitude = view_height / 96.;
|
||||
amplitude * ((f64::consts::TAU * now / 3.6).sin() - 1.)
|
||||
}
|
||||
|
||||
#[cfg(feature = "dbus")]
|
||||
pub fn show_screenshot_notification(image_path: Option<PathBuf>) -> anyhow::Result<()> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -32,8 +32,8 @@ layer-rule {
|
||||
}
|
||||
|
||||
geometry-corner-radius 12
|
||||
|
||||
place-within-backdrop true
|
||||
baba-is-float true
|
||||
}
|
||||
```
|
||||
|
||||
@@ -174,3 +174,20 @@ layer-rule {
|
||||
place-within-backdrop true
|
||||
}
|
||||
```
|
||||
|
||||
#### `baba-is-float`
|
||||
|
||||
<sup>Since: next release</sup>
|
||||
|
||||
Make your layer surfaces FLOAT up and down.
|
||||
|
||||
This is a natural extension of the [April Fools' 2025 feature](./Configuration:-Window-Rules.md#baba-is-float).
|
||||
|
||||
```kdl
|
||||
// Make fuzzel FLOAT.
|
||||
layer-rule {
|
||||
match namespace="^launcher$"
|
||||
|
||||
baba-is-float true
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user