aboutsummaryrefslogtreecommitdiff
path: root/src/layer
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2024-11-14 11:33:08 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2024-11-14 12:05:30 +0300
commit1a0612cbfd0abee0796efa86470226686ae78f21 (patch)
tree809037c94948e0107614f5d01564712512468332 /src/layer
parentfbbd3ba349223f7cc4ebeaa397f7c48e880a7c30 (diff)
downloadniri-1a0612cbfd0abee0796efa86470226686ae78f21.tar.gz
niri-1a0612cbfd0abee0796efa86470226686ae78f21.tar.bz2
niri-1a0612cbfd0abee0796efa86470226686ae78f21.zip
Implement layer rules: opacity and block-out-from
Diffstat (limited to 'src/layer')
-rw-r--r--src/layer/mapped.rs122
-rw-r--r--src/layer/mod.rs69
2 files changed, 191 insertions, 0 deletions
diff --git a/src/layer/mapped.rs b/src/layer/mapped.rs
new file mode 100644
index 00000000..a70503d7
--- /dev/null
+++ b/src/layer/mapped.rs
@@ -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
+ }
+}
diff --git a/src/layer/mod.rs b/src/layer/mod.rs
new file mode 100644
index 00000000..4d4d7819
--- /dev/null
+++ b/src/layer/mod.rs
@@ -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
+}