From 41e493728c3d92856c85c989c40f170ef9bd49db Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Thu, 24 Sep 2026 16:54:33 +0100 Subject: [PATCH] feat: render layer shell surfaces --- compositor/src/backends/direct.rs | 26 +++++++++- compositor/src/winit.rs | 81 ++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/compositor/src/backends/direct.rs b/compositor/src/backends/direct.rs index 992b37f..8de169c 100644 --- a/compositor/src/backends/direct.rs +++ b/compositor/src/backends/direct.rs @@ -13,7 +13,7 @@ use smithay::{ session::{Event as SessionEvent, Session, libseat::LibSeatSession}, udev::{UdevBackend, UdevEvent, primary_gpu}, }, - desktop::utils::OutputPresentationFeedback, + desktop::{layer_map_for_output, utils::OutputPresentationFeedback}, output::{Mode as OutputMode, Output, PhysicalProperties, Scale, Subpixel}, reexports::{ calloop::EventLoop, @@ -321,6 +321,17 @@ fn render_device(state: &mut Ferese, node: DrmNode) { |_, _| Kind::Vsync | Kind::HwClock | Kind::HwCompletion, ); }); + let layers = layer_map_for_output(&device.output) + .layers() + .cloned() + .collect::>(); + layers.iter().for_each(|layer| { + layer.take_presentation_feedback( + &mut presentation, + |_, _| Some(device.output.clone()), + |_, _| Kind::Vsync | Kind::HwClock | Kind::HwCompletion, + ); + }); device .surface .queue_buffer(Some(result.sync), Some(damage), presentation)?; @@ -380,9 +391,22 @@ fn send_frame_callbacks(state: &mut Ferese) { |_, _| Some(output.clone()), ); }); + let layers = layer_map_for_output(&output) + .layers() + .cloned() + .collect::>(); + layers.iter().for_each(|layer| { + layer.send_frame( + &output, + state.start_time.elapsed(), + Some(Duration::ZERO), + |_, _| Some(output.clone()), + ); + }); state.send_cursor_frame(&output); state.space.refresh(); state.popups.cleanup(); + layer_map_for_output(&output).cleanup(); if let Err(error) = state.display_handle.flush_clients() { tracing::debug!(%error, "failed to flush clients after DRM frame"); } diff --git a/compositor/src/winit.rs b/compositor/src/winit.rs index 1ad8467..845491f 100644 --- a/compositor/src/winit.rs +++ b/compositor/src/winit.rs @@ -8,7 +8,7 @@ use smithay::{ renderer::{ damage::OutputDamageTracker, element::{ - Kind as RenderElementKind, + AsRenderElements, Kind as RenderElementKind, surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree}, utils::{ ConstrainAlign, ConstrainScaleBehavior, CropRenderElement, Relocate, @@ -20,6 +20,7 @@ use smithay::{ winit::{self, WinitEvent}, }, desktop::{ + LayerSurface, layer_map_for_output, space::{ConstrainBehavior, ConstrainReference, constrain_space_element}, utils::OutputPresentationFeedback, }, @@ -28,6 +29,7 @@ use smithay::{ reexports::calloop::EventLoop, reexports::wayland_protocols::wp::presentation_time::server::wp_presentation_feedback::Kind as PresentationKind, utils::{Clock, Logical, Monotonic, Physical, Point, Rectangle, Transform}, + wayland::shell::wlr_layer::Layer, wayland::{compositor::with_states, presentation::Refresh}, }; @@ -103,6 +105,13 @@ pub fn init(event_loop: &mut EventLoop, state: &mut Ferese) -> Result<() |_, _| PresentationKind::Vsync, ); }); + layer_surfaces(&output).iter().for_each(|layer| { + layer.take_presentation_feedback( + &mut presentation, + |_, _| Some(output.clone()), + |_, _| PresentationKind::Vsync, + ); + }); let rendered = (|| -> Result<(), Box> { { let (renderer, mut framebuffer) = backend.bind()?; @@ -138,9 +147,18 @@ pub fn init(event_loop: &mut EventLoop, state: &mut Ferese) -> Result<() |_, _| Some(output.clone()), ); }); + layer_surfaces(&output).iter().for_each(|layer| { + layer.send_frame( + &output, + state.start_time.elapsed(), + Some(Duration::ZERO), + |_, _| Some(output.clone()), + ); + }); state.send_cursor_frame(&output); state.space.refresh(); state.popups.cleanup(); + layer_map_for_output(&output).cleanup(); if let Err(error) = state.display_handle.flush_clients() { tracing::debug!(%error, "failed to flush clients"); } @@ -163,6 +181,11 @@ pub(crate) fn animated_window_elements( let scale = output.current_scale().fractional_scale(); let mut elements = cursor_elements(state, renderer, output_geometry, scale); + elements.extend(layer_elements( + renderer, + output, + &[Layer::Overlay, Layer::Top], + )); elements.extend( state .space @@ -204,9 +227,65 @@ pub(crate) fn animated_window_elements( }) .flatten(), ); + elements.extend(layer_elements( + renderer, + output, + &[Layer::Bottom, Layer::Background], + )); elements } +fn layer_surfaces(output: &Output) -> Vec { + layer_map_for_output(output).layers().cloned().collect() +} + +fn layer_elements( + renderer: &mut GlesRenderer, + output: &Output, + requested_layers: &[Layer], +) -> Vec { + let scale = output.current_scale().fractional_scale(); + let Some(mode) = output.current_mode() else { + return Vec::new(); + }; + let output_crop = Rectangle::::from_size(mode.size); + let layers = { + let map = layer_map_for_output(output); + + requested_layers + .iter() + .flat_map(|requested| { + map.layers_on(*requested).rev().filter_map(|layer| { + map.layer_geometry(layer) + .map(|geometry| (geometry.loc, layer.clone())) + }) + }) + .collect::>() + }; + + layers + .into_iter() + .flat_map(|(location, layer)| { + AsRenderElements::::render_elements::< + WaylandSurfaceRenderElement, + >( + &layer, + renderer, + location.to_physical_precise_round(scale), + scale.into(), + 1.0, + ) + }) + .filter_map(|element| { + let origin = Point::::default(); + let element = RescaleRenderElement::from_element(element, origin, 1.0); + let element = RelocateRenderElement::from_element(element, origin, Relocate::Relative); + + CropRenderElement::from_element(element, scale, output_crop) + }) + .collect() +} + fn cursor_elements( state: &Ferese, renderer: &mut GlesRenderer,