aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2024-03-19 18:22:25 +0400
committerIvan Molodetskikh <yalterz@gmail.com>2024-03-19 18:29:13 +0400
commitdb49deb7fd2fbe805ceec060aa4dec65009ad7a7 (patch)
treeb8f85d4aee4d366643626e97b7f37fa49d93b75b
parentc61361de3ca4484387f39b067eadc612908560eb (diff)
downloadniri-db49deb7fd2fbe805ceec060aa4dec65009ad7a7.tar.gz
niri-db49deb7fd2fbe805ceec060aa4dec65009ad7a7.tar.bz2
niri-db49deb7fd2fbe805ceec060aa4dec65009ad7a7.zip
Implement draw-border-with-background window rule
-rw-r--r--niri-config/src/lib.rs3
-rw-r--r--niri-visual-tests/src/test_window.rs6
-rw-r--r--resources/default-config.kdl10
-rw-r--r--src/handlers/compositor.rs2
-rw-r--r--src/layout/mod.rs8
-rw-r--r--src/layout/tile.rs26
-rw-r--r--src/window/mapped.rs4
-rw-r--r--src/window/mod.rs27
8 files changed, 79 insertions, 7 deletions
diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs
index 7a1233da..937df0ad 100644
--- a/niri-config/src/lib.rs
+++ b/niri-config/src/lib.rs
@@ -693,6 +693,9 @@ pub struct WindowRule {
pub max_width: Option<u16>,
#[knuffel(child, unwrap(argument))]
pub max_height: Option<u16>,
+
+ #[knuffel(child, unwrap(argument))]
+ pub draw_border_with_background: Option<bool>,
}
#[derive(knuffel::Decode, Debug, Default, Clone)]
diff --git a/niri-visual-tests/src/test_window.rs b/niri-visual-tests/src/test_window.rs
index df7f33c5..a0098f73 100644
--- a/niri-visual-tests/src/test_window.rs
+++ b/niri-visual-tests/src/test_window.rs
@@ -4,6 +4,7 @@ use std::rc::Rc;
use niri::layout::{LayoutElement, LayoutElementRenderElement};
use niri::render_helpers::renderer::NiriRenderer;
+use niri::window::ResolvedWindowRules;
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
use smithay::backend::renderer::element::{Id, Kind};
use smithay::output::Output;
@@ -217,4 +218,9 @@ impl LayoutElement for TestWindow {
}
fn refresh(&self) {}
+
+ fn rules(&self) -> &ResolvedWindowRules {
+ static EMPTY: ResolvedWindowRules = ResolvedWindowRules::empty();
+ &EMPTY
+ }
}
diff --git a/resources/default-config.kdl b/resources/default-config.kdl
index 014135fa..1b6cddf2 100644
--- a/resources/default-config.kdl
+++ b/resources/default-config.kdl
@@ -123,6 +123,9 @@ layout {
// If you don't like that, you should uncomment `prefer-no-csd` below.
// Niri will draw focus ring and border *around* windows that agree to omit their
// client-side decorations.
+ //
+ // Alternatively, you can override it with a window rule called
+ // `draw-border-with-background`.
// You can change how the focus ring looks.
focus-ring {
@@ -393,6 +396,13 @@ animations {
// if it is equal to min-height. Either set this equal to min-height,
// or change the window height manually for this to apply.
max-height 300
+
+ // Override whether the border and the focus ring draw with a background.
+ // Set this to `true` to draw them as solid colored rectangles even for
+ // windows which agreed to omit their client-side decorations.
+ // Set this to `false` to draw them as borders around the window even for
+ // windows which use client-side decorations.
+ draw-border-with-background false
}
// Here's a useful example. Work around WezTerm's initial configure bug
diff --git a/src/handlers/compositor.rs b/src/handlers/compositor.rs
index 188ba831..19489f2c 100644
--- a/src/handlers/compositor.rs
+++ b/src/handlers/compositor.rs
@@ -123,7 +123,7 @@ impl CompositorHandler for State {
(rules, width, is_full_width, output)
} else {
error!("window map must happen after initial configure");
- (ResolvedWindowRules::default(), None, false, None)
+ (ResolvedWindowRules::empty(), None, false, None)
};
let parent = window
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index c2c4bf4e..7162c3e3 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -49,6 +49,7 @@ use self::workspace::{compute_working_area, Column, ColumnWidth, OutputId, Works
use crate::niri_render_elements;
use crate::render_helpers::renderer::NiriRenderer;
use crate::utils::output_size;
+use crate::window::ResolvedWindowRules;
pub mod focus_ring;
pub mod monitor;
@@ -121,6 +122,8 @@ pub trait LayoutElement {
/// This *will* switch immediately after a [`LayoutElement::request_fullscreen()`] call.
fn is_pending_fullscreen(&self) -> bool;
+ fn rules(&self) -> &ResolvedWindowRules;
+
/// Runs periodic clean-up tasks.
fn refresh(&self);
}
@@ -1905,6 +1908,11 @@ mod tests {
}
fn refresh(&self) {}
+
+ fn rules(&self) -> &ResolvedWindowRules {
+ static EMPTY: ResolvedWindowRules = ResolvedWindowRules::empty();
+ &EMPTY
+ }
}
fn arbitrary_bbox() -> impl Strategy<Value = Rectangle<i32, Logical>> {
diff --git a/src/layout/tile.rs b/src/layout/tile.rs
index 589b2fc0..0609583e 100644
--- a/src/layout/tile.rs
+++ b/src/layout/tile.rs
@@ -85,11 +85,22 @@ impl<W: LayoutElement> Tile<W> {
}
pub fn advance_animations(&mut self, current_time: Duration, is_active: bool) {
+ let draw_border_with_background = self
+ .window
+ .rules()
+ .draw_border_with_background
+ .unwrap_or_else(|| !self.window.has_ssd());
self.border
- .update(self.window.size(), self.window.has_ssd());
+ .update(self.window.size(), !draw_border_with_background);
self.border.set_active(is_active);
- self.focus_ring.update(self.tile_size(), self.has_ssd());
+ let draw_focus_ring_with_background = if self.effective_border_width().is_some() {
+ false
+ } else {
+ draw_border_with_background
+ };
+ self.focus_ring
+ .update(self.tile_size(), !draw_focus_ring_with_background);
self.focus_ring.set_active(is_active);
match &mut self.open_animation {
@@ -295,8 +306,15 @@ impl<W: LayoutElement> Tile<W> {
size
}
- pub fn has_ssd(&self) -> bool {
- self.effective_border_width().is_some() || self.window.has_ssd()
+ pub fn draw_border_with_background(&self) -> bool {
+ if self.effective_border_width().is_some() {
+ return false;
+ }
+
+ self.window
+ .rules()
+ .draw_border_with_background
+ .unwrap_or_else(|| !self.window.has_ssd())
}
fn render_inner<R: NiriRenderer>(
diff --git a/src/window/mapped.rs b/src/window/mapped.rs
index 6afd995e..b5caf8e2 100644
--- a/src/window/mapped.rs
+++ b/src/window/mapped.rs
@@ -184,4 +184,8 @@ impl LayoutElement for Mapped {
fn refresh(&self) {
self.window.refresh();
}
+
+ fn rules(&self) -> &ResolvedWindowRules {
+ &self.rules
+ }
}
diff --git a/src/window/mod.rs b/src/window/mod.rs
index 25b3531f..bb6f2151 100644
--- a/src/window/mod.rs
+++ b/src/window/mod.rs
@@ -13,7 +13,7 @@ pub mod unmapped;
pub use unmapped::{InitialConfigureState, Unmapped};
/// Rules fully resolved for a window.
-#[derive(Debug, Default, PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct ResolvedWindowRules {
/// Default width for this window.
///
@@ -39,13 +39,32 @@ pub struct ResolvedWindowRules {
pub max_width: Option<u16>,
/// Extra bound on the maximum window height.
pub max_height: Option<u16>,
+
+ /// Whether or not to draw the border with a solid background.
+ ///
+ /// `None` means using the SSD heuristic.
+ pub draw_border_with_background: Option<bool>,
}
impl ResolvedWindowRules {
+ pub const fn empty() -> Self {
+ Self {
+ default_width: None,
+ open_on_output: None,
+ open_maximized: None,
+ open_fullscreen: None,
+ min_width: None,
+ min_height: None,
+ max_width: None,
+ max_height: None,
+ draw_border_with_background: None,
+ }
+ }
+
pub fn compute(rules: &[WindowRule], toplevel: &ToplevelSurface) -> Self {
let _span = tracy_client::span!("ResolvedWindowRules::compute");
- let mut resolved = ResolvedWindowRules::default();
+ let mut resolved = ResolvedWindowRules::empty();
with_states(toplevel.wl_surface(), |states| {
let role = states
@@ -100,6 +119,10 @@ impl ResolvedWindowRules {
if let Some(x) = rule.max_height {
resolved.max_height = Some(x);
}
+
+ if let Some(x) = rule.draw_border_with_background {
+ resolved.draw_border_with_background = Some(x);
+ }
}
resolved.open_on_output = open_on_output.map(|x| x.to_owned());