use std::sync::Arc;
use smithay_client_toolkit::compositor::CompositorState;
use smithay_client_toolkit::reexports::client::protocol::{ wl_shm, wl_surface::WlSurface };
use crate::event_loop::SurfaceState;
use crate::render::Canvas;
use crate::types::{ Color, Rect };
use crate::widget::Element;
use super::{ DrawCtx, compute_damage, layout_and_draw };
use super::chrome::{ apply_input_region, draw_fallback_banner, draw_titlebar };
pub( crate ) fn draw_surface_full<Msg: Clone>(
ss: &mut SurfaceState<Msg>,
compositor: &CompositorState,
view: &Element<Msg>,
bg: Color,
input_region: Option<&[Rect]>,
debug_layout: bool,
shm_format: wl_shm::Format,
swap_rb: bool,
pw: u32,
ph: u32,
scale: u32,
request_frame: &dyn Fn( &WlSurface ),
)
{
let Some( pool ) = ss.pool.as_mut() else { return };
let stride = pw * 4;
let ( buffer, canvas_buf ) = match pool.create_buffer(
pw as i32, ph as i32, stride as i32, shm_format,
)
{
Ok( r ) => r,
Err( _ ) => return,
};
let canvas = ss.canvas.get_or_insert_with( || {
let mut c = Canvas::new( pw, ph );
c.set_dpi_scale( scale as f32 );
if let Some( reg ) = crate::theme::build_font_registry()
{
c.set_font_registry( Arc::new( reg ) );
}
c
} );
if canvas.size() != ( pw, ph )
{
canvas.resize( pw, ph );
canvas.set_dpi_scale( scale as f32 );
}
canvas.clear_clip();
let sf = scale as f32;
let tb_h = ss.titlebar_height * sf;
let screen_rect = Rect { x: 0.0, y: tb_h, width: pw as f32, height: (ph as f32 - tb_h).max( 0.0 ) };
if bg.a > 0.0 { canvas.fill( bg ); } else { canvas.clear(); }
ss.titlebar_close_rect = draw_titlebar( canvas, &ss.titlebar_title, pw, tb_h, sf );
let mut ctx: DrawCtx<Msg> = DrawCtx
{
focused_idx: ss.focused_idx,
hovered_idx: ss.hovered_idx,
pressed_idx: ss.gesture.pressed_idx,
cursor_state: std::mem::take( &mut ss.cursor_state ),
selection_anchor: std::mem::take( &mut ss.selection_anchor ),
widget_rects: Vec::new(),
debug_layout,
scroll_offsets: std::mem::take( &mut ss.scroll_offsets ),
scroll_rects: Vec::new(),
scroll_canvases: std::mem::take( &mut ss.scroll_canvases ),
scroll_navigable_items: std::mem::take( &mut ss.scroll_navigable_items ),
previous_widget_rects: ss.widget_rects.clone(),
accessible_extras: Vec::new(),
live_depth: 0,
};
layout_and_draw::<Msg>( view, canvas, screen_rect, &mut ctx, 0 );
if ctx.debug_layout
{
for w in &ctx.widget_rects
{
canvas.stroke_rect( w.rect, Color::rgb( 1.0, 0.0, 0.0 ), 1.5, 0.0 );
}
}
if let Some( ref menu ) = ss.context_menu
{
super::draw_context_menu( canvas, menu );
}
draw_fallback_banner( canvas, pw, sf );
let damage_rects = if ss.content_dirty
{
Vec::new()
} else {
compute_damage(
&ss.widget_rects,
&ctx.widget_rects,
ss.prev_focused,
ss.prev_hovered,
ss.prev_pressed,
ss.focused_idx,
ss.hovered_idx,
ss.gesture.pressed_idx,
pw, ph,
)
};
ss.prev_focused = ss.focused_idx;
ss.prev_hovered = ss.hovered_idx;
ss.prev_pressed = ss.gesture.pressed_idx;
ss.widget_rects = ctx.widget_rects;
ss.scroll_rects = ctx.scroll_rects;
ss.scroll_canvases = std::mem::take( &mut ctx.scroll_canvases );
ss.cursor_state = ctx.cursor_state;
ss.selection_anchor = ctx.selection_anchor;
ss.scroll_offsets = ctx.scroll_offsets;
ss.accessible_extras = ctx.accessible_extras;
ss.scroll_navigable_items = ctx.scroll_navigable_items;
ss.content_dirty = false;
canvas.write_to_wayland_buf( canvas_buf, swap_rb );
let wl_surface = ss.surface.wl_surface();
buffer.attach_to( wl_surface ).expect( "attach" );
if damage_rects.is_empty()
{
wl_surface.damage_buffer( 0, 0, pw as i32, ph as i32 );
} else {
for r in &damage_rects
{
wl_surface.damage_buffer( r.x as i32, r.y as i32, r.width as i32, r.height as i32 );
}
}
apply_input_region( wl_surface, compositor, input_region, scale );
request_frame( wl_surface );
wl_surface.commit();
ss.frame_pending = true;
}
pub( crate ) fn draw_surface_partial<Msg: Clone>(
ss: &mut SurfaceState<Msg>,
compositor: &CompositorState,
view: &Element<Msg>,
bg: Color,
input_region: Option<&[Rect]>,
shm_format: wl_shm::Format,
swap_rb: bool,
dirty_rects: Vec<Rect>,
pw: u32,
ph: u32,
scale: u32,
request_frame: &dyn Fn( &WlSurface ),
)
{
let Some( pool ) = ss.pool.as_mut() else { return };
let stride = pw * 4;
let ( buffer, canvas_buf ) = match pool.create_buffer(
pw as i32, ph as i32, stride as i32, shm_format,
)
{
Ok( r ) => r,
Err( _ ) => return,
};
let canvas = ss.canvas.as_mut().expect( "partial path requires existing canvas" );
canvas.set_clip_rects( &dirty_rects );
let sf = scale as f32;
let tb_h = ss.titlebar_height * sf;
let screen_rect = Rect { x: 0.0, y: tb_h, width: pw as f32, height: (ph as f32 - tb_h).max( 0.0 ) };
if bg.a > 0.0 { canvas.fill( bg ); }
else
{
canvas.clear_rects_transparent( &dirty_rects );
}
ss.titlebar_close_rect = draw_titlebar( canvas, &ss.titlebar_title, pw, tb_h, sf );
let mut ctx: DrawCtx<Msg> = DrawCtx
{
focused_idx: ss.focused_idx,
hovered_idx: ss.hovered_idx,
pressed_idx: ss.gesture.pressed_idx,
cursor_state: std::mem::take( &mut ss.cursor_state ),
selection_anchor: std::mem::take( &mut ss.selection_anchor ),
widget_rects: Vec::new(),
debug_layout: false,
scroll_offsets: std::mem::take( &mut ss.scroll_offsets ),
scroll_rects: Vec::new(),
scroll_canvases: std::mem::take( &mut ss.scroll_canvases ),
scroll_navigable_items: std::mem::take( &mut ss.scroll_navigable_items ),
previous_widget_rects: ss.widget_rects.clone(),
accessible_extras: Vec::new(),
live_depth: 0,
};
layout_and_draw::<Msg>( view, canvas, screen_rect, &mut ctx, 0 );
if let Some( ref menu ) = ss.context_menu
{
super::draw_context_menu( canvas, menu );
}
draw_fallback_banner( canvas, pw, sf );
canvas.clear_clip();
ss.prev_focused = ss.focused_idx;
ss.prev_hovered = ss.hovered_idx;
ss.prev_pressed = ss.gesture.pressed_idx;
ss.widget_rects = ctx.widget_rects;
ss.scroll_rects = ctx.scroll_rects;
ss.scroll_canvases = std::mem::take( &mut ctx.scroll_canvases );
ss.cursor_state = ctx.cursor_state;
ss.selection_anchor = ctx.selection_anchor;
ss.scroll_offsets = ctx.scroll_offsets;
ss.accessible_extras = ctx.accessible_extras;
ss.scroll_navigable_items = ctx.scroll_navigable_items;
ss.content_dirty = false;
canvas.write_to_wayland_buf( canvas_buf, swap_rb );
let wl_surface = ss.surface.wl_surface();
buffer.attach_to( wl_surface ).expect( "attach" );
for r in &dirty_rects
{
wl_surface.damage_buffer( r.x as i32, r.y as i32, r.width as i32, r.height as i32 );
}
apply_input_region( wl_surface, compositor, input_region, scale );
request_frame( wl_surface );
wl_surface.commit();
ss.frame_pending = true;
}