aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/handlers/layer_shell.rs60
-rw-r--r--src/niri.rs4
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,