aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2024-04-24 21:49:07 +0400
committerIvan Molodetskikh <yalterz@gmail.com>2024-04-24 22:01:26 +0400
commit3e598c565e6e8ad4c34e93aec9a49e60d51d730e (patch)
tree95d3f12d04f3a416ba86915ebea01b7e0f66498e /src
parente261b641ed62474676c90aaa4e734cdd1ecde703 (diff)
downloadniri-3e598c565e6e8ad4c34e93aec9a49e60d51d730e.tar.gz
niri-3e598c565e6e8ad4c34e93aec9a49e60d51d730e.tar.bz2
niri-3e598c565e6e8ad4c34e93aec9a49e60d51d730e.zip
Implement border window rule
Diffstat (limited to 'src')
-rw-r--r--src/handlers/xdg_shell.rs10
-rw-r--r--src/layout/mod.rs18
-rw-r--r--src/layout/tile.rs15
-rw-r--r--src/layout/workspace.rs66
-rw-r--r--src/window/mod.rs16
5 files changed, 91 insertions, 34 deletions
diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs
index 36033da8..0b0a5a33 100644
--- a/src/handlers/xdg_shell.rs
+++ b/src/handlers/xdg_shell.rs
@@ -230,7 +230,7 @@ impl XdgShellHandler for State {
// The required configure will be the initial configure.
}
- InitialConfigureState::Configured { output, .. } => {
+ InitialConfigureState::Configured { rules, output, .. } => {
// Figure out the monitor following a similar logic to initial configure.
// FIXME: deduplicate.
let mon = requested_output
@@ -269,7 +269,7 @@ impl XdgShellHandler for State {
toplevel.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Fullscreen);
});
- ws.configure_new_window(&unmapped.window, None);
+ ws.configure_new_window(&unmapped.window, None, rules);
}
// We already sent the initial configure, so we need to reconfigure.
@@ -302,10 +302,10 @@ impl XdgShellHandler for State {
// The required configure will be the initial configure.
}
InitialConfigureState::Configured {
+ rules,
width,
is_full_width,
output,
- ..
} => {
// Figure out the monitor following a similar logic to initial configure.
// FIXME: deduplicate.
@@ -349,7 +349,7 @@ impl XdgShellHandler for State {
} else {
*width
};
- ws.configure_new_window(&unmapped.window, configure_width);
+ ws.configure_new_window(&unmapped.window, configure_width, rules);
}
// We already sent the initial configure, so we need to reconfigure.
@@ -580,7 +580,7 @@ impl State {
} else {
width
};
- ws.configure_new_window(window, configure_width);
+ ws.configure_new_window(window, configure_width, &rules);
}
// If the user prefers no CSD, it's a reasonable assumption that they would prefer to get
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 4c570ef2..4889e9a7 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -463,8 +463,10 @@ impl<W: LayoutElement> Layout<W> {
) -> Option<&Output> {
let mut width = width.unwrap_or_else(|| ColumnWidth::Fixed(window.size().w));
if let ColumnWidth::Fixed(w) = &mut width {
- if !self.options.border.off {
- *w += self.options.border.width as i32 * 2;
+ let rules = window.rules();
+ let border_config = rules.border.resolve_against(self.options.border);
+ if !border_config.off {
+ *w += border_config.width as i32 * 2;
}
}
@@ -519,8 +521,10 @@ impl<W: LayoutElement> Layout<W> {
) -> Option<&Output> {
let mut width = width.unwrap_or_else(|| ColumnWidth::Fixed(window.size().w));
if let ColumnWidth::Fixed(w) = &mut width {
- if !self.options.border.off {
- *w += self.options.border.width as i32 * 2;
+ let rules = window.rules();
+ let border_config = rules.border.resolve_against(self.options.border);
+ if !border_config.off {
+ *w += border_config.width as i32 * 2;
}
}
@@ -555,8 +559,10 @@ impl<W: LayoutElement> Layout<W> {
) {
let mut width = width.unwrap_or_else(|| ColumnWidth::Fixed(window.size().w));
if let ColumnWidth::Fixed(w) = &mut width {
- if !self.options.border.off {
- *w += self.options.border.width as i32 * 2;
+ let rules = window.rules();
+ let border_config = rules.border.resolve_against(self.options.border);
+ if !border_config.off {
+ *w += border_config.width as i32 * 2;
}
}
diff --git a/src/layout/tile.rs b/src/layout/tile.rs
index ed6773da..4778db69 100644
--- a/src/layout/tile.rs
+++ b/src/layout/tile.rs
@@ -107,9 +107,12 @@ struct MoveAnimation {
impl<W: LayoutElement> Tile<W> {
pub fn new(window: W, options: Rc<Options>) -> Self {
+ let rules = window.rules();
+ let border_config = rules.border.resolve_against(options.border);
+
Self {
window,
- border: FocusRing::new(options.border.into()),
+ border: FocusRing::new(border_config.into()),
focus_ring: FocusRing::new(options.focus_ring),
is_fullscreen: false, // FIXME: up-to-date fullscreen right away, but we need size.
fullscreen_backdrop: SolidColorBuffer::new((0, 0), [0., 0., 0., 1.]),
@@ -123,7 +126,11 @@ impl<W: LayoutElement> Tile<W> {
}
pub fn update_config(&mut self, options: Rc<Options>) {
- self.border.update_config(options.border.into());
+ let rules = self.window.rules();
+
+ let border_config = rules.border.resolve_against(self.options.border);
+ self.border.update_config(border_config.into());
+
self.focus_ring.update_config(options.focus_ring);
self.options = options;
}
@@ -164,6 +171,10 @@ impl<W: LayoutElement> Tile<W> {
self.resize_animation = None;
}
}
+
+ let rules = self.window.rules();
+ let border_config = rules.border.resolve_against(self.options.border);
+ self.border.update_config(border_config.into());
}
pub fn advance_animations(&mut self, current_time: Duration, is_active: bool) {
diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs
index b6b38285..fd5662b8 100644
--- a/src/layout/workspace.rs
+++ b/src/layout/workspace.rs
@@ -23,6 +23,7 @@ use crate::render_helpers::RenderTarget;
use crate::swipe_tracker::SwipeTracker;
use crate::utils::id::IdCounter;
use crate::utils::output_size;
+use crate::window::ResolvedWindowRules;
/// Amount of touchpad movement to scroll the view for the width of one working area.
const VIEW_GESTURE_WORKING_AREA_MOVEMENT: f64 = 1200.;
@@ -406,16 +407,9 @@ impl<W: LayoutElement> Workspace<W> {
}
}
- fn toplevel_bounds(&self) -> Size<i32, Logical> {
- let mut border = 0;
- if !self.options.border.off {
- border = self.options.border.width as i32 * 2;
- }
-
- Size::from((
- max(self.working_area.size.w - self.options.gaps * 2 - border, 1),
- max(self.working_area.size.h - self.options.gaps * 2 - border, 1),
- ))
+ fn toplevel_bounds(&self, rules: &ResolvedWindowRules) -> Size<i32, Logical> {
+ let border_config = rules.border.resolve_against(self.options.border);
+ compute_toplevel_bounds(border_config, self.working_area.size, self.options.gaps)
}
pub fn resolve_default_width(
@@ -429,14 +423,20 @@ impl<W: LayoutElement> Workspace<W> {
}
}
- pub fn new_window_size(&self, width: Option<ColumnWidth>) -> Size<i32, Logical> {
+ pub fn new_window_size(
+ &self,
+ width: Option<ColumnWidth>,
+ rules: &ResolvedWindowRules,
+ ) -> Size<i32, Logical> {
+ let border = rules.border.resolve_against(self.options.border);
+
let width = if let Some(width) = width {
let is_fixed = matches!(width, ColumnWidth::Fixed(_));
let mut width = width.resolve(&self.options, self.working_area.size.w);
- if !is_fixed && !self.options.border.off {
- width -= self.options.border.width as i32 * 2;
+ if !is_fixed && !border.off {
+ width -= border.width as i32 * 2;
}
max(1, width)
@@ -445,14 +445,19 @@ impl<W: LayoutElement> Workspace<W> {
};
let mut height = self.working_area.size.h - self.options.gaps * 2;
- if !self.options.border.off {
- height -= self.options.border.width as i32 * 2;
+ if !border.off {
+ height -= border.width as i32 * 2;
}
Size::from((width, max(height, 1)))
}
- pub fn configure_new_window(&self, window: &Window, width: Option<ColumnWidth>) {
+ pub fn configure_new_window(
+ &self,
+ window: &Window,
+ width: Option<ColumnWidth>,
+ rules: &ResolvedWindowRules,
+ ) {
if let Some(output) = self.output.as_ref() {
let scale = output.current_scale().integer_scale();
let transform = output.current_transform();
@@ -468,10 +473,10 @@ impl<W: LayoutElement> Workspace<W> {
if state.states.contains(xdg_toplevel::State::Fullscreen) {
state.size = Some(self.view_size);
} else {
- state.size = Some(self.new_window_size(width));
+ state.size = Some(self.new_window_size(width, rules));
}
- state.bounds = Some(self.toplevel_bounds());
+ state.bounds = Some(self.toplevel_bounds(rules));
});
}
@@ -2134,8 +2139,6 @@ impl<W: LayoutElement> Workspace<W> {
}
pub fn refresh(&mut self, is_active: bool) {
- let bounds = self.toplevel_bounds();
-
for (col_idx, col) in self.columns.iter_mut().enumerate() {
for (tile_idx, tile) in col.tiles.iter_mut().enumerate() {
let win = tile.window_mut();
@@ -2144,7 +2147,14 @@ impl<W: LayoutElement> Workspace<W> {
&& col.active_tile_idx == tile_idx;
win.set_activated(active);
+ let border_config = win.rules().border.resolve_against(self.options.border);
+ let bounds = compute_toplevel_bounds(
+ border_config,
+ self.working_area.size,
+ self.options.gaps,
+ );
win.set_bounds(bounds);
+
win.send_pending_configure();
win.refresh();
}
@@ -2824,3 +2834,19 @@ pub fn compute_working_area(output: &Output, struts: Struts) -> Rectangle<i32, L
working_area
}
+
+fn compute_toplevel_bounds(
+ border_config: niri_config::Border,
+ working_area_size: Size<i32, Logical>,
+ gaps: i32,
+) -> Size<i32, Logical> {
+ let mut border = 0;
+ if !border_config.off {
+ border = border_config.width as i32 * 2;
+ }
+
+ Size::from((
+ max(working_area_size.w - gaps * 2 - border, 1),
+ max(working_area_size.h - gaps * 2 - border, 1),
+ ))
+}
diff --git a/src/window/mod.rs b/src/window/mod.rs
index 3c46e9e5..a793cedb 100644
--- a/src/window/mod.rs
+++ b/src/window/mod.rs
@@ -1,4 +1,4 @@
-use niri_config::{BlockOutFrom, Match, WindowRule};
+use niri_config::{BlockOutFrom, BorderRule, Match, WindowRule};
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
use smithay::wayland::compositor::with_states;
use smithay::wayland::shell::xdg::{
@@ -48,6 +48,9 @@ pub struct ResolvedWindowRules {
/// Extra bound on the maximum window height.
pub max_height: Option<u16>,
+ /// Window border overrides.
+ pub border: BorderRule,
+
/// Whether or not to draw the border with a solid background.
///
/// `None` means using the SSD heuristic.
@@ -87,6 +90,15 @@ impl ResolvedWindowRules {
min_height: None,
max_width: None,
max_height: None,
+ border: BorderRule {
+ off: false,
+ on: false,
+ width: None,
+ active_color: None,
+ inactive_color: None,
+ active_gradient: None,
+ inactive_gradient: None,
+ },
draw_border_with_background: None,
opacity: None,
block_out_from: None,
@@ -158,6 +170,8 @@ impl ResolvedWindowRules {
resolved.max_height = Some(x);
}
+ resolved.border.merge_with(&rule.border);
+
if let Some(x) = rule.draw_border_with_background {
resolved.draw_border_with_background = Some(x);
}