use smithay::{ backend::renderer::{ damage::{Error as OutputDamageTrackerError, OutputDamageTracker, RenderOutputResult}, element::{ surface::WaylandSurfaceRenderElement, utils::{ ConstrainAlign, ConstrainScaleBehavior, CropRenderElement, RelocateRenderElement, RescaleRenderElement, }, AsRenderElements, RenderElement, Wrap, }, Color32F, ImportAll, ImportMem, Renderer, }, desktop::space::{ constrain_space_element, ConstrainBehavior, ConstrainReference, Space, SpaceRenderElements, }, output::Output, utils::{Point, Rectangle, Size}, }; #[cfg(feature = "debug")] use crate::drawing::FpsElement; use crate::{ drawing::{PointerRenderElement, CLEAR_COLOR, CLEAR_COLOR_FULLSCREEN}, shell::{FullscreenSurface, WindowElement, WindowRenderElement}, }; smithay::backend::renderer::element::render_elements! { pub CustomRenderElements where R: ImportAll + ImportMem; Pointer=PointerRenderElement, Surface=WaylandSurfaceRenderElement, #[cfg(feature = "debug")] // Note: We would like to borrow this element instead, but that would introduce // a feature-dependent lifetime, which introduces a lot more feature bounds // as the whole type changes and we can't have an unused lifetime (for when "debug" is disabled) // in the declaration. Fps=FpsElement, } impl std::fmt::Debug for CustomRenderElements { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Pointer(arg0) => f.debug_tuple("Pointer").field(arg0).finish(), Self::Surface(arg0) => f.debug_tuple("Surface").field(arg0).finish(), #[cfg(feature = "debug")] Self::Fps(arg0) => f.debug_tuple("Fps").field(arg0).finish(), Self::_GenericCatcher(arg0) => f.debug_tuple("_GenericCatcher").field(arg0).finish(), } } } smithay::backend::renderer::element::render_elements! { pub OutputRenderElements where R: ImportAll + ImportMem; Space=SpaceRenderElements, Window=Wrap, Custom=CustomRenderElements, Preview=CropRenderElement>>>, } impl + std::fmt::Debug> std::fmt::Debug for OutputRenderElements { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Space(arg0) => f.debug_tuple("Space").field(arg0).finish(), Self::Window(arg0) => f.debug_tuple("Window").field(arg0).finish(), Self::Custom(arg0) => f.debug_tuple("Custom").field(arg0).finish(), Self::Preview(arg0) => f.debug_tuple("Preview").field(arg0).finish(), Self::_GenericCatcher(arg0) => f.debug_tuple("_GenericCatcher").field(arg0).finish(), } } } pub fn space_preview_elements<'a, R, C>( renderer: &'a mut R, space: &'a Space, output: &'a Output, ) -> impl Iterator + 'a where R: Renderer + ImportAll + ImportMem, R::TextureId: Clone + 'static, C: From>>>> + 'a, { let constrain_behavior = ConstrainBehavior { reference: ConstrainReference::BoundingBox, behavior: ConstrainScaleBehavior::Fit, align: ConstrainAlign::CENTER, }; let preview_padding = 10; let elements_on_space = space.elements_for_output(output).count(); let output_scale = output.current_scale().fractional_scale(); let output_transform = output.current_transform(); let output_size = output .current_mode() .map(|mode| { output_transform .transform_size(mode.size) .to_f64() .to_logical(output_scale) }) .unwrap_or_default(); let max_elements_per_row = 4; let elements_per_row = usize::min(elements_on_space, max_elements_per_row); let rows = f64::ceil(elements_on_space as f64 / elements_per_row as f64); let preview_size = Size::from(( f64::round(output_size.w / elements_per_row as f64) as i32 - preview_padding * 2, f64::round(output_size.h / rows) as i32 - preview_padding * 2, )); space .elements_for_output(output) .enumerate() .flat_map(move |(element_index, window)| { let column = element_index % elements_per_row; let row = element_index / elements_per_row; let preview_location = Point::from(( preview_padding + (preview_padding + preview_size.w) * column as i32, preview_padding + (preview_padding + preview_size.h) * row as i32, )); let constrain = Rectangle::new(preview_location, preview_size); constrain_space_element( renderer, window, preview_location, 1.0, output_scale, constrain, constrain_behavior, ) }) } #[profiling::function] pub fn output_elements( output: &Output, space: &Space, custom_elements: impl IntoIterator>, renderer: &mut R, show_window_preview: bool, ) -> (Vec>>, Color32F) where R: Renderer + ImportAll + ImportMem, R::TextureId: Clone + 'static, { if let Some(window) = output .user_data() .get::() .and_then(|f| f.get()) { let scale = output.current_scale().fractional_scale().into(); let window_render_elements: Vec> = AsRenderElements::::render_elements(&window, renderer, (0, 0).into(), scale, 1.0); let elements = custom_elements .into_iter() .map(OutputRenderElements::from) .chain( window_render_elements .into_iter() .map(|e| OutputRenderElements::Window(Wrap::from(e))), ) .collect::>(); (elements, CLEAR_COLOR_FULLSCREEN) } else { let mut output_render_elements = custom_elements .into_iter() .map(OutputRenderElements::from) .collect::>(); if show_window_preview && space.elements_for_output(output).count() > 0 { output_render_elements.extend(space_preview_elements(renderer, space, output)); } let space_elements = smithay::desktop::space::space_render_elements::<_, WindowElement, _>( renderer, [space], output, 1.0, ) .expect("output without mode?"); output_render_elements.extend(space_elements.into_iter().map(OutputRenderElements::Space)); (output_render_elements, CLEAR_COLOR) } } #[allow(clippy::too_many_arguments)] pub fn render_output<'a, 'd, R>( output: &'a Output, space: &'a Space, custom_elements: impl IntoIterator>, renderer: &'a mut R, framebuffer: &'a mut R::Framebuffer<'_>, damage_tracker: &'d mut OutputDamageTracker, age: usize, show_window_preview: bool, ) -> Result, OutputDamageTrackerError> where R: Renderer + ImportAll + ImportMem, R::TextureId: Clone + 'static, { let (elements, clear_color) = output_elements(output, space, custom_elements, renderer, show_window_preview); damage_tracker.render_output(renderer, framebuffer, age, &elements, clear_color) }