Add initial layer-shell implementation

Those surfaces never receive mouse focus and always keyboard focus,
which may not be always good, but it'll do for now.
This commit is contained in:
Ivan Molodetskikh
2023-08-15 12:49:26 +04:00
parent 8cdd37b993
commit 37cbed890f
5 changed files with 216 additions and 19 deletions
+3
View File
@@ -112,6 +112,9 @@ impl CompositorHandler for Niri {
} }
} }
} }
// This might be a layer-shell surface.
self.layer_shell_handle_commit(surface);
} }
} }
+100
View File
@@ -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);
}
}
+1
View File
@@ -1,4 +1,5 @@
mod compositor; mod compositor;
mod layer_shell;
mod xdg_shell; mod xdg_shell;
// //
+10
View File
@@ -566,6 +566,16 @@ impl<W: LayoutElement> MonitorSet<W> {
.find(|monitor| &monitor.output == output) .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) { pub fn move_left(&mut self) {
let Some(monitor) = self.active_monitor() else { let Some(monitor) = self.active_monitor() else {
return; return;
+101 -18
View File
@@ -3,10 +3,13 @@ use std::os::unix::io::AsRawFd;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use smithay::backend::renderer::element::render_elements;
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement}; 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::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::keyboard::XkbConfig;
use smithay::input::{Seat, SeatState}; use smithay::input::{Seat, SeatState};
use smithay::output::Output; 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::protocol::wl_surface::WlSurface;
use smithay::reexports::wayland_server::{Display, DisplayHandle}; 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::compositor::{CompositorClientState, CompositorState};
use smithay::wayland::data_device::DataDeviceState; use smithay::wayland::data_device::DataDeviceState;
use smithay::wayland::output::OutputManagerState; 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::shell::xdg::XdgShellState;
use smithay::wayland::shm::ShmState; use smithay::wayland::shm::ShmState;
use smithay::wayland::socket::ListeningSocketSource; use smithay::wayland::socket::ListeningSocketSource;
@@ -53,6 +58,7 @@ pub struct Niri {
// Smithay state. // Smithay state.
pub compositor_state: CompositorState, pub compositor_state: CompositorState,
pub xdg_shell_state: XdgShellState, pub xdg_shell_state: XdgShellState,
pub layer_shell_state: WlrLayerShellState,
pub shm_state: ShmState, pub shm_state: ShmState,
pub output_manager_state: OutputManagerState, pub output_manager_state: OutputManagerState,
pub seat_state: SeatState<Self>, pub seat_state: SeatState<Self>,
@@ -95,6 +101,7 @@ impl Niri {
WmCapabilities::WindowMenu, WmCapabilities::WindowMenu,
], ],
); );
let layer_shell_state = WlrLayerShellState::new::<Self>(&display_handle);
let shm_state = ShmState::new::<Self>(&display_handle, vec![]); let shm_state = ShmState::new::<Self>(&display_handle, vec![]);
let output_manager_state = OutputManagerState::new_with_xdg_output::<Self>(&display_handle); let output_manager_state = OutputManagerState::new_with_xdg_output::<Self>(&display_handle);
let mut seat_state = SeatState::new(); let mut seat_state = SeatState::new();
@@ -155,6 +162,7 @@ impl Niri {
compositor_state, compositor_state,
xdg_shell_state, xdg_shell_state,
layer_shell_state,
shm_state, shm_state,
output_manager_state, output_manager_state,
seat_state, seat_state,
@@ -203,6 +211,7 @@ impl Niri {
pub fn output_resized(&mut self, output: Output) { pub fn output_resized(&mut self, output: Output) {
self.monitor_set.update_output(&output); self.monitor_set.update_output(&output);
layer_map_for_output(&output).arrange();
self.queue_redraw(output); self.queue_redraw(output);
} }
@@ -256,11 +265,23 @@ impl Niri {
self.global_space.output_under(pos).next().cloned() 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) { pub fn update_focus(&mut self) {
let focus = self let focus = self.layer_surface_focus().or_else(|| {
.monitor_set self.monitor_set
.focus() .focus()
.map(|win| win.toplevel().wl_surface().clone()); .map(|win| win.toplevel().wl_surface().clone())
});
let keyboard = self.seat.get_keyboard().unwrap(); let keyboard = self.seat.get_keyboard().unwrap();
if keyboard.current_focus() != focus { if keyboard.current_focus() != focus {
keyboard.set_focus(self, focus, SERIAL_COUNTER.next_serial()); keyboard.set_focus(self, focus, SERIAL_COUNTER.next_serial());
@@ -306,38 +327,100 @@ impl Niri {
assert!(state.queued_redraw.take().is_some()); assert!(state.queued_redraw.take().is_some());
assert!(!state.waiting_for_vblank); assert!(!state.waiting_for_vblank);
let renderer = backend.renderer();
let mon = self.monitor_set.monitor_for_output_mut(output).unwrap(); let mon = self.monitor_set.monitor_for_output_mut(output).unwrap();
mon.advance_animations(presentation_time); 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 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 pointer_pos = self.seat.get_pointer().unwrap().current_location() - output_pos.to_f64();
let mut elements: Vec<_> = elements // Get layer-shell elements.
.into_iter() let layer_map = layer_map_for_output(output);
.map(OutputRenderElements::from) let (lower, upper): (Vec<&LayerSurface>, Vec<&LayerSurface>) = layer_map
.collect(); .layers()
elements.insert( .rev()
0, .partition(|s| matches!(s.layer(), Layer::Background | Layer::Bottom));
OutputRenderElements::Pointer(SolidColorRenderElement::from_buffer(
// The pointer goes on the top.
let mut elements = vec![OutputRenderElements::Pointer(
SolidColorRenderElement::from_buffer(
&self.pointer_buffer, &self.pointer_buffer,
pointer_pos.to_physical_precise_round(1.), pointer_pos.to_physical_precise_round(1.),
1., 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); backend.render(self, output, &elements);
self.monitor_set // Send frame callbacks.
.send_frame(output, self.start_time.elapsed()); 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! { render_elements! {
#[derive(Debug)] #[derive(Debug)]
pub OutputRenderElements<R> where R: ImportAll; pub OutputRenderElements<R> where R: ImportAll;
Wayland = MonitorRenderElement<R>, Monitor = MonitorRenderElement<R>,
Wayland = WaylandSurfaceRenderElement<R>,
Pointer = SolidColorRenderElement, Pointer = SolidColorRenderElement,
} }