smithay/desktop/space/element/
mod.rs

1use std::hash::Hash;
2
3#[cfg(feature = "wayland_frontend")]
4use crate::{
5    backend::renderer::{element::surface::WaylandSurfaceRenderElement, ImportAll},
6    desktop::LayerSurface,
7    wayland::shell::wlr_layer::Layer,
8};
9use crate::{
10    backend::renderer::{
11        element::{AsRenderElements, Wrap},
12        Renderer, Texture,
13    },
14    output::Output,
15    utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale},
16};
17
18#[cfg(feature = "wayland_frontend")]
19mod wayland;
20#[cfg(feature = "wayland_frontend")]
21pub use self::wayland::SurfaceTree;
22
23/// Indicates default values for some zindexs inside smithay
24#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
25#[repr(u8)]
26pub enum RenderZindex {
27    /// WlrLayer::Background default zindex
28    Background = 10,
29    /// WlrLayer::Bottom default zindex
30    Bottom = 20,
31    /// Default zindex for Windows
32    Shell = 30,
33    /// WlrLayer::Top default zindex
34    Top = 40,
35    /// Default Layer for RenderElements
36    Overlay = 60,
37}
38
39impl From<RenderZindex> for u8 {
40    #[inline]
41    fn from(idx: RenderZindex) -> u8 {
42        idx as u8
43    }
44}
45
46impl From<RenderZindex> for Option<u8> {
47    #[inline]
48    fn from(idx: RenderZindex) -> Option<u8> {
49        Some(idx as u8)
50    }
51}
52
53/// Element mappable onto a [`Space`](super::Space)
54pub trait SpaceElement: IsAlive {
55    /// Returns the geometry of this element.
56    ///
57    /// Defaults to be equal to it's bounding box.
58    fn geometry(&self) -> Rectangle<i32, Logical> {
59        self.bbox()
60    }
61    /// Returns the bounding box of this element
62    fn bbox(&self) -> Rectangle<i32, Logical>;
63    /// Returns whenever a given point inside this element will be able to receive input
64    fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool;
65    /// Gets the z-index of this element
66    fn z_index(&self) -> u8 {
67        RenderZindex::Overlay as u8
68    }
69
70    /// Set the rendered state to activated, if applicable to this element
71    fn set_activate(&self, activated: bool);
72    /// The element is displayed on a given output.
73    ///
74    /// Maybe called for an already entered output,
75    /// if the overlap changes
76    fn output_enter(&self, output: &Output, overlap: Rectangle<i32, Logical>);
77    /// The element left a given output
78    fn output_leave(&self, output: &Output);
79    /// Periodically called to update internal state, if necessary
80    fn refresh(&self) {}
81}
82
83impl<T: SpaceElement> SpaceElement for &T {
84    fn geometry(&self) -> Rectangle<i32, Logical> {
85        SpaceElement::geometry(*self)
86    }
87    fn bbox(&self) -> Rectangle<i32, Logical> {
88        SpaceElement::bbox(*self)
89    }
90    fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool {
91        SpaceElement::is_in_input_region(*self, point)
92    }
93    fn z_index(&self) -> u8 {
94        SpaceElement::z_index(*self)
95    }
96
97    fn set_activate(&self, activated: bool) {
98        SpaceElement::set_activate(*self, activated)
99    }
100    fn output_enter(&self, output: &Output, overlap: Rectangle<i32, Logical>) {
101        SpaceElement::output_enter(*self, output, overlap)
102    }
103    fn output_leave(&self, output: &Output) {
104        SpaceElement::output_leave(*self, output)
105    }
106    fn refresh(&self) {
107        SpaceElement::refresh(*self)
108    }
109}
110
111#[derive(Debug)]
112pub(super) enum SpaceElements<'a, E> {
113    #[cfg(feature = "wayland_frontend")]
114    Layer {
115        surface: LayerSurface,
116        output_location: Point<i32, Logical>,
117    },
118    Element(&'a InnerElement<E>),
119}
120
121impl<E> SpaceElements<'_, E>
122where
123    E: SpaceElement,
124{
125    pub(super) fn z_index(&self) -> u8 {
126        match self {
127            #[cfg(feature = "wayland_frontend")]
128            SpaceElements::Layer { surface, .. } => {
129                let layer = match surface.layer() {
130                    Layer::Background => RenderZindex::Background,
131                    Layer::Bottom => RenderZindex::Bottom,
132                    Layer::Top => RenderZindex::Top,
133                    Layer::Overlay => RenderZindex::Overlay,
134                };
135                layer as u8
136            }
137            SpaceElements::Element(inner) => inner.element.z_index(),
138        }
139    }
140
141    pub(super) fn bbox(&self) -> Rectangle<i32, Logical> {
142        match self {
143            #[cfg(feature = "wayland_frontend")]
144            SpaceElements::Layer {
145                surface,
146                output_location,
147            } => {
148                let mut bbox = surface.bbox();
149                bbox.loc += *output_location;
150                bbox
151            }
152            SpaceElements::Element(inner) => inner.bbox(),
153        }
154    }
155
156    pub(super) fn render_location(&self) -> Point<i32, Logical> {
157        match self {
158            #[cfg(feature = "wayland_frontend")]
159            SpaceElements::Layer { .. } => self.bbox().loc,
160            SpaceElements::Element(inner) => inner.render_location(),
161        }
162    }
163}
164
165impl<
166        'a,
167        #[cfg(feature = "wayland_frontend")] R: Renderer + ImportAll,
168        #[cfg(not(feature = "wayland_frontend"))] R: Renderer,
169        E: AsRenderElements<R>,
170    > AsRenderElements<R> for SpaceElements<'a, E>
171where
172    R::TextureId: Clone + Texture + 'static,
173    <E as AsRenderElements<R>>::RenderElement: 'a,
174    SpaceRenderElements<R, <E as AsRenderElements<R>>::RenderElement>:
175        From<Wrap<<E as AsRenderElements<R>>::RenderElement>>,
176{
177    type RenderElement = SpaceRenderElements<R, <E as AsRenderElements<R>>::RenderElement>;
178
179    #[profiling::function]
180    fn render_elements<C: From<Self::RenderElement>>(
181        &self,
182        renderer: &mut R,
183        location: Point<i32, Physical>,
184        scale: Scale<f64>,
185        alpha: f32,
186    ) -> Vec<C> {
187        match &self {
188            #[cfg(feature = "wayland_frontend")]
189            SpaceElements::Layer { surface, .. } => AsRenderElements::<R>::render_elements::<
190                WaylandSurfaceRenderElement<R>,
191            >(surface, renderer, location, scale, alpha)
192            .into_iter()
193            .map(SpaceRenderElements::Surface)
194            .map(C::from)
195            .collect(),
196            SpaceElements::Element(element) => element
197                .element
198                .render_elements::<Wrap<<E as AsRenderElements<R>>::RenderElement>>(
199                    renderer, location, scale, alpha,
200                )
201                .into_iter()
202                .map(SpaceRenderElements::Element)
203                .map(C::from)
204                .collect(),
205        }
206    }
207}
208
209// Clippy: `large_enum_variant` is easily triggered here, as the `_GenericCatcher` is always zero-size.
210//          Thus we disable the lint.
211
212#[macro_export]
213#[doc(hidden)]
214macro_rules! space_elements_internal {
215    (@enum $(#[$attr:meta])* $vis:vis $name:ident<$lt:lifetime, $custom:ident>; $($(#[$meta:meta])* $body:ident=$field:ty),* $(,)?) => {
216        $(#[$attr])*
217        #[allow(clippy::large_enum_variant)]
218        $vis enum $name<$lt, $custom> {
219            $(
220                $(
221                    #[$meta]
222                )*
223                $body($field)
224            ),*,
225            #[doc(hidden)]
226            _GenericCatcher(std::convert::Infallible),
227        }
228    };
229    (@enum $(#[$attr:meta])* $vis:vis $name:ident<$custom:ident>; $($(#[$meta:meta])* $body:ident=$field:ty),* $(,)?) => {
230        $(#[$attr])*
231        #[allow(clippy::large_enum_variant)]
232        $vis enum $name<$custom> {
233            $(
234                $(
235                    #[$meta]
236                )*
237                $body($field)
238            ),*,
239            #[doc(hidden)]
240            _GenericCatcher(std::convert::Infallible),
241        }
242    };
243    (@enum $(#[$attr:meta])* $vis:vis $name:ident<$lt:lifetime>; $($(#[$meta:meta])* $body:ident=$field:ty),* $(,)?) => {
244        $(#[$attr])*
245        #[allow(clippy::large_enum_variant)]
246        $vis enum $name<$lt> {
247            $(
248                $(
249                    #[$meta]
250                )*
251                $body($field)
252            ),*,
253            #[doc(hidden)]
254            _GenericCatcher(std::convert::Infallible),
255        }
256    };
257    (@enum $(#[$attr:meta])* $vis:vis $name:ident; $($(#[$meta:meta])* $body:ident=$field:ty),* $(,)?) => {
258        $(#[$attr])*
259        #[allow(clippy::large_enum_variant)]
260        $vis enum $name {
261            $(
262                $(
263                    #[$meta]
264                )*
265                $body($field)
266            ),*,
267            #[doc(hidden)]
268            _GenericCatcher(std::convert::Infallible),
269        }
270    };
271    (@call $name:ident; $($x:ident),*) => {
272        $crate::desktop::space::SpaceElement::$name($($x),*)
273    };
274    (@alive $($(#[$meta:meta])* $body:ident=$field:ty),* $(,)?) => {
275        #[inline]
276        fn alive(&self) -> bool {
277            match self {
278                $(
279                    #[allow(unused_doc_comments)]
280                    $(
281                        #[$meta]
282                    )*
283                    Self::$body(x) => $crate::utils::IsAlive::alive(x)
284                ),*,
285                Self::_GenericCatcher(_) => unreachable!(),
286            }
287        }
288    };
289    (@body $($(#[$meta:meta])* $body:ident=$field:ty),* $(,)?) => {
290        fn geometry(&self) -> $crate::utils::Rectangle<i32, $crate::utils::Logical> {
291            match self {
292                $(
293                    #[allow(unused_doc_comments)]
294                    $(
295                        #[$meta]
296                    )*
297                    Self::$body(x) => $crate::space_elements_internal!(@call geometry; x)
298                ),*,
299                Self::_GenericCatcher(_) => unreachable!(),
300            }
301        }
302        fn bbox(&self) -> $crate::utils::Rectangle<i32, $crate::utils::Logical> {
303            match self {
304                $(
305                    #[allow(unused_doc_comments)]
306                    $(
307                        #[$meta]
308                    )*
309                    Self::$body(x) => $crate::space_elements_internal!(@call bbox; x)
310                ),*,
311                Self::_GenericCatcher(_) => unreachable!(),
312            }
313        }
314        fn is_in_input_region(&self, point: &$crate::utils::Point<f64, $crate::utils::Logical>) -> bool {
315            match self {
316                $(
317                    #[allow(unused_doc_comments)]
318                    $(
319                        #[$meta]
320                    )*
321                    Self::$body(x) => $crate::space_elements_internal!(@call is_in_input_region; x, point)
322                ),*,
323                Self::_GenericCatcher(_) => unreachable!(),
324            }
325        }
326
327        fn z_index(&self) -> u8 {
328            match self {
329                $(
330                    #[allow(unused_doc_comments)]
331                    $(
332                        #[$meta]
333                    )*
334                    Self::$body(x) => $crate::space_elements_internal!(@call z_index; x)
335                ),*,
336                Self::_GenericCatcher(_) => unreachable!(),
337            }
338        }
339
340        fn set_activate(&self, activated: bool) {
341            match self {
342                $(
343                    #[allow(unused_doc_comments)]
344                    $(
345                        #[$meta]
346                    )*
347                    Self::$body(x) => $crate::space_elements_internal!(@call set_activate; x, activated)
348                ),*,
349                Self::_GenericCatcher(_) => unreachable!(),
350            }
351        }
352        fn output_enter(&self, output: &$crate::output::Output, overlap: $crate::utils::Rectangle<i32, $crate::utils::Logical>) {
353            match self {
354                $(
355                    #[allow(unused_doc_comments)]
356                    $(
357                        #[$meta]
358                    )*
359                    Self::$body(x) => $crate::space_elements_internal!(@call output_enter; x, output, overlap)
360                ),*,
361                Self::_GenericCatcher(_) => unreachable!(),
362            }
363        }
364        fn output_leave(&self, output: &$crate::output::Output) {
365            match self {
366                $(
367                    #[allow(unused_doc_comments)]
368                    $(
369                        #[$meta]
370                    )*
371                    Self::$body(x) => $crate::space_elements_internal!(@call output_leave; x, output)
372                ),*,
373                Self::_GenericCatcher(_) => unreachable!(),
374            }
375        }
376        fn refresh(&self) {
377            match self {
378                $(
379                    #[allow(unused_doc_comments)]
380                    $(
381                        #[$meta]
382                    )*
383                    Self::$body(x) => $crate::space_elements_internal!(@call refresh; x)
384                ),*,
385                Self::_GenericCatcher(_) => unreachable!(),
386            }
387        }
388    };
389    (@impl $name:ident<$lt:lifetime>; $($tail:tt)*) => {
390        impl<$lt> $crate::desktop::space::SpaceElement for $name<$lt>
391        {
392            $crate::space_elements_internal!(@body $($tail)*);
393        }
394        impl<$lt> $crate::utils::IsAlive for $name<$lt>
395        {
396            $crate::space_elements_internal!(@alive $($tail)*);
397        }
398    };
399    (@impl $name:ident<$lt:lifetime, $custom:ident>; $($tail:tt)*) => {
400        impl<$lt, $custom> $crate::desktop::space::SpaceElement for $name<$lt, $custom>
401        where
402            $custom: $crate::desktop::space::SpaceElement,
403        {
404            $crate::space_elements_internal!(@body $($tail)*);
405        }
406        impl<$lt, $custom> $crate::utils::IsAlive for $name<$lt, $custom>
407        where
408            $custom: $crate::utils::IsAlive,
409        {
410            $crate::space_elements_internal!(@alive $($tail)*);
411        }
412    };
413    (@impl $name:ident<$custom:ident>; $($tail:tt)*) => {
414        impl<$custom> $crate::desktop::space::SpaceElement for $name<$custom>
415        where
416            $custom: $crate::desktop::space::SpaceElement,
417        {
418            $crate::space_elements_internal!(@body $($tail)*);
419        }
420        impl<$custom> $crate::utils::IsAlive for $name<$custom>
421        where
422            $custom: $crate::utils::IsAlive,
423        {
424            $crate::space_elements_internal!(@alive $($tail)*);
425        }
426    };
427    (@impl $name:ident; $($tail:tt)*) => {
428        impl $crate::desktop::space::SpaceElement for $name
429        {
430            $crate::space_elements_internal!(@body $($tail)*);
431        }
432        impl $crate::utils::IsAlive for $name
433        {
434            $crate::space_elements_internal!(@alive $($tail)*);
435        }
436    };
437}
438
439/// Aggregate multiple types implementing [`SpaceElement`] into a single enum type to be used
440/// with a [`Space`][super::Space].
441///
442/// # Example
443///
444/// ```
445/// use smithay::desktop::space::space_elements;
446/// # use smithay::{desktop::space::SpaceElement, output::Output, utils::{Point, Rectangle, Logical, IsAlive}};
447/// # struct Window;
448/// # impl SpaceElement for Window {
449/// #     fn bbox(&self) -> Rectangle<i32, Logical> { unimplemented!() }
450/// #     fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool { unimplemented!() }
451/// #     fn set_activate(&self, activated: bool) {}
452/// #     fn output_enter(&self, output: &Output, overlap: Rectangle<i32, Logical>) {}
453/// #     fn output_leave(&self, output: &Output) {}
454/// # }
455/// # impl IsAlive for Window {
456/// #     fn alive(&self) -> bool { unimplemented!() }
457/// # }
458///
459/// space_elements! {
460///     /// Name of the type
461///     pub MySpaceElements<'a, C>; // can be generic if necessary
462///     Window=Window, // variant name = type
463///     Custom=&'a C, // also supports references or usage of generic types
464/// }
465/// ```
466#[macro_export]
467macro_rules! space_elements {
468    ($(#[$attr:meta])* $vis:vis $name:ident<$lt:lifetime, $custom:ident>; $($tail:tt)*) => {
469        $crate::space_elements_internal!(@enum $(#[$attr])* $vis $name<$lt, $custom>; $($tail)*);
470        $crate::space_elements_internal!(@impl $name<$lt, $custom>; $($tail)*);
471    };
472    ($(#[$attr:meta])* $vis:vis $name:ident<$custom:ident>; $($tail:tt)*) => {
473        $crate::space_elements_internal!(@enum $(#[$attr])* $vis $name<$custom>; $($tail)*);
474        $crate::space_elements_internal!(@impl $name<$custom>; $($tail)*);
475    };
476    ($(#[$attr:meta])* $vis:vis $name:ident<$lt:lifetime>; $($tail:tt)*) => {
477        $crate::space_elements_internal!(@enum $(#[$attr])* $vis $name<$lt>; $($tail)*);
478        $crate::space_elements_internal!(@impl $name<$lt>; $($tail)*);
479    };
480    ($(#[$attr:meta])* $vis:vis $name:ident; $($tail:tt)*) => {
481        $crate::space_elements_internal!(@enum $(#[$attr])* $vis $name; $($tail)*);
482        $crate::space_elements_internal!(@impl $name; $($tail)*);
483    };
484}
485
486pub use space_elements;
487
488use super::{InnerElement, SpaceRenderElements};
489
490#[cfg(test)]
491#[allow(dead_code)]
492mod tests {
493    use crate::{
494        desktop::space::SpaceElement,
495        output::Output,
496        utils::{IsAlive, Logical, Point, Rectangle},
497    };
498
499    pub struct TestElement;
500    impl SpaceElement for TestElement {
501        fn bbox(&self) -> Rectangle<i32, Logical> {
502            unimplemented!()
503        }
504        fn is_in_input_region(&self, _point: &Point<f64, Logical>) -> bool {
505            unimplemented!()
506        }
507        fn set_activate(&self, _activated: bool) {}
508        fn output_enter(&self, _output: &Output, _overlap: Rectangle<i32, Logical>) {}
509        fn output_leave(&self, _output: &Output) {}
510    }
511    impl IsAlive for TestElement {
512        fn alive(&self) -> bool {
513            unimplemented!()
514        }
515    }
516
517    space_elements! {
518        /// Some test space elements
519        pub TestSpaceElements;
520        /// A complete surface tree
521        Window=TestElement,
522    }
523
524    space_elements! {
525        /// Some test space elements
526        pub TestSpaceElements2<'a>;
527        /// A complete surface tree
528        Window=&'a TestElement,
529    }
530
531    space_elements! {
532        /// Some test space elements
533        pub TestSpaceElements5<'a, C>;
534        /// A complete surface tree
535        Window=TestElement,
536        Custom=&'a C,
537    }
538
539    space_elements! {
540        /// Some test space elements
541        pub TestSpaceElements7<C>;
542        /// A complete surface tree
543        Window=TestElement,
544        Custom=C,
545    }
546}