mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-23 02:05:33 +07:00
Implement place-within-backdrop layer rule
This commit is contained in:
@@ -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)]
|
||||
|
||||
@@ -4888,6 +4888,7 @@ mod tests {
|
||||
inactive_color: None,
|
||||
},
|
||||
geometry_corner_radius: None,
|
||||
place_within_backdrop: None,
|
||||
},
|
||||
],
|
||||
binds: Binds(
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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))
|
||||
});
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user