layer-shika/crates/adapters/src/wayland/surfaces/surface_renderer.rs

192 lines
6.2 KiB
Rust

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<W: RenderableWindow> {
pub window: Rc<W>,
pub surface: ManagedWlSurface,
pub layer_surface: ManagedZwlrLayerSurfaceV1,
pub viewport: Option<ManagedWpViewport>,
pub fractional_scale: Option<ManagedWpFractionalScaleV1>,
pub height: u32,
pub size: PhysicalSize,
}
pub struct SurfaceRenderer<W: RenderableWindow> {
window: Rc<W>,
surface: ManagedWlSurface,
layer_surface: ManagedZwlrLayerSurfaceV1,
viewport: Option<ManagedWpViewport>,
fractional_scale: Option<ManagedWpFractionalScaleV1>,
height: u32,
size: PhysicalSize,
logical_size: PhysicalSize,
}
impl<W: RenderableWindow> SurfaceRenderer<W> {
#[must_use]
pub fn new(params: SurfaceRendererParams<W>) -> 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<W> {
&self.window
}
pub fn layer_surface(&self) -> Rc<ZwlrLayerSurfaceV1> {
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();
}
}