Implement place-within-backdrop layer rule

This commit is contained in:
Ivan Molodetskikh
2025-05-06 16:51:18 +03:00
parent fe682938db
commit 3e31c134a6
8 changed files with 137 additions and 23 deletions
+2
View File
@@ -15,6 +15,8 @@ pub struct LayerRule {
pub shadow: ShadowRule,
#[knuffel(child)]
pub geometry_corner_radius: Option<CornerRadius>,
#[knuffel(child, unwrap(argument))]
pub place_within_backdrop: Option<bool>,
}
#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]
+1
View File
@@ -4888,6 +4888,7 @@ mod tests {
inactive_color: None,
},
geometry_corner_radius: None,
place_within_backdrop: None,
},
],
binds: Binds(
+11 -4
View File
@@ -319,10 +319,17 @@ impl XdgShellHandler for State {
// FIXME: somewhere here we probably need to check is_overview_open to match the logic
// in update_keyboard_focus().
if layers
.layer_for_surface(&root, WindowSurfaceType::TOPLEVEL)
.is_none()
{
if let Some(layer) = layers.layer_for_surface(&root, WindowSurfaceType::TOPLEVEL) {
// This is a grab for a layer surface.
if let Some(mapped) = self.niri.mapped_layer_surfaces.get(layer) {
if mapped.place_within_backdrop() {
trace!("ignoring popup grab for a layer surface within overview backdrop");
let _ = PopupManager::dismiss_popup(&root, &popup);
return;
}
}
} else {
// This is a grab for a regular window; check that there's no layer surface with a
// higher input priority.
+18
View File
@@ -6,6 +6,7 @@ use smithay::backend::renderer::element::surface::{
use smithay::backend::renderer::element::Kind;
use smithay::desktop::{LayerSurface, PopupManager};
use smithay::utils::{Logical, Point, Scale, Size};
use smithay::wayland::shell::wlr_layer::{ExclusiveZone, Layer};
use super::ResolvedLayerRules;
use crate::layout::shadow::Shadow;
@@ -96,6 +97,23 @@ impl MappedLayer {
true
}
pub fn place_within_backdrop(&self) -> bool {
if !self.rules.place_within_backdrop {
return false;
}
if self.surface.layer() != Layer::Background {
return false;
}
let state = self.surface.cached_state();
if state.exclusive_zone != ExclusiveZone::DontCare {
return false;
}
true
}
pub fn render<R: NiriRenderer>(
&self,
renderer: &mut R,
+7
View File
@@ -19,6 +19,9 @@ pub struct ResolvedLayerRules {
/// Corner radius to assume this layer surface has.
pub geometry_corner_radius: Option<CornerRadius>,
/// Whether to place this layer surface within the overview backdrop.
pub place_within_backdrop: bool,
}
impl ResolvedLayerRules {
@@ -37,6 +40,7 @@ impl ResolvedLayerRules {
inactive_color: None,
},
geometry_corner_radius: None,
place_within_backdrop: false,
}
}
@@ -73,6 +77,9 @@ impl ResolvedLayerRules {
if let Some(x) = rule.geometry_corner_radius {
resolved.geometry_corner_radius = Some(x);
}
if let Some(x) = rule.place_within_backdrop {
resolved.place_within_backdrop = x;
}
resolved.shadow.merge_with(&rule.shadow);
}
+58 -17
View File
@@ -992,9 +992,19 @@ impl State {
// Clean up on-demand layer surface focus if necessary.
if let Some(surface) = &self.niri.layer_shell_on_demand_focus {
// Still alive and has on-demand interactivity.
let good = surface.alive()
let mut good = surface.alive()
&& surface.cached_state().keyboard_interactivity
== wlr_layer::KeyboardInteractivity::OnDemand;
// Check if it moved to the overview backdrop.
if let Some(mapped) = self.niri.mapped_layer_surfaces.get(surface) {
if mapped.place_within_backdrop() {
good = false;
}
} else {
good = false;
}
if !good {
self.niri.layer_shell_on_demand_focus = None;
}
@@ -1042,6 +1052,11 @@ impl State {
return None;
}
let mapped = self.niri.mapped_layer_surfaces.get(surface)?;
if mapped.place_within_backdrop() {
return None;
}
let surface = surface.wl_surface().clone();
Some(KeyboardFocus::LayerShell { surface })
})
@@ -1513,11 +1528,10 @@ impl State {
resized_outputs.push(output.clone());
}
let mut background_color = config
let background_color = config
.map(|c| c.background_color)
.unwrap_or(DEFAULT_BACKGROUND_COLOR)
.to_array_unpremul();
background_color[3] = 1.;
let background_color = Color32F::from(background_color);
let mut backdrop_color = config
@@ -2710,11 +2724,10 @@ impl Niri {
.map(|c| ipc_transform_to_smithay(c.transform))
.unwrap_or(Transform::Normal);
let mut background_color = c
let background_color = c
.map(|c| c.background_color)
.unwrap_or(DEFAULT_BACKGROUND_COLOR)
.to_array_unpremul();
background_color[3] = 1.;
let mut backdrop_color = c
.and_then(|c| c.backdrop_color)
@@ -2991,6 +3004,11 @@ impl Niri {
.layers_on(layer)
.rev()
.find_map(|layer_surface| {
let mapped = self.mapped_layer_surfaces.get(layer_surface)?;
if mapped.place_within_backdrop() {
return None;
}
let mut layer_pos_within_output =
layers.layer_geometry(layer_surface).unwrap().loc.to_f64();
@@ -3141,6 +3159,11 @@ impl Niri {
.layers_on(layer)
.rev()
.find_map(|layer_surface| {
let mapped = self.mapped_layer_surfaces.get(layer_surface)?;
if mapped.place_within_backdrop() {
return None;
}
let mut layer_pos_within_output =
layers.layer_geometry(layer_surface).unwrap().loc.to_f64();
@@ -4021,19 +4044,27 @@ impl Niri {
// Get layer-shell elements.
let layer_map = layer_map_for_output(output);
let mut extend_from_layer = |elements: &mut SplitElements<LayerSurfaceRenderElement<R>>,
layer| {
self.render_layer(renderer, target, output_scale, &layer_map, layer, elements);
};
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,
);
};
// The overlay layer elements go next.
let mut layer_elems = SplitElements::default();
extend_from_layer(&mut layer_elems, Layer::Overlay);
extend_from_layer(&mut layer_elems, Layer::Overlay, false);
elements.extend(layer_elems.into_iter().map(OutputRenderElements::from));
// Collect the top layer elements.
let mut layer_elems = SplitElements::default();
extend_from_layer(&mut layer_elems, Layer::Top);
extend_from_layer(&mut layer_elems, Layer::Top, false);
let top_layer = layer_elems;
// When rendering above the top layer, we put the regular monitor elements first.
@@ -4041,8 +4072,8 @@ impl Niri {
if mon.render_above_top_layer() {
// Collect all other layer-shell elements.
let mut layer_elems = SplitElements::default();
extend_from_layer(&mut layer_elems, Layer::Bottom);
extend_from_layer(&mut layer_elems, Layer::Background);
extend_from_layer(&mut layer_elems, Layer::Bottom, false);
extend_from_layer(&mut layer_elems, Layer::Background, false);
elements.extend(
int_move_elements
@@ -4062,8 +4093,7 @@ impl Niri {
);
elements.extend(top_layer.into_iter().map(OutputRenderElements::from));
elements.extend(layer_elems.popups.drain(..).map(OutputRenderElements::from));
elements.extend(layer_elems.normal.drain(..).map(OutputRenderElements::from));
elements.extend(layer_elems.into_iter().map(OutputRenderElements::from));
elements.push(OutputRenderElements::from(background));
@@ -4090,8 +4120,8 @@ impl Niri {
for (ws_geo, ws_elements) in monitor_elements {
// Collect all other layer-shell elements.
let mut layer_elems = SplitElements::default();
extend_from_layer(&mut layer_elems, Layer::Bottom);
extend_from_layer(&mut layer_elems, Layer::Background);
extend_from_layer(&mut layer_elems, Layer::Bottom, false);
extend_from_layer(&mut layer_elems, Layer::Background, false);
elements.extend(
layer_elems
@@ -4126,6 +4156,10 @@ impl Niri {
}
// Then the backdrop.
let mut layer_elems = SplitElements::default();
extend_from_layer(&mut layer_elems, Layer::Background, true);
elements.extend(layer_elems.into_iter().map(OutputRenderElements::from));
elements.push(backdrop);
if self.debug_draw_opaque_regions {
@@ -4135,6 +4169,7 @@ impl Niri {
elements
}
#[allow(clippy::too_many_arguments)]
fn render_layer<R: NiriRenderer>(
&self,
renderer: &mut R,
@@ -4143,10 +4178,16 @@ impl Niri {
layer_map: &LayerMap,
layer: Layer,
elements: &mut SplitElements<LayerSurfaceRenderElement<R>>,
for_backdrop: bool,
) {
// LayerMap returns layers in reverse stacking order.
let iter = layer_map.layers_on(layer).rev().filter_map(|surface| {
let mapped = self.mapped_layer_surfaces.get(surface)?;
if for_backdrop != mapped.place_within_backdrop() {
return None;
}
let geo = layer_map.layer_geometry(surface)?;
Some((mapped, geo))
});
+21
View File
@@ -32,6 +32,8 @@ layer-rule {
}
geometry-corner-radius 12
place-within-backdrop true
}
```
@@ -149,6 +151,25 @@ This setting will only affect the shadow—it will round its corners to match th
```kdl
layer-rule {
match namespace="^launcher$"
geometry-corner-radius 12
}
```
#### `place-within-backdrop`
<sup>Since: next release</sup>
Set to `true` to place the surface into the backdrop visible in the [Overview](./Overview.md) and between workspaces.
This will only work for *background* layer surfaces that ignore exclusive zones (typical for wallpaper tools).
```kdl
// Put swaybg inside the overview backdrop.
layer-rule {
match namespace="^wallpaper$"
place-within-backdrop true
}
```
+18 -1
View File
@@ -14,7 +14,7 @@ While in the overview, all keyboard binds keep working, while pointing devices g
> [!TIP]
> The overview needs to draw a background under every workspace.
> So, layer-shell surfaces work this way: the *background* and *bottom* layers zoom out and remain under workspaces, while the *top* and *overlay* layers remain on top of the overview.
> So, layer-shell surfaces work this way: the *background* and *bottom* layers zoom out and remain on the workspaces, while the *top* and *overlay* layers remain on top of the overview.
>
> Put your bar on the *top* layer.
@@ -59,3 +59,20 @@ gestures {
}
}
```
### Backdrop customization
Apart from setting a custom backdrop color like described above, you can also put a layer-shell wallpaper into the backdrop with a [layer rule](./Configuration:-Layer-Rules.md#place-within-backdrop), for example:
```kdl
// Put swaybg inside the overview backdrop.
layer-rule {
match namespace="^wallpaper$"
place-within-backdrop true
}
```
This will only work for *background* layer surfaces that ignore exclusive zones (typical for wallpaper tools).
You can run two different wallpaper tools (like swaybg and swww), one for the backdrop and one for the normal workspace background.
This way you could set the backdrop one to a blurred version of the wallpaper for a nice effect.