use crate::errors::Result; use crate::rendering::femtovg::renderable_window::RenderableWindow; use crate::wayland::managed_proxies::{ ManagedWlSurface, ManagedZwlrLayerSurfaceV1, ManagedWpFractionalScaleV1, ManagedWpViewport, }; use crate::wayland::surfaces::dimensions::SurfaceDimensionsExt; use layer_shika_domain::surface_dimensions::SurfaceDimensions; use log::{error, info}; use slint::PhysicalSize; use smithay_client_toolkit::reexports::protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; use std::rc::Rc; #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum ScalingMode { FractionalWithViewport, FractionalOnly, Integer, } pub struct SurfaceRendererParams { pub window: Rc, pub surface: ManagedWlSurface, pub layer_surface: ManagedZwlrLayerSurfaceV1, pub viewport: Option, pub fractional_scale: Option, pub height: u32, pub size: PhysicalSize, } pub struct SurfaceRenderer { window: Rc, surface: ManagedWlSurface, layer_surface: ManagedZwlrLayerSurfaceV1, viewport: Option, fractional_scale: Option, height: u32, size: PhysicalSize, logical_size: PhysicalSize, } impl SurfaceRenderer { #[must_use] pub fn new(params: SurfaceRendererParams) -> Self { Self { window: params.window, surface: params.surface, layer_surface: params.layer_surface, viewport: params.viewport, fractional_scale: params.fractional_scale, height: params.height, size: params.size, logical_size: PhysicalSize::default(), } } pub fn render_frame_if_dirty(&self) -> Result<()> { self.window.render_frame_if_dirty() } pub const fn window(&self) -> &Rc { &self.window } pub fn layer_surface(&self) -> Rc { Rc::clone(self.layer_surface.inner()) } pub const fn height(&self) -> u32 { self.height } pub const fn size(&self) -> PhysicalSize { self.size } const fn determine_scaling_mode(&self) -> ScalingMode { if self.fractional_scale.is_some() && self.viewport.is_some() { ScalingMode::FractionalWithViewport } else if self.fractional_scale.is_some() { ScalingMode::FractionalOnly } else { ScalingMode::Integer } } #[allow(clippy::cast_precision_loss)] fn configure_slint_window( &self, dimensions: &SurfaceDimensions, mode: ScalingMode, scale_factor: f32, ) { match mode { ScalingMode::FractionalWithViewport => { self.window.set_scale_factor(scale_factor); self.window .set_size(slint::WindowSize::Logical(slint::LogicalSize::new( dimensions.logical_width() as f32, dimensions.logical_height() as f32, ))); } ScalingMode::FractionalOnly => { self.window .set_scale_factor(dimensions.buffer_scale() as f32); self.window .set_size(slint::WindowSize::Logical(slint::LogicalSize::new( dimensions.logical_width() as f32, dimensions.logical_height() as f32, ))); } ScalingMode::Integer => { self.window.set_scale_factor(scale_factor); self.window.set_size(slint::WindowSize::Physical( dimensions.to_slint_physical_size(), )); } } } #[allow(clippy::cast_possible_wrap)] fn configure_wayland_surface(&self, dimensions: &SurfaceDimensions, mode: ScalingMode) { match mode { ScalingMode::FractionalWithViewport => { self.surface.set_buffer_scale(1); if let Some(viewport) = &self.viewport { viewport.set_destination( dimensions.logical_width() as i32, dimensions.logical_height() as i32, ); } } ScalingMode::FractionalOnly | ScalingMode::Integer => { self.surface.set_buffer_scale(dimensions.buffer_scale()); } } self.surface.commit(); } pub fn update_size(&mut self, width: u32, height: u32, scale_factor: f32) { if width == 0 || height == 0 { info!("Skipping update_size with zero dimension: {width}x{height}"); return; } let dimensions = match SurfaceDimensions::calculate(width, height, scale_factor) { Ok(d) => d, Err(e) => { error!("Failed to calculate surface dimensions: {e}"); return; } }; self.apply_surface_dimensions(dimensions, scale_factor); } pub fn apply_surface_dimensions(&mut self, dimensions: SurfaceDimensions, scale_factor: f32) { let scaling_mode = self.determine_scaling_mode(); info!( "Updating window size: logical {}x{}, physical {}x{}, scale {}, buffer_scale {}, mode {:?}", dimensions.logical_width(), dimensions.logical_height(), dimensions.physical_width(), dimensions.physical_height(), scale_factor, dimensions.buffer_scale(), scaling_mode ); self.configure_slint_window(&dimensions, scaling_mode, scale_factor); self.configure_wayland_surface(&dimensions, scaling_mode); info!("Window physical size: {:?}", self.window.size()); self.size = dimensions.to_slint_physical_size(); self.logical_size = dimensions.to_slint_logical_size(); RenderableWindow::request_redraw(self.window.as_ref()); } pub const fn logical_size(&self) -> PhysicalSize { self.logical_size } pub const fn fractional_scale(&self) -> Option<&ManagedWpFractionalScaleV1> { self.fractional_scale.as_ref() } pub fn commit_surface(&self) { self.surface.commit(); } }