atagen

monolith

public
Star0
Watch0
47b86d08d05a97fea42c27b6ed194c0ceaa30374
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/monolith/src/layers.rsRust237 lines8.6 KB
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
}