From c61361de3ca4484387f39b067eadc612908560eb Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Tue, 19 Mar 2024 15:20:03 +0400 Subject: Implement window rule reloading and min/max size rules --- src/window/mapped.rs | 36 +++++++++++++++-- src/window/mod.rs | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 138 insertions(+), 5 deletions(-) (limited to 'src/window') diff --git a/src/window/mapped.rs b/src/window/mapped.rs index 4476dede..6afd995e 100644 --- a/src/window/mapped.rs +++ b/src/window/mapped.rs @@ -1,3 +1,5 @@ +use std::cmp::{max, min}; + use smithay::backend::renderer::element::{AsRenderElements as _, Id}; use smithay::desktop::space::SpaceElement as _; use smithay::desktop::Window; @@ -82,17 +84,43 @@ impl LayoutElement for Mapped { } fn min_size(&self) -> Size { - with_states(self.toplevel().wl_surface(), |state| { + let mut size = with_states(self.toplevel().wl_surface(), |state| { let curr = state.cached_state.current::(); curr.min_size - }) + }); + + if let Some(x) = self.rules.min_width { + size.w = max(size.w, i32::from(x)); + } + if let Some(x) = self.rules.min_height { + size.h = max(size.h, i32::from(x)); + } + + size } fn max_size(&self) -> Size { - with_states(self.toplevel().wl_surface(), |state| { + let mut size = with_states(self.toplevel().wl_surface(), |state| { let curr = state.cached_state.current::(); curr.max_size - }) + }); + + if let Some(x) = self.rules.max_width { + if size.w == 0 { + size.w = i32::from(x); + } else if x > 0 { + size.w = min(size.w, i32::from(x)); + } + } + if let Some(x) = self.rules.max_height { + if size.h == 0 { + size.h = i32::from(x); + } else if x > 0 { + size.h = min(size.h, i32::from(x)); + } + } + + size } fn is_wl_surface(&self, wl_surface: &WlSurface) -> bool { diff --git a/src/window/mod.rs b/src/window/mod.rs index 124a62f3..25b3531f 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -1,3 +1,9 @@ +use niri_config::{Match, WindowRule}; +use smithay::wayland::compositor::with_states; +use smithay::wayland::shell::xdg::{ + ToplevelSurface, XdgToplevelSurfaceData, XdgToplevelSurfaceRoleAttributes, +}; + use crate::layout::workspace::ColumnWidth; pub mod mapped; @@ -7,7 +13,7 @@ pub mod unmapped; pub use unmapped::{InitialConfigureState, Unmapped}; /// Rules fully resolved for a window. -#[derive(Debug, Default)] +#[derive(Debug, Default, PartialEq)] pub struct ResolvedWindowRules { /// Default width for this window. /// @@ -24,4 +30,103 @@ pub struct ResolvedWindowRules { /// Whether the window should open fullscreen. pub open_fullscreen: Option, + + /// Extra bound on the minimum window width. + pub min_width: Option, + /// Extra bound on the minimum window height. + pub min_height: Option, + /// Extra bound on the maximum window width. + pub max_width: Option, + /// Extra bound on the maximum window height. + pub max_height: Option, +} + +impl ResolvedWindowRules { + pub fn compute(rules: &[WindowRule], toplevel: &ToplevelSurface) -> Self { + let _span = tracy_client::span!("ResolvedWindowRules::compute"); + + let mut resolved = ResolvedWindowRules::default(); + + with_states(toplevel.wl_surface(), |states| { + let role = states + .data_map + .get::() + .unwrap() + .lock() + .unwrap(); + + let mut open_on_output = None; + + for rule in rules { + if !(rule.matches.is_empty() + || rule.matches.iter().any(|m| window_matches(&role, m))) + { + continue; + } + + if rule.excludes.iter().any(|m| window_matches(&role, m)) { + continue; + } + + if let Some(x) = rule + .default_column_width + .as_ref() + .map(|d| d.0.map(ColumnWidth::from)) + { + resolved.default_width = Some(x); + } + + if let Some(x) = rule.open_on_output.as_deref() { + open_on_output = Some(x); + } + + if let Some(x) = rule.open_maximized { + resolved.open_maximized = Some(x); + } + + if let Some(x) = rule.open_fullscreen { + resolved.open_fullscreen = Some(x); + } + + if let Some(x) = rule.min_width { + resolved.min_width = Some(x); + } + if let Some(x) = rule.min_height { + resolved.min_height = Some(x); + } + if let Some(x) = rule.max_width { + resolved.max_width = Some(x); + } + if let Some(x) = rule.max_height { + resolved.max_height = Some(x); + } + } + + resolved.open_on_output = open_on_output.map(|x| x.to_owned()); + }); + + resolved + } +} + +fn window_matches(role: &XdgToplevelSurfaceRoleAttributes, m: &Match) -> bool { + if let Some(app_id_re) = &m.app_id { + let Some(app_id) = &role.app_id else { + return false; + }; + if !app_id_re.is_match(app_id) { + return false; + } + } + + if let Some(title_re) = &m.title { + let Some(title) = &role.title else { + return false; + }; + if !title_re.is_match(title) { + return false; + } + } + + true } -- cgit