aboutsummaryrefslogtreecommitdiff
path: root/src/window
diff options
context:
space:
mode:
Diffstat (limited to 'src/window')
-rw-r--r--src/window/mapped.rs36
-rw-r--r--src/window/mod.rs107
2 files changed, 138 insertions, 5 deletions
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<i32, Logical> {
- with_states(self.toplevel().wl_surface(), |state| {
+ let mut size = with_states(self.toplevel().wl_surface(), |state| {
let curr = state.cached_state.current::<SurfaceCachedState>();
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<i32, Logical> {
- with_states(self.toplevel().wl_surface(), |state| {
+ let mut size = with_states(self.toplevel().wl_surface(), |state| {
let curr = state.cached_state.current::<SurfaceCachedState>();
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<bool>,
+
+ /// Extra bound on the minimum window width.
+ pub min_width: Option<u16>,
+ /// Extra bound on the minimum window height.
+ pub min_height: Option<u16>,
+ /// Extra bound on the maximum window width.
+ pub max_width: Option<u16>,
+ /// Extra bound on the maximum window height.
+ pub max_height: Option<u16>,
+}
+
+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::<XdgToplevelSurfaceData>()
+ .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
}