diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-09-10 10:14:34 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-09-10 10:14:34 +0300 |
| commit | 7d5785e96f3deaee1bb279ee16b3b12f05344951 (patch) | |
| tree | 6fd7b975b8fd6a3174f17cd656f35892faf0bdf5 /src | |
| parent | 70fa38fadfe3ad5305ac450d801b425f4262a7d4 (diff) | |
| download | niri-7d5785e96f3deaee1bb279ee16b3b12f05344951.tar.gz niri-7d5785e96f3deaee1bb279ee16b3b12f05344951.tar.bz2 niri-7d5785e96f3deaee1bb279ee16b3b12f05344951.zip | |
Give focus to on-demand layer surfaces on map
Diffstat (limited to 'src')
| -rw-r--r-- | src/handlers/layer_shell.rs | 60 | ||||
| -rw-r--r-- | src/niri.rs | 4 |
2 files changed, 56 insertions, 8 deletions
diff --git a/src/handlers/layer_shell.rs b/src/handlers/layer_shell.rs index f1138ea1..ad255c99 100644 --- a/src/handlers/layer_shell.rs +++ b/src/handlers/layer_shell.rs @@ -1,3 +1,4 @@ +use smithay::backend::renderer::utils::with_renderer_surface_state; use smithay::delegate_layer_shell; use smithay::desktop::{layer_map_for_output, LayerSurface, PopupKind, WindowSurfaceType}; use smithay::output::Output; @@ -5,7 +6,7 @@ use smithay::reexports::wayland_server::protocol::wl_output::WlOutput; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; use smithay::wayland::compositor::{get_parent, with_states}; use smithay::wayland::shell::wlr_layer::{ - Layer, LayerSurface as WlrLayerSurface, LayerSurfaceData, WlrLayerShellHandler, + self, Layer, LayerSurface as WlrLayerSurface, LayerSurfaceData, WlrLayerShellHandler, WlrLayerShellState, }; use smithay::wayland::shell::xdg::PopupSurface; @@ -36,12 +37,19 @@ impl WlrLayerShellHandler for State { return; }; + let wl_surface = surface.wl_surface().clone(); + let is_new = self.niri.unmapped_layer_surfaces.insert(wl_surface); + assert!(is_new); + let mut map = layer_map_for_output(&output); map.map_layer(&LayerSurface::new(surface, namespace)) .unwrap(); } fn layer_destroyed(&mut self, surface: WlrLayerSurface) { + let wl_surface = surface.wl_surface(); + self.niri.unmapped_layer_surfaces.remove(wl_surface); + let output = if let Some((output, mut map, layer)) = self.niri.layout.outputs().find_map(|o| { let map = layer_map_for_output(o); @@ -101,15 +109,51 @@ impl State { 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 + // 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(); + let layer = map + .layer_for_surface(surface, WindowSurfaceType::TOPLEVEL) + .unwrap(); + + if initial_configure_sent { + let is_mapped = + with_renderer_surface_state(surface, |state| state.buffer().is_some()) + .unwrap_or_else(|| { + error!("no renderer surface state even though we use commit handler"); + false + }); + + if is_mapped { + let was_unmapped = self.niri.unmapped_layer_surfaces.remove(surface); + + // Give focus to newly mapped on-demand surfaces. Some launchers like + // lxqt-runner rely on this behavior. While this behavior doesn't make much + // sense for other clients like panels, the consensus seems to be that it's not + // a big deal since panels generally only open once at the start of the + // session. + // + // Note that: + // 1) Exclusive layer surfaces already get focus automatically in + // update_keyboard_focus(). + // 2) Same-layer exclusive layer surfaces are already preferred to on-demand + // surfaces in update_keyboard_focus(), so we don't need to check for that + // here. + // + // https://github.com/YaLTeR/niri/issues/641 + let on_demand = layer.cached_state().keyboard_interactivity + == wlr_layer::KeyboardInteractivity::OnDemand; + if was_unmapped && on_demand { + // I guess it'd make sense to check that no higher-layer on-demand surface + // has focus, but Smithay's Layer doesn't implement Ord so this would be a + // little annoying. + self.niri.layer_shell_on_demand_focus = Some(layer.clone()); + } + } else { + self.niri.unmapped_layer_surfaces.insert(surface.clone()); + } + } else { let scale = output.current_scale(); let transform = output.current_transform(); with_states(surface, |data| { diff --git a/src/niri.rs b/src/niri.rs index cd1e3979..0f43333b 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -186,6 +186,9 @@ pub struct Niri { // Windows which don't have a buffer attached yet. pub unmapped_windows: HashMap<WlSurface, Unmapped>, + /// Layer surfaces which don't have a buffer attached yet. + pub unmapped_layer_surfaces: HashSet<WlSurface>, + // Cached root surface for every surface, so that we can access it in destroyed() where the // normal get_parent() is cleared out. pub root_surface: HashMap<WlSurface, WlSurface>, @@ -1788,6 +1791,7 @@ impl Niri { global_space: Space::default(), output_state: HashMap::new(), unmapped_windows: HashMap::new(), + unmapped_layer_surfaces: HashSet::new(), root_surface: HashMap::new(), dmabuf_pre_commit_hook: HashMap::new(), blocker_cleared_tx, |
