| 1 | //! wlr-layer-shell, for bars, launchers, wallpapers and notifications. |
| 2 | |
| 3 | use crate::scene::Draw; |
| 4 | use crate::state::State; |
| 5 | use monolith_layout::{Insets, OutputId}; |
| 6 | use monolith_render_vk::VulkanRenderer; |
| 7 | use smithay::backend::renderer::element::AsRenderElements; |
| 8 | use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement; |
| 9 | use smithay::desktop::{LayerSurface, PopupKind, WindowSurfaceType, layer_map_for_output}; |
| 10 | use smithay::output::Output; |
| 11 | use smithay::reexports::wayland_server::Resource; |
| 12 | use smithay::reexports::wayland_server::protocol::wl_output::WlOutput; |
| 13 | use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; |
| 14 | use smithay::utils::{Logical, Point, Scale}; |
| 15 | use smithay::wayland::compositor::with_states; |
| 16 | use smithay::wayland::shell::wlr_layer::{ |
| 17 | KeyboardInteractivity, Layer, LayerSurface as WlrLayerSurface, LayerSurfaceData, |
| 18 | WlrLayerShellHandler, WlrLayerShellState, |
| 19 | }; |
| 20 | use smithay::wayland::shell::xdg::PopupSurface; |
| 21 | use std::time::Duration; |
| 22 | |
| 23 | impl WlrLayerShellHandler for State { |
| 24 | fn shell_state(&mut self) -> &mut WlrLayerShellState { |
| 25 | &mut self.protocols.layer_shell_state |
| 26 | } |
| 27 | |
| 28 | fn new_layer_surface( |
| 29 | &mut self, |
| 30 | surface: WlrLayerSurface, |
| 31 | output: Option<WlOutput>, |
| 32 | _layer: Layer, |
| 33 | namespace: String, |
| 34 | ) { |
| 35 | let output = output |
| 36 | .as_ref() |
| 37 | .and_then(Output::from_resource) |
| 38 | .or_else(|| { |
| 39 | self.wm |
| 40 | .space |
| 41 | .output_under(self.input.pointer_pos) |
| 42 | .next() |
| 43 | .cloned() |
| 44 | }) |
| 45 | .or_else(|| self.wm.space.outputs().next().cloned()); |
| 46 | let Some(output) = output else { |
| 47 | surface.send_close(); |
| 48 | return; |
| 49 | }; |
| 50 | tracing::debug!(namespace, output = output.name(), "new layer surface"); |
| 51 | let mut map = layer_map_for_output(&output); |
| 52 | if let Err(e) = map.map_layer(&LayerSurface::new(surface, namespace)) { |
| 53 | tracing::warn!("cannot map layer surface: {e}"); |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | fn new_popup(&mut self, _parent: WlrLayerSurface, popup: PopupSurface) { |
| 58 | let _ = self.wm.popups.track_popup(PopupKind::Xdg(popup)); |
| 59 | } |
| 60 | |
| 61 | fn layer_destroyed(&mut self, surface: WlrLayerSurface) { |
| 62 | let outputs: Vec<Output> = self.wm.space.outputs().cloned().collect(); |
| 63 | for output in outputs { |
| 64 | let mut map = layer_map_for_output(&output); |
| 65 | let gone = map |
| 66 | .layers() |
| 67 | .find(|l| l.layer_surface() == &surface) |
| 68 | .cloned(); |
| 69 | if let Some(layer) = gone { |
| 70 | map.unmap_layer(&layer); |
| 71 | } |
| 72 | } |
| 73 | if self.wm.layer_focus.as_ref() == Some(surface.wl_surface()) { |
| 74 | self.wm.layer_focus = None; |
| 75 | } |
| 76 | self.reserve_layers(); |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | impl State { |
| 81 | /// Sends a layer surface its first configure once it has committed its |
| 82 | /// anchors and size, and picks up exclusive zone changes after that. |
| 83 | /// Returns false for surfaces that are not layer surfaces. |
| 84 | pub fn layer_commit(&mut self, surface: &WlSurface) -> bool { |
| 85 | let Some(output) = self |
| 86 | .wm |
| 87 | .space |
| 88 | .outputs() |
| 89 | .find(|o| { |
| 90 | layer_map_for_output(o) |
| 91 | .layer_for_surface(surface, WindowSurfaceType::TOPLEVEL) |
| 92 | .is_some() |
| 93 | }) |
| 94 | .cloned() |
| 95 | else { |
| 96 | return false; |
| 97 | }; |
| 98 | let configured = with_states(surface, |s| { |
| 99 | s.data_map |
| 100 | .get::<LayerSurfaceData>() |
| 101 | .and_then(|d| d.lock().ok()) |
| 102 | .is_some_and(|d| d.initial_configure_sent) |
| 103 | }); |
| 104 | { |
| 105 | let mut map = layer_map_for_output(&output); |
| 106 | map.arrange(); |
| 107 | if !configured |
| 108 | && let Some(layer) = map.layer_for_surface(surface, WindowSurfaceType::TOPLEVEL) |
| 109 | { |
| 110 | layer.layer_surface().send_configure(); |
| 111 | } |
| 112 | } |
| 113 | self.reserve_layers(); |
| 114 | true |
| 115 | } |
| 116 | |
| 117 | /// Closes and unmaps every layer surface on an output that was unplugged. |
| 118 | pub fn close_layers(&mut self, output: &Output) { |
| 119 | let mut map = layer_map_for_output(output); |
| 120 | let layers: Vec<LayerSurface> = map.layers().cloned().collect(); |
| 121 | for layer in layers { |
| 122 | layer.layer_surface().send_close(); |
| 123 | map.unmap_layer(&layer); |
| 124 | if self.wm.layer_focus.as_ref() == Some(layer.wl_surface()) { |
| 125 | self.wm.layer_focus = None; |
| 126 | } |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | /// Keeps tiled windows out of the space bars reserve. |
| 131 | pub fn reserve_layers(&mut self) { |
| 132 | let outputs: Vec<OutputId> = self.wm.layout.outputs().map(|(o, _)| o).collect(); |
| 133 | let mut changed = false; |
| 134 | for o in outputs { |
| 135 | let Some(output) = self.backend.output(o) else { |
| 136 | continue; |
| 137 | }; |
| 138 | let Some(area) = self.wm.layout.output(o).map(|out| out.area) else { |
| 139 | continue; |
| 140 | }; |
| 141 | let mut map = layer_map_for_output(&output); |
| 142 | map.arrange(); |
| 143 | let zone = map.non_exclusive_zone(); |
| 144 | let reserved = Insets { |
| 145 | top: f64::from(zone.loc.y), |
| 146 | left: f64::from(zone.loc.x), |
| 147 | bottom: (area.h - f64::from(zone.loc.y + zone.size.h)).max(0.0), |
| 148 | right: (area.w - f64::from(zone.loc.x + zone.size.w)).max(0.0), |
| 149 | }; |
| 150 | drop(map); |
| 151 | if self.wm.layout.set_reserved(o, reserved) { |
| 152 | tracing::debug!( |
| 153 | output = output.name(), |
| 154 | ?zone, |
| 155 | ?reserved, |
| 156 | "layer zones changed" |
| 157 | ); |
| 158 | changed = true; |
| 159 | } |
| 160 | } |
| 161 | if changed { |
| 162 | self.apply_layout(); |
| 163 | } else { |
| 164 | self.sync_focus(); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | /// The layer surface that should hold the keyboard, if any. One asking |
| 169 | /// for exclusive focus above the windows wins over everything. |
| 170 | pub fn layer_keyboard_focus(&self) -> Option<WlSurface> { |
| 171 | for output in self.wm.space.outputs() { |
| 172 | let map = layer_map_for_output(output); |
| 173 | let exclusive = map |
| 174 | .layers_on(Layer::Overlay) |
| 175 | .chain(map.layers_on(Layer::Top)) |
| 176 | .find(|l| { |
| 177 | l.cached_state().keyboard_interactivity == KeyboardInteractivity::Exclusive |
| 178 | }); |
| 179 | if let Some(l) = exclusive { |
| 180 | return Some(l.wl_surface().clone()); |
| 181 | } |
| 182 | } |
| 183 | self.wm.layer_focus.clone().filter(|s| s.is_alive()) |
| 184 | } |
| 185 | |
| 186 | pub fn layer_maps_contain(&self, surface: &WlSurface) -> bool { |
| 187 | self.wm.space.outputs().any(|o| { |
| 188 | layer_map_for_output(o) |
| 189 | .layer_for_surface(surface, WindowSurfaceType::TOPLEVEL) |
| 190 | .is_some() |
| 191 | }) |
| 192 | } |
| 193 | |
| 194 | /// The layer surface under `pos` on `layers`, with its surface origin. |
| 195 | pub fn layer_under( |
| 196 | &self, |
| 197 | pos: Point<f64, Logical>, |
| 198 | layers: &[Layer], |
| 199 | ) -> Option<(LayerSurface, WlSurface, Point<f64, Logical>)> { |
| 200 | let output = self.wm.space.output_under(pos).next()?; |
| 201 | let origin = self.wm.space.output_geometry(output)?.loc; |
| 202 | let local = pos - origin.to_f64(); |
| 203 | let map = layer_map_for_output(output); |
| 204 | layers.iter().find_map(|&layer| { |
| 205 | let l = map.layer_under(layer, local)?; |
| 206 | let geo = map.layer_geometry(l)?; |
| 207 | let (s, p) = l.surface_under(local - geo.loc.to_f64(), WindowSurfaceType::ALL)?; |
| 208 | Some((l.clone(), s, (p + geo.loc + origin).to_f64())) |
| 209 | }) |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | /// Draws an output's layer surfaces on `layer`, front to back. |
| 214 | pub fn elements(out: &mut Vec<Draw>, renderer: &mut VulkanRenderer, output: &Output, layer: Layer) { |
| 215 | let map = layer_map_for_output(output); |
| 216 | for l in map.layers_on(layer).rev() { |
| 217 | let Some(geo) = map.layer_geometry(l) else { |
| 218 | continue; |
| 219 | }; |
| 220 | let elems: Vec<WaylandSurfaceRenderElement<VulkanRenderer>> = |
| 221 | l.render_elements(renderer, geo.loc.to_physical(1), Scale::from(1.0), 1.0); |
| 222 | out.extend(elems.into_iter().map(Draw::Surface)); |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | /// With `covered`, a fullscreen window hides everything below the overlay |
| 227 | /// layer, so only overlay surfaces are asked to draw. |
| 228 | pub fn send_frames(output: &Output, elapsed: Duration, covered: bool) { |
| 229 | for l in layer_map_for_output(output).layers() { |
| 230 | if covered && l.layer() != Layer::Overlay { |
| 231 | continue; |
| 232 | } |
| 233 | l.send_frame(output, elapsed, Some(Duration::ZERO), |_, _| { |
| 234 | Some(output.clone()) |
| 235 | }); |
| 236 | } |
| 237 | } |