From f2ef8de74fe3dbd957b8e6810a008ff4eda7ea82 Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Thu, 24 Sep 2026 16:57:13 +0100 Subject: [PATCH] feat: route input to layer shell surfaces --- compositor/src/handlers/layer_shell.rs | 38 +++++++++++++++- compositor/src/input.rs | 10 +++- compositor/src/state.rs | 63 +++++++++++++++++++++++++- 3 files changed, 107 insertions(+), 4 deletions(-) diff --git a/compositor/src/handlers/layer_shell.rs b/compositor/src/handlers/layer_shell.rs index cfd5f22..355b22c 100644 --- a/compositor/src/handlers/layer_shell.rs +++ b/compositor/src/handlers/layer_shell.rs @@ -5,7 +5,9 @@ use smithay::{ output::Output, reexports::wayland_server::protocol::{wl_output::WlOutput, wl_surface::WlSurface}, wayland::shell::{ - wlr_layer::{Layer, LayerSurface, WlrLayerShellHandler, WlrLayerShellState}, + wlr_layer::{ + KeyboardInteractivity, Layer, LayerSurface, WlrLayerShellHandler, WlrLayerShellState, + }, xdg::PopupSurface, }, }; @@ -57,6 +59,7 @@ impl WlrLayerShellHandler for Ferese { else { return; }; + let restore_focus = layer_has_keyboard_focus(self, &layer); let outputs = self.space.outputs().cloned().collect::>(); for output in outputs { @@ -64,6 +67,9 @@ impl WlrLayerShellHandler for Ferese { } self.relayout(); + if restore_focus { + self.restore_keyboard_focus(); + } crate::backends::direct::render_all(self); } } @@ -86,5 +92,35 @@ pub fn handle_commit(state: &mut Ferese, surface: &WlSurface) { } } + let policy = layer.cached_state(); + if policy.keyboard_interactivity == KeyboardInteractivity::Exclusive + && matches!(policy.layer, Layer::Top | Layer::Overlay) + { + state + .seat + .get_keyboard() + .expect("seat has a keyboard") + .set_focus( + state, + Some(layer.wl_surface().clone()), + smithay::utils::SERIAL_COUNTER.next_serial(), + ); + } else if !layer.can_receive_keyboard_focus() && layer_has_keyboard_focus(state, &layer) { + state.restore_keyboard_focus(); + } + state.relayout(); } + +fn layer_has_keyboard_focus(state: &Ferese, layer: &DesktopLayerSurface) -> bool { + state + .seat + .get_keyboard() + .and_then(|keyboard| keyboard.current_focus()) + .and_then(|surface| { + state + .space + .layer_for_surface(&surface, WindowSurfaceType::ALL) + }) + .is_some_and(|focused| focused == *layer) +} diff --git a/compositor/src/input.rs b/compositor/src/input.rs index 9a5129d..09c2505 100644 --- a/compositor/src/input.rs +++ b/compositor/src/input.rs @@ -247,8 +247,16 @@ impl Ferese { fn focus_window_under_pointer(&mut self, serial: Serial) { let pointer = self.seat.get_pointer().expect("seat has a pointer"); let keyboard = self.seat.get_keyboard().expect("seat has a keyboard"); + let position = pointer.current_location(); - if let Some(window) = self.window_under_visual(pointer.current_location()) { + if let Some((layer, _, _)) = self.layer_under(position) { + if layer.can_receive_keyboard_focus() { + keyboard.set_focus(self, Some(layer.wl_surface().clone()), serial); + } + return; + } + + if let Some(window) = self.window_under_visual(position) { self.focused_window = self.window_ids.get(&window).copied(); if let Some(focused) = self.focused_window && let Err(error) = self.workspaces.focus_window(focused) diff --git a/compositor/src/state.rs b/compositor/src/state.rs index 6e547d5..bf57226 100644 --- a/compositor/src/state.rs +++ b/compositor/src/state.rs @@ -12,7 +12,10 @@ use ferese_layout::{Axis, Direction, GapConfig, LayoutResult, Rect, SizeConstrai use smithay::{ backend::drm::DrmEventTime, - desktop::{PopupManager, Space, Window, WindowSurfaceType, utils::send_frames_surface_tree}, + desktop::{ + LayerSurface, PopupManager, Space, Window, WindowSurfaceType, layer_map_for_output, + utils::send_frames_surface_tree, + }, input::{Seat, SeatState, pointer::CursorImageStatus}, output::Output, reexports::{ @@ -33,6 +36,7 @@ use smithay::{ presentation::PresentationState, relative_pointer::RelativePointerManagerState, selection::{data_device::DataDeviceState, primary_selection::PrimarySelectionState}, + shell::wlr_layer::Layer, shell::wlr_layer::WlrLayerShellState, shell::xdg::decoration::XdgDecorationState, shell::xdg::{SurfaceCachedState, XdgShellState}, @@ -171,6 +175,33 @@ impl Ferese { pub fn surface_under( &self, position: Point, + ) -> Option<(WlSurface, Point)> { + self.layer_surface_under(position, &[Layer::Overlay, Layer::Top]) + .map(|(_, surface, origin)| (surface, origin)) + .or_else(|| self.window_surface_under(position)) + .or_else(|| { + self.layer_surface_under(position, &[Layer::Bottom, Layer::Background]) + .map(|(_, surface, origin)| (surface, origin)) + }) + } + + pub fn layer_under( + &self, + position: Point, + ) -> Option<(LayerSurface, WlSurface, Point)> { + if let Some(layer) = self.layer_surface_under(position, &[Layer::Overlay, Layer::Top]) { + return Some(layer); + } + if self.window_surface_under(position).is_some() { + return None; + } + + self.layer_surface_under(position, &[Layer::Bottom, Layer::Background]) + } + + fn window_surface_under( + &self, + position: Point, ) -> Option<(WlSurface, Point)> { self.space.elements().rev().find_map(|window| { let id = self.window_ids.get(window)?; @@ -196,6 +227,34 @@ impl Ferese { }) } + fn layer_surface_under( + &self, + position: Point, + layers: &[Layer], + ) -> Option<(LayerSurface, WlSurface, Point)> { + let output = self.space.output_under(position).next()?; + let output_geometry = self.space.output_geometry(output)?; + let output_position = position - output_geometry.loc.to_f64(); + let map = layer_map_for_output(output); + + for requested in layers { + for layer in map.layers_on(*requested).rev() { + let geometry = map.layer_geometry(layer)?; + let layer_position = output_position - geometry.loc.to_f64(); + let Some((surface, surface_location)) = + layer.surface_under(layer_position, WindowSurfaceType::ALL) + else { + continue; + }; + let origin = output_geometry.loc + geometry.loc + surface_location; + + return Some((layer.clone(), surface, origin.to_f64())); + } + } + + None + } + pub fn send_cursor_frame(&self, output: &Output) { let CursorImageStatus::Surface(surface) = &self.cursor_status else { return; @@ -694,7 +753,7 @@ impl Ferese { self.restore_keyboard_focus(); } - fn restore_keyboard_focus(&mut self) { + pub(crate) fn restore_keyboard_focus(&mut self) { let surface = self.focused_window.and_then(|focused| { self.window_ids.iter().find_map(|(window, id)| { (*id == focused)