diff options
| -rw-r--r-- | src/handlers/compositor.rs | 3 | ||||
| -rw-r--r-- | src/handlers/layer_shell.rs | 100 | ||||
| -rw-r--r-- | src/handlers/mod.rs | 1 | ||||
| -rw-r--r-- | src/layout.rs | 10 | ||||
| -rw-r--r-- | src/niri.rs | 121 |
5 files changed, 216 insertions, 19 deletions
diff --git a/src/handlers/compositor.rs b/src/handlers/compositor.rs index 8b0f032b..2feba445 100644 --- a/src/handlers/compositor.rs +++ b/src/handlers/compositor.rs @@ -112,6 +112,9 @@ impl CompositorHandler for Niri { } } } + + // This might be a layer-shell surface. + self.layer_shell_handle_commit(surface); } } diff --git a/src/handlers/layer_shell.rs b/src/handlers/layer_shell.rs new file mode 100644 index 00000000..236ae7ab --- /dev/null +++ b/src/handlers/layer_shell.rs @@ -0,0 +1,100 @@ +use smithay::delegate_layer_shell; +use smithay::desktop::{layer_map_for_output, LayerSurface, WindowSurfaceType}; +use smithay::output::Output; +use smithay::reexports::wayland_server::protocol::wl_output::WlOutput; +use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; +use smithay::wayland::compositor::with_states; +use smithay::wayland::shell::wlr_layer::{ + Layer, LayerSurface as WlrLayerSurface, LayerSurfaceData, WlrLayerShellHandler, + WlrLayerShellState, +}; + +use crate::niri::Niri; + +impl WlrLayerShellHandler for Niri { + fn shell_state(&mut self) -> &mut WlrLayerShellState { + &mut self.layer_shell_state + } + + fn new_layer_surface( + &mut self, + surface: WlrLayerSurface, + wl_output: Option<WlOutput>, + _layer: Layer, + namespace: String, + ) { + let output = wl_output + .as_ref() + .and_then(Output::from_resource) + .or_else(|| self.monitor_set.active_output().cloned()) + .unwrap(); + let mut map = layer_map_for_output(&output); + map.map_layer(&LayerSurface::new(surface, namespace)) + .unwrap(); + } + + fn layer_destroyed(&mut self, surface: WlrLayerSurface) { + let output = if let Some((output, mut map, layer)) = + self.monitor_set.outputs().find_map(|o| { + let map = layer_map_for_output(o); + let layer = map + .layers() + .find(|&layer| layer.layer_surface() == &surface) + .cloned(); + layer.map(|layer| (o.clone(), map, layer)) + }) { + map.unmap_layer(&layer); + Some(output) + } else { + None + }; + if let Some(output) = output { + self.queue_redraw(output); + } + } +} +delegate_layer_shell!(Niri); + +impl Niri { + pub fn layer_shell_handle_commit(&mut self, surface: &WlSurface) { + let Some(output) = self + .monitor_set + .outputs() + .find(|o| { + let map = layer_map_for_output(o); + map.layer_for_surface(surface, WindowSurfaceType::TOPLEVEL) + .is_some() + }) + .cloned() + else { + return; + }; + + let initial_configure_sent = with_states(surface, |states| { + states + .data_map + .get::<LayerSurfaceData>() + .unwrap() + .lock() + .unwrap() + .initial_configure_sent + }); + + let mut map = layer_map_for_output(&output); + + // arrange the layers before sending the initial configure + // to respect any size the client may have sent + map.arrange(); + // send the initial configure if relevant + if !initial_configure_sent { + let layer = map + .layer_for_surface(surface, WindowSurfaceType::TOPLEVEL) + .unwrap(); + + layer.layer_surface().send_configure(); + } + drop(map); + + self.queue_redraw(output); + } +} diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index ab2a1596..4ad355ca 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -1,4 +1,5 @@ mod compositor; +mod layer_shell; mod xdg_shell; // diff --git a/src/layout.rs b/src/layout.rs index c4ab517b..7dc89c3d 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -566,6 +566,16 @@ impl<W: LayoutElement> MonitorSet<W> { .find(|monitor| &monitor.output == output) } + pub fn outputs(&self) -> impl Iterator<Item = &Output> + '_ { + let monitors = if let MonitorSet::Normal { monitors, .. } = self { + &monitors[..] + } else { + &[][..] + }; + + monitors.iter().map(|mon| &mon.output) + } + pub fn move_left(&mut self) { let Some(monitor) = self.active_monitor() else { return; diff --git a/src/niri.rs b/src/niri.rs index 29395c01..968392c3 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -3,10 +3,13 @@ use std::os::unix::io::AsRawFd; use std::sync::Arc; use std::time::Duration; -use smithay::backend::renderer::element::render_elements; use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement}; +use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement; +use smithay::backend::renderer::element::{render_elements, AsRenderElements}; use smithay::backend::renderer::ImportAll; -use smithay::desktop::{PopupManager, Space, Window, WindowSurfaceType}; +use smithay::desktop::{ + layer_map_for_output, LayerSurface, PopupManager, Space, Window, WindowSurfaceType, +}; use smithay::input::keyboard::XkbConfig; use smithay::input::{Seat, SeatState}; use smithay::output::Output; @@ -18,10 +21,12 @@ use smithay::reexports::wayland_server::backend::{ }; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; use smithay::reexports::wayland_server::{Display, DisplayHandle}; -use smithay::utils::{Logical, Point, SERIAL_COUNTER}; +use smithay::utils::{Logical, Point, Scale, SERIAL_COUNTER}; use smithay::wayland::compositor::{CompositorClientState, CompositorState}; use smithay::wayland::data_device::DataDeviceState; use smithay::wayland::output::OutputManagerState; +use smithay::wayland::seat::WaylandFocus; +use smithay::wayland::shell::wlr_layer::{Layer, WlrLayerShellState}; use smithay::wayland::shell::xdg::XdgShellState; use smithay::wayland::shm::ShmState; use smithay::wayland::socket::ListeningSocketSource; @@ -53,6 +58,7 @@ pub struct Niri { // Smithay state. pub compositor_state: CompositorState, pub xdg_shell_state: XdgShellState, + pub layer_shell_state: WlrLayerShellState, pub shm_state: ShmState, pub output_manager_state: OutputManagerState, pub seat_state: SeatState<Self>, @@ -95,6 +101,7 @@ impl Niri { WmCapabilities::WindowMenu, ], ); + let layer_shell_state = WlrLayerShellState::new::<Self>(&display_handle); let shm_state = ShmState::new::<Self>(&display_handle, vec![]); let output_manager_state = OutputManagerState::new_with_xdg_output::<Self>(&display_handle); let mut seat_state = SeatState::new(); @@ -155,6 +162,7 @@ impl Niri { compositor_state, xdg_shell_state, + layer_shell_state, shm_state, output_manager_state, seat_state, @@ -203,6 +211,7 @@ impl Niri { pub fn output_resized(&mut self, output: Output) { self.monitor_set.update_output(&output); + layer_map_for_output(&output).arrange(); self.queue_redraw(output); } @@ -256,11 +265,23 @@ impl Niri { self.global_space.output_under(pos).next().cloned() } + fn layer_surface_focus(&self) -> Option<WlSurface> { + let output = self.monitor_set.active_output()?; + let layers = layer_map_for_output(output); + let surface = layers + .layers_on(Layer::Overlay) + .chain(layers.layers_on(Layer::Top)) + .find(|surface| surface.can_receive_keyboard_focus())?; + + Some(surface.wl_surface().clone()) + } + pub fn update_focus(&mut self) { - let focus = self - .monitor_set - .focus() - .map(|win| win.toplevel().wl_surface().clone()); + let focus = self.layer_surface_focus().or_else(|| { + self.monitor_set + .focus() + .map(|win| win.toplevel().wl_surface().clone()) + }); let keyboard = self.seat.get_keyboard().unwrap(); if keyboard.current_focus() != focus { keyboard.set_focus(self, focus, SERIAL_COUNTER.next_serial()); @@ -306,38 +327,100 @@ impl Niri { assert!(state.queued_redraw.take().is_some()); assert!(!state.waiting_for_vblank); + let renderer = backend.renderer(); + let mon = self.monitor_set.monitor_for_output_mut(output).unwrap(); mon.advance_animations(presentation_time); - let elements = mon.render_elements(backend.renderer()); + // Get monitor elements. + let monitor_elements = mon.render_elements(renderer); let output_pos = self.global_space.output_geometry(output).unwrap().loc; let pointer_pos = self.seat.get_pointer().unwrap().current_location() - output_pos.to_f64(); - let mut elements: Vec<_> = elements - .into_iter() - .map(OutputRenderElements::from) - .collect(); - elements.insert( - 0, - OutputRenderElements::Pointer(SolidColorRenderElement::from_buffer( + // Get layer-shell elements. + let layer_map = layer_map_for_output(output); + let (lower, upper): (Vec<&LayerSurface>, Vec<&LayerSurface>) = layer_map + .layers() + .rev() + .partition(|s| matches!(s.layer(), Layer::Background | Layer::Bottom)); + + // The pointer goes on the top. + let mut elements = vec![OutputRenderElements::Pointer( + SolidColorRenderElement::from_buffer( &self.pointer_buffer, pointer_pos.to_physical_precise_round(1.), 1., 1., - )), + ), + )]; + + // Then the upper layer-shell elements. + elements.extend( + upper + .into_iter() + .filter_map(|surface| { + layer_map + .layer_geometry(surface) + .map(|geo| (geo.loc, surface)) + }) + .flat_map(|(loc, surface)| { + surface + .render_elements( + renderer, + loc.to_physical_precise_round(1.), + Scale::from(1.), + 1., + ) + .into_iter() + .map(OutputRenderElements::Wayland) + }), + ); + + // Then the regular monitor elements. + elements.extend(monitor_elements.into_iter().map(OutputRenderElements::from)); + + // Then the lower layer-shell elements. + elements.extend( + lower + .into_iter() + .filter_map(|surface| { + layer_map + .layer_geometry(surface) + .map(|geo| (geo.loc, surface)) + }) + .flat_map(|(loc, surface)| { + surface + .render_elements( + renderer, + loc.to_physical_precise_round(1.), + Scale::from(1.), + 1., + ) + .into_iter() + .map(OutputRenderElements::Wayland) + }), ); + // Hand it over to the backend. backend.render(self, output, &elements); - self.monitor_set - .send_frame(output, self.start_time.elapsed()); + // Send frame callbacks. + let frame_callback_time = self.start_time.elapsed(); + self.monitor_set.send_frame(output, frame_callback_time); + + for surface in layer_map.layers() { + surface.send_frame(output, frame_callback_time, None, |_, _| { + Some(output.clone()) + }); + } } } render_elements! { #[derive(Debug)] pub OutputRenderElements<R> where R: ImportAll; - Wayland = MonitorRenderElement<R>, + Monitor = MonitorRenderElement<R>, + Wayland = WaylandSurfaceRenderElement<R>, Pointer = SolidColorRenderElement, } |
