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#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
25#[repr(u8)]
26pub enum RenderZindex {
27 Background = 10,
29 Bottom = 20,
31 Shell = 30,
33 Top = 40,
35 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
53pub trait SpaceElement: IsAlive {
55 fn geometry(&self) -> Rectangle<i32, Logical> {
59 self.bbox()
60 }
61 fn bbox(&self) -> Rectangle<i32, Logical>;
63 fn is_in_input_region(&self, point: &Point<f64, Logical>) -> bool;
65 fn z_index(&self) -> u8 {
67 RenderZindex::Overlay as u8
68 }
69
70 fn set_activate(&self, activated: bool);
72 fn output_enter(&self, output: &Output, overlap: Rectangle<i32, Logical>);
77 fn output_leave(&self, output: &Output);
79 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#[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#[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 pub TestSpaceElements;
520 Window=TestElement,
522 }
523
524 space_elements! {
525 pub TestSpaceElements2<'a>;
527 Window=&'a TestElement,
529 }
530
531 space_elements! {
532 pub TestSpaceElements5<'a, C>;
534 Window=TestElement,
536 Custom=&'a C,
537 }
538
539 space_elements! {
540 pub TestSpaceElements7<C>;
542 Window=TestElement,
544 Custom=C,
545 }
546}