// SPDX-License-Identifier: LGPL-2.1-only // Copyright (C) 2026 Liberux Labs, S. L. use web_sys::Element; use crate::app::App; use crate::event_loop::AppData; use crate::event_loop::surface::Insets; /// The main surface's browser half. #[ derive( Default ) ] pub( crate ) struct WebSurfaceState { /// The software renderer's frame, unpremultiplied for `putImageData`. pub pixels: Vec, } /// The safe-area insets `probe` is padded by, in CSS pixels; none without a probe. fn safe_area( probe: Option<&Element> ) -> Insets { let ( Some( probe ), Some( window ) ) = ( probe, web_sys::window() ) else { return Insets::default() }; let Ok( Some( style ) ) = window.get_computed_style( probe ) else { return Insets::default() }; let side = |name: &str| style.get_property_value( name ).ok() .and_then( |value| value.trim().trim_end_matches( "px" ).parse::().ok() ) .unwrap_or( 0.0 ); Insets { top: side( "padding-top" ), right: side( "padding-right" ), bottom: side( "padding-bottom" ), left: side( "padding-left" ) } } impl AppData { /// Size the main surface to the canvas, whose CSS pixels are the surface's logical ones at the page's `devicePixelRatio`, so the canvas holds the display's own pixels, and keep the view out of what a phone's notch and bars cover. pub( super ) fn fit_canvas_element( &mut self ) { let insets = safe_area( self.platform.inset_probe.as_ref() ); if insets != self.main.insets { self.main.insets = insets; self.main.request_redraw(); } let canvas = &self.platform.canvas; let ( w, h ) = ( canvas.client_width().max( 1 ) as u32, canvas.client_height().max( 1 ) as u32 ); let scale = web_sys::window().map_or( 1.0, |w| w.device_pixel_ratio() ) as f32; if self.main.configured && ( w, h ) == ( self.main.width, self.main.height ) && scale == self.main.scale_factor { return; } if scale != self.main.scale_factor { self.main.scale_factor = scale; if let Some( ref mut canvas ) = self.main.canvas { canvas.set_dpi_scale( scale ); } } self.main.set_size( w, h ); self.on_main_resized( w, h ); } }