diff --git a/src/layout/floating.rs b/src/layout/floating.rs index dfeb18c1f..39adf7aab 100644 --- a/src/layout/floating.rs +++ b/src/layout/floating.rs @@ -20,7 +20,7 @@ use crate::render_helpers::RenderTarget; use crate::utils::transaction::TransactionBlocker; use crate::utils::{ center_preferring_top_left_in_area, clamp_preferring_top_left_in_area, ensure_min_max_size, - ResizeEdge, + ensure_min_max_size_maybe_zero, ResizeEdge, }; use crate::window::ResolvedWindowRules; @@ -386,15 +386,14 @@ impl FloatingSpace { // fullscreen until now), fall back to (0, 0). floating_size.unwrap_or_else(|| win.expected_size().unwrap_or_default()) }; - // Make sure fixed-size through window rules keeps working. + + // Apply min/max size window rules. If requesting a concrete size, apply completely; if + // requesting (0, 0), apply only when min/max results in a fixed size. let min_size = win.min_size(); let max_size = win.max_size(); - if min_size.w != 0 && min_size.w == max_size.w { - size.w = min_size.w; - } - if min_size.h != 0 && min_size.h == max_size.h { - size.h = min_size.h; - } + size.w = ensure_min_max_size_maybe_zero(size.w, min_size.w, max_size.w); + size.h = ensure_min_max_size_maybe_zero(size.h, min_size.h, max_size.h); + win.request_size_once(size, true); if activate || self.tiles.is_empty() { diff --git a/src/layout/mod.rs b/src/layout/mod.rs index a09a99884..535491545 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -62,7 +62,10 @@ use crate::render_helpers::texture::TextureBuffer; use crate::render_helpers::{BakedBuffer, RenderTarget, SplitElements}; use crate::rubber_band::RubberBand; use crate::utils::transaction::{Transaction, TransactionBlocker}; -use crate::utils::{output_matches_name, output_size, round_logical_in_physical_max1, ResizeEdge}; +use crate::utils::{ + ensure_min_max_size_maybe_zero, output_matches_name, output_size, + round_logical_in_physical_max1, ResizeEdge, +}; use crate::window::ResolvedWindowRules; pub mod closing_window; @@ -2698,15 +2701,15 @@ impl Layout { let win = move_.tile.window_mut(); let mut size = floating_size.unwrap_or_else(|| win.expected_size().unwrap_or_default()); - // Make sure fixed-size through window rules keeps working. + + // Apply min/max size window rules. If requesting a concrete size, apply + // completely; if requesting (0, 0), apply only when min/max results in a fixed + // size. let min_size = win.min_size(); let max_size = win.max_size(); - if min_size.w != 0 && min_size.w == max_size.w { - size.w = min_size.w; - } - if min_size.h != 0 && min_size.h == max_size.h { - size.h = min_size.h; - } + size.w = ensure_min_max_size_maybe_zero(size.w, min_size.w, max_size.w); + size.h = ensure_min_max_size_maybe_zero(size.h, min_size.h, max_size.h); + win.request_size_once(size, true); } return; @@ -3277,15 +3280,13 @@ impl Layout { Size::from((0, 0)) }; - // Make sure fixed-size through window rules keeps working. + // Apply min/max size window rules. If requesting a concrete size, apply + // completely; if requesting (0, 0), apply only when min/max results in a fixed + // size. let min_size = win.min_size(); let max_size = win.max_size(); - if min_size.w != 0 && min_size.w == max_size.w { - size.w = min_size.w; - } - if min_size.h != 0 && min_size.h == max_size.h { - size.h = min_size.h; - } + size.w = ensure_min_max_size_maybe_zero(size.w, min_size.w, max_size.w); + size.h = ensure_min_max_size_maybe_zero(size.h, min_size.h, max_size.h); win.request_size_once(size, true); diff --git a/src/tests/floating.rs b/src/tests/floating.rs index ece1a5dc1..8b63d55d8 100644 --- a/src/tests/floating.rs +++ b/src/tests/floating.rs @@ -1,5 +1,6 @@ use client::ClientId; use insta::assert_snapshot; +use niri_config::Config; use niri_ipc::SizeChange; use smithay::utils::Point; use wayland_client::protocol::wl_surface::WlSurface; @@ -785,3 +786,71 @@ fn floating_doesnt_store_fullscreen_size() { @"size: 100 × 100, bounds: 1920 × 1080, states: [Activated]" ); } + +#[test] +fn floating_respects_non_fixed_min_max_rule() { + let config = r##" +window-rule { + min-width 200 + max-width 300 +} +"##; + let config = Config::parse("test.kdl", config).unwrap(); + let mut f = Fixture::with_config(config); + f.add_output(1, (1920, 1080)); + f.add_output(2, (1280, 720)); + + let id = f.add_client(); + let window = f.client(id).create_window(); + let surface = window.surface.clone(); + window.commit(); + f.roundtrip(id); + + // Open with smaller width than min. + let window = f.client(id).window(&surface); + window.attach_new_buffer(); + window.set_size(100, 100); + window.ack_last_and_commit(); + f.double_roundtrip(id); + + // Commit to the Activated state configure. + f.client(id).window(&surface).ack_last_and_commit(); + f.double_roundtrip(id); + + let _ = f.client(id).window(&surface).recent_configures(); + + // Make it floating. + f.niri().layout.toggle_window_floating(None); + f.double_roundtrip(id); + + // This should clamp to min-width and request 200 × 100. + assert_snapshot!( + f.client(id).window(&surface).format_recent_configures(), + @"size: 200 × 100, bounds: 1920 × 1080, states: [Activated]" + ); + + // Commit with a bigger width than max. + let window = f.client(id).window(&surface); + window.set_size(400, 100); + window.ack_last_and_commit(); + f.roundtrip(id); + + // Make it tiling. + f.niri().layout.toggle_window_floating(None); + f.double_roundtrip(id); + + let _ = f.client(id).window(&surface).recent_configures(); + + f.client(id).window(&surface).ack_last_and_commit(); + f.roundtrip(id); + + // Make it floating. + f.niri().layout.toggle_window_floating(None); + f.double_roundtrip(id); + + // This should clamp to max-width and request 300 × 100. + assert_snapshot!( + f.client(id).window(&surface).format_recent_configures(), + @"size: 300 × 100, bounds: 1920 × 1080, states: [Activated]" + ); +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 7ab89a72d..28cebf2c6 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -272,6 +272,16 @@ pub fn ensure_min_max_size(mut x: i32, min_size: i32, max_size: i32) -> i32 { x } +pub fn ensure_min_max_size_maybe_zero(x: i32, min_size: i32, max_size: i32) -> i32 { + if x != 0 { + ensure_min_max_size(x, min_size, max_size) + } else if min_size > 0 && min_size == max_size { + min_size + } else { + 0 + } +} + pub fn clamp_preferring_top_left_in_area( area: Rectangle, rect: &mut Rectangle,