use std::hash::Hash; #[cfg(feature = "wayland_frontend")] use crate::{ backend::renderer::{element::surface::WaylandSurfaceRenderElement, ImportAll}, desktop::LayerSurface, wayland::shell::wlr_layer::Layer, }; use crate::{ backend::renderer::{ element::{AsRenderElements, Wrap}, Renderer, Texture, }, output::Output, utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale}, }; #[cfg(feature = "wayland_frontend")] mod wayland; #[cfg(feature = "wayland_frontend")] pub use self::wayland::SurfaceTree; /// Indicates default values for some zindexs inside smithay #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(u8)] pub enum RenderZindex { /// WlrLayer::Background default zindex Background = 10, /// WlrLayer::Bottom default zindex Bottom = 20, /// Default zindex for Windows Shell = 30, /// WlrLayer::Top default zindex Top = 40, /// Default Layer for RenderElements Overlay = 60, } impl From for u8 { #[inline] fn from(idx: RenderZindex) -> u8 { idx as u8 } } impl From for Option { #[inline] fn from(idx: RenderZindex) -> Option { Some(idx as u8) } } /// Element mappable onto a [`Space`](super::Space) pub trait SpaceElement: IsAlive { /// Returns the geometry of this element. /// /// Defaults to be equal to it's bounding box. fn geometry(&self) -> Rectangle { self.bbox() } /// Returns the bounding box of this element fn bbox(&self) -> Rectangle; /// Returns whenever a given point inside this element will be able to receive input fn is_in_input_region(&self, point: &Point) -> bool; /// Gets the z-index of this element fn z_index(&self) -> u8 { RenderZindex::Overlay as u8 } /// Set the rendered state to activated, if applicable to this element fn set_activate(&self, activated: bool); /// The element is displayed on a given output. /// /// Maybe called for an already entered output, /// if the overlap changes fn output_enter(&self, output: &Output, overlap: Rectangle); /// The element left a given output fn output_leave(&self, output: &Output); /// Periodically called to update internal state, if necessary fn refresh(&self) {} } impl SpaceElement for &T { fn geometry(&self) -> Rectangle { SpaceElement::geometry(*self) } fn bbox(&self) -> Rectangle { SpaceElement::bbox(*self) } fn is_in_input_region(&self, point: &Point) -> bool { SpaceElement::is_in_input_region(*self, point) } fn z_index(&self) -> u8 { SpaceElement::z_index(*self) } fn set_activate(&self, activated: bool) { SpaceElement::set_activate(*self, activated) } fn output_enter(&self, output: &Output, overlap: Rectangle) { SpaceElement::output_enter(*self, output, overlap) } fn output_leave(&self, output: &Output) { SpaceElement::output_leave(*self, output) } fn refresh(&self) { SpaceElement::refresh(*self) } } #[derive(Debug)] pub(super) enum SpaceElements<'a, E> { #[cfg(feature = "wayland_frontend")] Layer { surface: LayerSurface, output_location: Point, }, Element(&'a InnerElement), } impl SpaceElements<'_, E> where E: SpaceElement, { pub(super) fn z_index(&self) -> u8 { match self { #[cfg(feature = "wayland_frontend")] SpaceElements::Layer { surface, .. } => { let layer = match surface.layer() { Layer::Background => RenderZindex::Background, Layer::Bottom => RenderZindex::Bottom, Layer::Top => RenderZindex::Top, Layer::Overlay => RenderZindex::Overlay, }; layer as u8 } SpaceElements::Element(inner) => inner.element.z_index(), } } pub(super) fn bbox(&self) -> Rectangle { match self { #[cfg(feature = "wayland_frontend")] SpaceElements::Layer { surface, output_location, } => { let mut bbox = surface.bbox(); bbox.loc += *output_location; bbox } SpaceElements::Element(inner) => inner.bbox(), } } pub(super) fn render_location(&self) -> Point { match self { #[cfg(feature = "wayland_frontend")] SpaceElements::Layer { .. } => self.bbox().loc, SpaceElements::Element(inner) => inner.render_location(), } } } impl< 'a, #[cfg(feature = "wayland_frontend")] R: Renderer + ImportAll, #[cfg(not(feature = "wayland_frontend"))] R: Renderer, E: AsRenderElements, > AsRenderElements for SpaceElements<'a, E> where R::TextureId: Clone + Texture + 'static, >::RenderElement: 'a, SpaceRenderElements>::RenderElement>: From>::RenderElement>>, { type RenderElement = SpaceRenderElements>::RenderElement>; #[profiling::function] fn render_elements>( &self, renderer: &mut R, location: Point, scale: Scale, alpha: f32, ) -> Vec { match &self { #[cfg(feature = "wayland_frontend")] SpaceElements::Layer { surface, .. } => AsRenderElements::::render_elements::< WaylandSurfaceRenderElement, >(surface, renderer, location, scale, alpha) .into_iter() .map(SpaceRenderElements::Surface) .map(C::from) .collect(), SpaceElements::Element(element) => element .element .render_elements::>::RenderElement>>( renderer, location, scale, alpha, ) .into_iter() .map(SpaceRenderElements::Element) .map(C::from) .collect(), } } } // Clippy: `large_enum_variant` is easily triggered here, as the `_GenericCatcher` is always zero-size. // Thus we disable the lint. #[macro_export] #[doc(hidden)] macro_rules! space_elements_internal { (@enum $(#[$attr:meta])* $vis:vis $name:ident<$lt:lifetime, $custom:ident>; $($(#[$meta:meta])* $body:ident=$field:ty),* $(,)?) => { $(#[$attr])* #[allow(clippy::large_enum_variant)] $vis enum $name<$lt, $custom> { $( $( #[$meta] )* $body($field) ),*, #[doc(hidden)] _GenericCatcher(std::convert::Infallible), } }; (@enum $(#[$attr:meta])* $vis:vis $name:ident<$custom:ident>; $($(#[$meta:meta])* $body:ident=$field:ty),* $(,)?) => { $(#[$attr])* #[allow(clippy::large_enum_variant)] $vis enum $name<$custom> { $( $( #[$meta] )* $body($field) ),*, #[doc(hidden)] _GenericCatcher(std::convert::Infallible), } }; (@enum $(#[$attr:meta])* $vis:vis $name:ident<$lt:lifetime>; $($(#[$meta:meta])* $body:ident=$field:ty),* $(,)?) => { $(#[$attr])* #[allow(clippy::large_enum_variant)] $vis enum $name<$lt> { $( $( #[$meta] )* $body($field) ),*, #[doc(hidden)] _GenericCatcher(std::convert::Infallible), } }; (@enum $(#[$attr:meta])* $vis:vis $name:ident; $($(#[$meta:meta])* $body:ident=$field:ty),* $(,)?) => { $(#[$attr])* #[allow(clippy::large_enum_variant)] $vis enum $name { $( $( #[$meta] )* $body($field) ),*, #[doc(hidden)] _GenericCatcher(std::convert::Infallible), } }; (@call $name:ident; $($x:ident),*) => { $crate::desktop::space::SpaceElement::$name($($x),*) }; (@alive $($(#[$meta:meta])* $body:ident=$field:ty),* $(,)?) => { #[inline] fn alive(&self) -> bool { match self { $( #[allow(unused_doc_comments)] $( #[$meta] )* Self::$body(x) => $crate::utils::IsAlive::alive(x) ),*, Self::_GenericCatcher(_) => unreachable!(), } } }; (@body $($(#[$meta:meta])* $body:ident=$field:ty),* $(,)?) => { fn geometry(&self) -> $crate::utils::Rectangle { match self { $( #[allow(unused_doc_comments)] $( #[$meta] )* Self::$body(x) => $crate::space_elements_internal!(@call geometry; x) ),*, Self::_GenericCatcher(_) => unreachable!(), } } fn bbox(&self) -> $crate::utils::Rectangle { match self { $( #[allow(unused_doc_comments)] $( #[$meta] )* Self::$body(x) => $crate::space_elements_internal!(@call bbox; x) ),*, Self::_GenericCatcher(_) => unreachable!(), } } fn is_in_input_region(&self, point: &$crate::utils::Point) -> bool { match self { $( #[allow(unused_doc_comments)] $( #[$meta] )* Self::$body(x) => $crate::space_elements_internal!(@call is_in_input_region; x, point) ),*, Self::_GenericCatcher(_) => unreachable!(), } } fn z_index(&self) -> u8 { match self { $( #[allow(unused_doc_comments)] $( #[$meta] )* Self::$body(x) => $crate::space_elements_internal!(@call z_index; x) ),*, Self::_GenericCatcher(_) => unreachable!(), } } fn set_activate(&self, activated: bool) { match self { $( #[allow(unused_doc_comments)] $( #[$meta] )* Self::$body(x) => $crate::space_elements_internal!(@call set_activate; x, activated) ),*, Self::_GenericCatcher(_) => unreachable!(), } } fn output_enter(&self, output: &$crate::output::Output, overlap: $crate::utils::Rectangle) { match self { $( #[allow(unused_doc_comments)] $( #[$meta] )* Self::$body(x) => $crate::space_elements_internal!(@call output_enter; x, output, overlap) ),*, Self::_GenericCatcher(_) => unreachable!(), } } fn output_leave(&self, output: &$crate::output::Output) { match self { $( #[allow(unused_doc_comments)] $( #[$meta] )* Self::$body(x) => $crate::space_elements_internal!(@call output_leave; x, output) ),*, Self::_GenericCatcher(_) => unreachable!(), } } fn refresh(&self) { match self { $( #[allow(unused_doc_comments)] $( #[$meta] )* Self::$body(x) => $crate::space_elements_internal!(@call refresh; x) ),*, Self::_GenericCatcher(_) => unreachable!(), } } }; (@impl $name:ident<$lt:lifetime>; $($tail:tt)*) => { impl<$lt> $crate::desktop::space::SpaceElement for $name<$lt> { $crate::space_elements_internal!(@body $($tail)*); } impl<$lt> $crate::utils::IsAlive for $name<$lt> { $crate::space_elements_internal!(@alive $($tail)*); } }; (@impl $name:ident<$lt:lifetime, $custom:ident>; $($tail:tt)*) => { impl<$lt, $custom> $crate::desktop::space::SpaceElement for $name<$lt, $custom> where $custom: $crate::desktop::space::SpaceElement, { $crate::space_elements_internal!(@body $($tail)*); } impl<$lt, $custom> $crate::utils::IsAlive for $name<$lt, $custom> where $custom: $crate::utils::IsAlive, { $crate::space_elements_internal!(@alive $($tail)*); } }; (@impl $name:ident<$custom:ident>; $($tail:tt)*) => { impl<$custom> $crate::desktop::space::SpaceElement for $name<$custom> where $custom: $crate::desktop::space::SpaceElement, { $crate::space_elements_internal!(@body $($tail)*); } impl<$custom> $crate::utils::IsAlive for $name<$custom> where $custom: $crate::utils::IsAlive, { $crate::space_elements_internal!(@alive $($tail)*); } }; (@impl $name:ident; $($tail:tt)*) => { impl $crate::desktop::space::SpaceElement for $name { $crate::space_elements_internal!(@body $($tail)*); } impl $crate::utils::IsAlive for $name { $crate::space_elements_internal!(@alive $($tail)*); } }; } /// Aggregate multiple types implementing [`SpaceElement`] into a single enum type to be used /// with a [`Space`][super::Space]. /// /// # Example /// /// ``` /// use smithay::desktop::space::space_elements; /// # use smithay::{desktop::space::SpaceElement, output::Output, utils::{Point, Rectangle, Logical, IsAlive}}; /// # struct Window; /// # impl SpaceElement for Window { /// # fn bbox(&self) -> Rectangle { unimplemented!() } /// # fn is_in_input_region(&self, point: &Point) -> bool { unimplemented!() } /// # fn set_activate(&self, activated: bool) {} /// # fn output_enter(&self, output: &Output, overlap: Rectangle) {} /// # fn output_leave(&self, output: &Output) {} /// # } /// # impl IsAlive for Window { /// # fn alive(&self) -> bool { unimplemented!() } /// # } /// /// space_elements! { /// /// Name of the type /// pub MySpaceElements<'a, C>; // can be generic if necessary /// Window=Window, // variant name = type /// Custom=&'a C, // also supports references or usage of generic types /// } /// ``` #[macro_export] macro_rules! space_elements { ($(#[$attr:meta])* $vis:vis $name:ident<$lt:lifetime, $custom:ident>; $($tail:tt)*) => { $crate::space_elements_internal!(@enum $(#[$attr])* $vis $name<$lt, $custom>; $($tail)*); $crate::space_elements_internal!(@impl $name<$lt, $custom>; $($tail)*); }; ($(#[$attr:meta])* $vis:vis $name:ident<$custom:ident>; $($tail:tt)*) => { $crate::space_elements_internal!(@enum $(#[$attr])* $vis $name<$custom>; $($tail)*); $crate::space_elements_internal!(@impl $name<$custom>; $($tail)*); }; ($(#[$attr:meta])* $vis:vis $name:ident<$lt:lifetime>; $($tail:tt)*) => { $crate::space_elements_internal!(@enum $(#[$attr])* $vis $name<$lt>; $($tail)*); $crate::space_elements_internal!(@impl $name<$lt>; $($tail)*); }; ($(#[$attr:meta])* $vis:vis $name:ident; $($tail:tt)*) => { $crate::space_elements_internal!(@enum $(#[$attr])* $vis $name; $($tail)*); $crate::space_elements_internal!(@impl $name; $($tail)*); }; } pub use space_elements; use super::{InnerElement, SpaceRenderElements}; #[cfg(test)] #[allow(dead_code)] mod tests { use crate::{ desktop::space::SpaceElement, output::Output, utils::{IsAlive, Logical, Point, Rectangle}, }; pub struct TestElement; impl SpaceElement for TestElement { fn bbox(&self) -> Rectangle { unimplemented!() } fn is_in_input_region(&self, _point: &Point) -> bool { unimplemented!() } fn set_activate(&self, _activated: bool) {} fn output_enter(&self, _output: &Output, _overlap: Rectangle) {} fn output_leave(&self, _output: &Output) {} } impl IsAlive for TestElement { fn alive(&self) -> bool { unimplemented!() } } space_elements! { /// Some test space elements pub TestSpaceElements; /// A complete surface tree Window=TestElement, } space_elements! { /// Some test space elements pub TestSpaceElements2<'a>; /// A complete surface tree Window=&'a TestElement, } space_elements! { /// Some test space elements pub TestSpaceElements5<'a, C>; /// A complete surface tree Window=TestElement, Custom=&'a C, } space_elements! { /// Some test space elements pub TestSpaceElements7; /// A complete surface tree Window=TestElement, Custom=C, } }