-
-
Notifications
You must be signed in to change notification settings - Fork 122
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement layer rules: opacity and block-out-from
- Loading branch information
Showing
11 changed files
with
401 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
use crate::{BlockOutFrom, RegexEq}; | ||
|
||
#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)] | ||
pub struct LayerRule { | ||
#[knuffel(children(name = "match"))] | ||
pub matches: Vec<Match>, | ||
#[knuffel(children(name = "exclude"))] | ||
pub excludes: Vec<Match>, | ||
|
||
#[knuffel(child, unwrap(argument))] | ||
pub opacity: Option<f32>, | ||
#[knuffel(child, unwrap(argument))] | ||
pub block_out_from: Option<BlockOutFrom>, | ||
} | ||
|
||
#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)] | ||
pub struct Match { | ||
#[knuffel(property, str)] | ||
pub namespace: Option<RegexEq>, | ||
#[knuffel(property)] | ||
pub at_startup: Option<bool>, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
use std::cell::RefCell; | ||
|
||
use niri_config::layer_rule::LayerRule; | ||
use smithay::backend::renderer::element::surface::{ | ||
render_elements_from_surface_tree, WaylandSurfaceRenderElement, | ||
}; | ||
use smithay::backend::renderer::element::Kind; | ||
use smithay::desktop::{LayerSurface, PopupManager}; | ||
use smithay::utils::{Logical, Rectangle, Scale}; | ||
|
||
use super::ResolvedLayerRules; | ||
use crate::niri_render_elements; | ||
use crate::render_helpers::renderer::NiriRenderer; | ||
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement}; | ||
use crate::render_helpers::{RenderTarget, SplitElements}; | ||
|
||
#[derive(Debug)] | ||
pub struct MappedLayer { | ||
/// The surface itself. | ||
surface: LayerSurface, | ||
|
||
/// Up-to-date rules. | ||
rules: ResolvedLayerRules, | ||
|
||
/// Buffer to draw instead of the surface when it should be blocked out. | ||
block_out_buffer: RefCell<SolidColorBuffer>, | ||
} | ||
|
||
niri_render_elements! { | ||
LayerSurfaceRenderElement<R> => { | ||
Wayland = WaylandSurfaceRenderElement<R>, | ||
SolidColor = SolidColorRenderElement, | ||
} | ||
} | ||
|
||
impl MappedLayer { | ||
pub fn new(surface: LayerSurface, rules: ResolvedLayerRules) -> Self { | ||
Self { | ||
surface, | ||
rules, | ||
block_out_buffer: RefCell::new(SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.])), | ||
} | ||
} | ||
|
||
pub fn surface(&self) -> &LayerSurface { | ||
&self.surface | ||
} | ||
|
||
pub fn rules(&self) -> &ResolvedLayerRules { | ||
&self.rules | ||
} | ||
|
||
/// Recomputes the resolved layer rules and returns whether they changed. | ||
pub fn recompute_layer_rules(&mut self, rules: &[LayerRule], is_at_startup: bool) -> bool { | ||
let new_rules = ResolvedLayerRules::compute(rules, &self.surface, is_at_startup); | ||
if new_rules == self.rules { | ||
return false; | ||
} | ||
|
||
self.rules = new_rules; | ||
true | ||
} | ||
|
||
pub fn render<R: NiriRenderer>( | ||
&self, | ||
renderer: &mut R, | ||
geometry: Rectangle<i32, Logical>, | ||
scale: Scale<f64>, | ||
target: RenderTarget, | ||
) -> SplitElements<LayerSurfaceRenderElement<R>> { | ||
let mut rv = SplitElements::default(); | ||
|
||
let alpha = self.rules.opacity.unwrap_or(1.).clamp(0., 1.); | ||
|
||
if target.should_block_out(self.rules.block_out_from) { | ||
// Round to physical pixels. | ||
let geometry = geometry | ||
.to_f64() | ||
.to_physical_precise_round(scale) | ||
.to_logical(scale); | ||
|
||
let mut buffer = self.block_out_buffer.borrow_mut(); | ||
buffer.resize(geometry.size.to_f64()); | ||
let elem = SolidColorRenderElement::from_buffer( | ||
&buffer, | ||
geometry.loc, | ||
alpha, | ||
Kind::Unspecified, | ||
); | ||
rv.normal.push(elem.into()); | ||
} else { | ||
// Layer surfaces don't have extra geometry like windows. | ||
let buf_pos = geometry.loc; | ||
|
||
let surface = self.surface.wl_surface(); | ||
for (popup, popup_offset) in PopupManager::popups_for_surface(surface) { | ||
// Layer surfaces don't have extra geometry like windows. | ||
let offset = popup_offset - popup.geometry().loc; | ||
|
||
rv.popups.extend(render_elements_from_surface_tree( | ||
renderer, | ||
popup.wl_surface(), | ||
(buf_pos + offset).to_physical_precise_round(scale), | ||
scale, | ||
alpha, | ||
Kind::Unspecified, | ||
)); | ||
} | ||
|
||
rv.normal = render_elements_from_surface_tree( | ||
renderer, | ||
surface, | ||
buf_pos.to_physical_precise_round(scale), | ||
scale, | ||
alpha, | ||
Kind::Unspecified, | ||
); | ||
} | ||
|
||
rv | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
use niri_config::layer_rule::{LayerRule, Match}; | ||
use niri_config::BlockOutFrom; | ||
use smithay::desktop::LayerSurface; | ||
|
||
pub mod mapped; | ||
pub use mapped::MappedLayer; | ||
|
||
/// Rules fully resolved for a layer-shell surface. | ||
#[derive(Debug, PartialEq)] | ||
pub struct ResolvedLayerRules { | ||
/// Extra opacity to draw this window with. | ||
pub opacity: Option<f32>, | ||
/// Whether to block out this window from certain render targets. | ||
pub block_out_from: Option<BlockOutFrom>, | ||
} | ||
|
||
impl ResolvedLayerRules { | ||
pub const fn empty() -> Self { | ||
Self { | ||
opacity: None, | ||
block_out_from: None, | ||
} | ||
} | ||
|
||
pub fn compute(rules: &[LayerRule], surface: &LayerSurface, is_at_startup: bool) -> Self { | ||
let _span = tracy_client::span!("ResolvedLayerRules::compute"); | ||
|
||
let mut resolved = ResolvedLayerRules::empty(); | ||
|
||
for rule in rules { | ||
let matches = |m: &Match| { | ||
if let Some(at_startup) = m.at_startup { | ||
if at_startup != is_at_startup { | ||
return false; | ||
} | ||
} | ||
|
||
surface_matches(surface, m) | ||
}; | ||
|
||
if !(rule.matches.is_empty() || rule.matches.iter().any(matches)) { | ||
continue; | ||
} | ||
|
||
if rule.excludes.iter().any(matches) { | ||
continue; | ||
} | ||
|
||
if let Some(x) = rule.opacity { | ||
resolved.opacity = Some(x); | ||
} | ||
if let Some(x) = rule.block_out_from { | ||
resolved.block_out_from = Some(x); | ||
} | ||
} | ||
|
||
resolved | ||
} | ||
} | ||
|
||
fn surface_matches(surface: &LayerSurface, m: &Match) -> bool { | ||
if let Some(namespace_re) = &m.namespace { | ||
if !namespace_re.0.is_match(surface.namespace()) { | ||
return false; | ||
} | ||
} | ||
|
||
true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.