diff --git a/crates/composition/src/shell.rs b/crates/composition/src/shell.rs index 3dd42f8..f3264dd 100644 --- a/crates/composition/src/shell.rs +++ b/crates/composition/src/shell.rs @@ -405,6 +405,10 @@ impl Shell { })); } + for def in &definitions { + def.config.validate().map_err(Error::Domain)?; + } + let is_single_window = definitions.len() == 1; if is_single_window { @@ -693,6 +697,12 @@ impl Shell { config: &SurfaceConfig, ) { log::debug!("Surface command: ApplyConfig {:?}", target); + + if let Err(e) = config.validate() { + log::error!("Invalid surface configuration: {}", e); + return; + } + for surface in Self::resolve_surface_target(ctx, target) { let handle = LayerSurfaceHandle::from_window_state(surface); diff --git a/crates/domain/src/config.rs b/crates/domain/src/config.rs index 3536330..6bcec07 100644 --- a/crates/domain/src/config.rs +++ b/crates/domain/src/config.rs @@ -1,4 +1,5 @@ use crate::dimensions::ScaleFactor; +use crate::errors::{DomainError, Result}; use crate::value_objects::anchor::AnchorEdges; use crate::value_objects::dimensions::SurfaceDimension; use crate::value_objects::keyboard_interactivity::KeyboardInteractivity; @@ -10,6 +11,14 @@ use crate::value_objects::output_policy::OutputPolicy; /// /// Contains all positioning, sizing, and behavioral properties for a surface. /// Use with `ShellConfig` for declarative configuration or build via `ShellBuilder`. +/// +/// # Wayland Protocol Requirements +/// +/// According to the wlr-layer-shell protocol, dimensions and anchors must be coordinated: +/// - If width is 0, the surface must be anchored to both left and right edges +/// - If height is 0, the surface must be anchored to both top and bottom edges +/// +/// Use `validate()` to check protocol compliance before use. #[derive(Debug, Clone)] pub struct SurfaceConfig { pub dimensions: SurfaceDimension, @@ -38,6 +47,33 @@ impl SurfaceConfig { output_policy: OutputPolicy::default(), } } + + /// Validates the surface configuration according to Wayland layer-shell protocol requirements. + /// + /// According to the protocol: + /// - If width is 0, the surface must be anchored to both left and right edges + /// - If height is 0, the surface must be anchored to both top and bottom edges + pub fn validate(&self) -> Result<()> { + if self.dimensions.width() == 0 && !(self.anchor.has_left() && self.anchor.has_right()) { + return Err(DomainError::Configuration { + message: "Width is 0 but surface is not anchored to both left and right edges. \ + According to wlr-layer-shell protocol, you must set your anchor to \ + opposite edges in the dimensions you omit." + .to_string(), + }); + } + + if self.dimensions.height() == 0 && !(self.anchor.has_top() && self.anchor.has_bottom()) { + return Err(DomainError::Configuration { + message: "Height is 0 but surface is not anchored to both top and bottom edges. \ + According to wlr-layer-shell protocol, you must set your anchor to \ + opposite edges in the dimensions you omit." + .to_string(), + }); + } + + Ok(()) + } } impl Default for SurfaceConfig { diff --git a/crates/domain/src/value_objects/dimensions.rs b/crates/domain/src/value_objects/dimensions.rs index a6d41c2..7e05a0c 100644 --- a/crates/domain/src/value_objects/dimensions.rs +++ b/crates/domain/src/value_objects/dimensions.rs @@ -1,26 +1,24 @@ /// Width and height of a layer surface in pixels /// -/// Use 0 for either dimension to let the surface stretch along that axis based on anchor edges. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// According to the Wayland wlr-layer-shell protocol: +/// - Pass 0 for either width or height to have the compositor assign it +/// - The compositor will center the surface with respect to its anchors +/// - When using 0, you MUST anchor to opposite edges in that dimension: +/// - width = 0 requires both left and right anchors +/// - height = 0 requires both top and bottom anchors +/// - Not following this requirement is a protocol error +/// - Both values default to 0 +/// +/// Size is double-buffered via `wl_surface.commit`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct SurfaceDimension { width: u32, height: u32, } impl SurfaceDimension { - pub fn new(width: u32, height: u32) -> Self { - Self { - width: if width == 0 { - Self::default().width - } else { - width - }, - height: if height == 0 { - Self::default().height - } else { - height - }, - } + pub const fn new(width: u32, height: u32) -> Self { + Self { width, height } } pub const fn from_raw(width: u32, height: u32) -> Self { @@ -36,15 +34,6 @@ impl SurfaceDimension { } } -impl Default for SurfaceDimension { - fn default() -> Self { - Self { - width: 20, - height: 20, - } - } -} - #[derive(Debug, Clone, Copy, PartialEq)] pub struct PopupDimensions { pub width: f32,