// SPDX-License-Identifier: LGPL-2.1-only // Copyright (C) 2026 Liberux Labs, S. L. use std::sync::Arc; use smithay_client_toolkit::compositor::CompositorState; use smithay_client_toolkit::reexports::client::protocol::{ wl_shm, wl_surface::WlSurface }; use smithay_client_toolkit::shm::Shm; use crate::app::App; use crate::egl_context::EglContext; use crate::types::{ Color, Rect }; use crate::widget::Element; use super::AppData; use super::surface::{ SurfaceFocus, SurfaceState }; use crate::draw::gles::{ draw_surface_full_gpu, draw_surface_partial_gpu }; use crate::draw::software::{ draw_surface_full, draw_surface_partial }; use crate::draw::damage::compute_interaction_dirty_rects; /// Pick the best wl_shm format for our RGBA-premultiplied pixmap. /// Abgr8888 matches tiny-skia's byteorder on little-endian systems (plain /// memcpy). Falls back to mandatory Argb8888 when not advertised. /// Returns `(format, swap_rb)`. pub( crate ) fn pick_shm_format( shm: &Shm ) -> ( wl_shm::Format, bool ) { if shm.formats().contains( &wl_shm::Format::Abgr8888 ) { ( wl_shm::Format::Abgr8888, false ) } else { ( wl_shm::Format::Argb8888, true ) } } pub( crate ) fn draw_frame( data: &mut AppData ) { let main_view = data.cached_view.as_ref().expect( "view cache populated" ); let overlays = data.cached_overlays.as_ref().expect( "overlays cache populated" ); let main_bg = data.app.background_color(); let main_region = data.app.input_region(); let debug_layout = data.debug_layout; let ( format, swap_rb ) = pick_shm_format( &data.shm ); let egl_ctx = data.egl_context.as_ref(); let qh = &data.qh; if data.main.configured && data.main.needs_redraw && !data.main.frame_pending { let req_frame = | wl: &WlSurface | { let _ = wl.frame( qh, SurfaceFocus::Main ); }; draw_surface::( &mut data.main, &data.compositor_state, egl_ctx, main_view, main_bg, main_region.as_deref(), debug_layout, format, swap_rb, &req_frame, ); data.main.needs_redraw = false; data.main.last_draw = std::time::Instant::now(); } for spec in overlays { if let Some( ss ) = data.overlays.get_mut( &spec.id ) { if !ss.configured || !ss.needs_redraw || ss.frame_pending { continue; } let focus = SurfaceFocus::Overlay( spec.id ); let req_frame = | wl: &WlSurface | { let _ = wl.frame( qh, focus ); }; let bg = Color::rgba( 0.0, 0.0, 0.0, 0.0 ); draw_surface::( ss, &data.compositor_state, egl_ctx, &spec.view, bg, spec.input_region.as_deref(), debug_layout, format, swap_rb, &req_frame, ); ss.needs_redraw = false; ss.last_draw = std::time::Instant::now(); } } } fn draw_surface( ss: &mut SurfaceState, compositor: &CompositorState, egl_ctx: Option<&Arc>, view: &Element, bg: Color, input_region: Option<&[Rect]>, debug_layout: bool, shm_format: wl_shm::Format, swap_rb: bool, request_frame: &dyn Fn( &WlSurface ), ) { let scale = ss.scale_factor.max( 1 ) as u32; let w = ss.width; let h = ss.height; if w == 0 || h == 0 { return; } let pw = w * scale; let ph = h * scale; let canvas_ready = ss.canvas.as_ref() .map( |c| c.size() == ( pw, ph ) ) .unwrap_or( false ); let partial_eligible = !ss.content_dirty && canvas_ready && !ss.widget_rects.is_empty(); if partial_eligible { let dirty_rects = compute_interaction_dirty_rects( &ss.widget_rects, ss.prev_focused, ss.prev_hovered, ss.prev_pressed, ss.focused_idx, ss.hovered_idx, ss.gesture.pressed_idx, pw, ph, ); if dirty_rects.is_empty() { return; } let total: f32 = dirty_rects.iter().map( |r| r.width * r.height ).sum(); if total < pw as f32 * ph as f32 * 0.5 { if let ( Some( ctx ), true ) = ( egl_ctx, ss.egl_surface.is_some() ) { draw_surface_partial_gpu( ss, compositor, ctx, view, bg, input_region, dirty_rects, pw, ph, scale, request_frame, ); } else { draw_surface_partial( ss, compositor, view, bg, input_region, shm_format, swap_rb, dirty_rects, pw, ph, scale, request_frame, ); } return; } } if let ( Some( ctx ), true ) = ( egl_ctx, ss.egl_surface.is_some() ) { draw_surface_full_gpu( ss, compositor, ctx, view, bg, input_region, debug_layout, pw, ph, scale, request_frame, ); } else { draw_surface_full( ss, compositor, view, bg, input_region, debug_layout, shm_format, swap_rb, pw, ph, scale, request_frame, ); } }