1//! # Shell abstractions
2//!
3//! A shell describes a set of wayland protocol extensions which define the capabilities of a surface and how
4//! the surface is displayed.
5//!
6//! ## Cross desktop group (XDG) shell
7//!
8//! The XDG shell describes the semantics of desktop application windows.
9//!
10//! The XDG shell defines two types of surfaces:
11//! - [`Window`] - An application window[^window].
12//! - [`Popup`] - A child surface positioned relative to a window.
13//!
14//! ### Why use the XDG shell
15//!
16//! The XDG shell is the primary protocol through which application windows are created. You can be near
17//! certain every desktop compositor will implement this shell so that applications may create windows.
18//!
19//! See the [XDG shell module documentation] for more information about creating application windows.
20//!
21//! ## Layer shell
22//!
23//! The layer shell is a protocol which allows the creation of "layers". A layer refers to a surface rendered
24//! at some specific z-depth relative to other layers. A layer may also be anchored to some edge and corner of
25//! the screen.
26//!
27//! The layer shell defines one type of surface: the [`wlr_layer::LayerSurface`].
28//!
29//! There is no guarantee that the layer shell will be available in every compositor.
30//!
31//! ### Why use the layer shell
32//!
33//! The layer shell may be used to implement many desktop shell components, such as backgrounds, docks and
34//! launchers.
35//!
36//! [^window]: The XDG shell protocol actually refers to a window as a toplevel surface, but we use the more
37//! familiar term "window" for the sake of clarity.
38//!
39//! [XDG shell module documentation]: self::xdg
40//! [`Window`]: self::xdg::window::Window
41//! [`Popup`]: self::xdg::popup::Popup
42//!
43//! [`Layer`]: self::layer::LayerSurface
44
45use wayland_client::{
46 protocol::{wl_buffer, wl_output, wl_region, wl_surface},
47 Proxy,
48};
49
50pub mod wlr_layer;
51pub mod xdg;
52
53/// An unsupported operation, often due to the version of the protocol.
54#[derive(Debug, Default)]
55pub struct Unsupported;
56
57/// Functionality shared by all [`wl_surface::WlSurface`] backed shell role objects.
58pub trait WaylandSurface: Sized {
59 /// The underlying [`WlSurface`](wl_surface::WlSurface).
60 fn wl_surface(&self) -> &wl_surface::WlSurface;
61
62 fn attach(&self, buffer: Option<&wl_buffer::WlBuffer>, x: u32, y: u32) {
63 // In version 5 and later, the x and y offset of `wl_surface::attach` must be zero and uses the
64 // `offset` request instead.
65 let (attach_x, attach_y) = if self.wl_surface().version() >= 5 { (0, 0) } else { (x, y) };
66
67 self.wl_surface().attach(buffer, attach_x as i32, attach_y as i32);
68
69 if self.wl_surface().version() >= 5 {
70 // Ignore the error since the version is garunteed to be at least 5 here.
71 let _ = self.offset(x, y);
72 }
73 }
74
75 // TODO: Damage (Buffer and Surface-local)
76
77 // TODO: Frame (a nice helper for this could exist).
78
79 fn set_opaque_region(&self, region: Option<&wl_region::WlRegion>) {
80 self.wl_surface().set_opaque_region(region);
81 }
82
83 fn set_input_region(&self, region: Option<&wl_region::WlRegion>) {
84 self.wl_surface().set_input_region(region);
85 }
86
87 fn set_buffer_transform(&self, transform: wl_output::Transform) -> Result<(), Unsupported> {
88 if self.wl_surface().version() < 2 {
89 return Err(Unsupported);
90 }
91
92 self.wl_surface().set_buffer_transform(transform);
93 Ok(())
94 }
95
96 fn set_buffer_scale(&self, scale: u32) -> Result<(), Unsupported> {
97 if self.wl_surface().version() < 3 {
98 return Err(Unsupported);
99 }
100
101 self.wl_surface().set_buffer_scale(scale as i32);
102 Ok(())
103 }
104
105 fn offset(&self, x: u32, y: u32) -> Result<(), Unsupported> {
106 if self.wl_surface().version() < 5 {
107 return Err(Unsupported);
108 }
109
110 self.wl_surface().offset(x as i32, y as i32);
111 Ok(())
112 }
113
114 /// Commits pending surface state.
115 ///
116 /// On commit, the pending double buffered state from the surface, including role dependent state is
117 /// applied.
118 ///
119 /// # Initial commit
120 ///
121 /// In many protocol extensions, the concept of an initial commit is used. A initial commit provides the
122 /// initial state of a surface to the compositor. For example with the [xdg shell](xdg),
123 /// creating a window requires an initial commit.
124 ///
125 /// # Protocol Errors
126 ///
127 /// If the commit is the initial commit, no buffers must have been attached to the surface. This rule
128 /// applies whether attaching the buffer was done using [`WaylandSurface::attach`] or under the hood in
129 /// via window system integration in graphics APIs such as Vulkan (using `vkQueuePresentKHR`) and EGL
130 /// (using `eglSwapBuffers`).
131 fn commit(&self) {
132 self.wl_surface().commit();
133 }
134}
135