diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-07-06 18:20:19 +0400 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-07-06 18:20:19 +0400 |
| commit | e394a7ff2006985b27903bdf694573543f0f6268 (patch) | |
| tree | be8c2bf4e707cb8a93d4935fe58788ae77ce199c | |
| parent | 921ed632041e3f6e3e3a6be26fcafd301c845004 (diff) | |
| download | niri-e394a7ff2006985b27903bdf694573543f0f6268.tar.gz niri-e394a7ff2006985b27903bdf694573543f0f6268.tar.bz2 niri-e394a7ff2006985b27903bdf694573543f0f6268.zip | |
Implement on-demand layer-shell keyboard focus
| -rw-r--r-- | src/handlers/mod.rs | 2 | ||||
| -rw-r--r-- | src/handlers/xdg_shell.rs | 6 | ||||
| -rw-r--r-- | src/input/mod.rs | 30 | ||||
| -rw-r--r-- | src/niri.rs | 69 |
4 files changed, 87 insertions, 20 deletions
diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 5c502a1f..ce2896b2 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -372,6 +372,7 @@ impl ForeignToplevelHandler for State { if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) { let window = mapped.window.clone(); self.niri.layout.activate_window(&window); + self.niri.layer_shell_on_demand_focus = None; self.niri.queue_redraw_all(); } } @@ -540,6 +541,7 @@ impl XdgActivationHandler for State { if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&surface) { let window = mapped.window.clone(); self.niri.layout.activate_window(&window); + self.niri.layer_shell_on_demand_focus = None; self.niri.queue_redraw_all(); } } diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index f3337786..73eb01e9 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -110,11 +110,13 @@ impl XdgShellHandler for State { if intersection.intersects(ResizeEdge::LEFT_RIGHT) { // FIXME: don't activate once we can pass specific windows to actions. self.niri.layout.activate_window(&window); + self.niri.layer_shell_on_demand_focus = None; self.niri.layout.toggle_full_width(); } if intersection.intersects(ResizeEdge::TOP_BOTTOM) { // FIXME: don't activate once we can pass specific windows to actions. self.niri.layout.activate_window(&window); + self.niri.layer_shell_on_demand_focus = None; self.niri.layout.reset_window_height(); } // FIXME: granular. @@ -184,10 +186,13 @@ impl XdgShellHandler for State { let _ = PopupManager::dismiss_popup(&root, &popup); return; } + + // FIXME: popup grabs for on-demand bottom and background layers. } else { if layers.layers_on(Layer::Overlay).any(|l| { l.cached_state().keyboard_interactivity == wlr_layer::KeyboardInteractivity::Exclusive + || Some(l) == self.niri.layer_shell_on_demand_focus.as_ref() }) { let _ = PopupManager::dismiss_popup(&root, &popup); return; @@ -198,6 +203,7 @@ impl XdgShellHandler for State { && layers.layers_on(Layer::Top).any(|l| { l.cached_state().keyboard_interactivity == wlr_layer::KeyboardInteractivity::Exclusive + || Some(l) == self.niri.layer_shell_on_demand_focus.as_ref() }) { let _ = PopupManager::dismiss_popup(&root, &popup); diff --git a/src/input/mod.rs b/src/input/mod.rs index 1560d2a8..e74ff100 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1404,6 +1404,11 @@ impl State { self.update_pointer_focus(); + if ButtonState::Pressed == button_state { + let layer_focus = self.niri.pointer_focus.layer.clone(); + self.niri.focus_layer_surface_if_on_demand(layer_focus); + } + if let Some(button) = event.button() { let pos = pointer.current_location(); if let Some((output, _)) = self.niri.output_under(pos) { @@ -1685,19 +1690,19 @@ impl State { tool.tip_down(serial, event.time_msec()); if let Some(pos) = self.niri.tablet_cursor_location { - if let Some(mapped) = self.niri.window_under(pos) { - let window = mapped.window.clone(); + let under = self.niri.surface_under_and_global_space(pos); + if let Some(window) = under.window { self.niri.layout.activate_window(&window); // FIXME: granular. self.niri.queue_redraw_all(); - } else if let Some((output, _)) = self.niri.output_under(pos) { - let output = output.clone(); + } else if let Some(output) = under.output { self.niri.layout.activate_output(&output); // FIXME: granular. self.niri.queue_redraw_all(); } + self.niri.focus_layer_surface_if_on_demand(under.layer); } } TabletToolTipState::Up => { @@ -2042,29 +2047,24 @@ impl State { return; }; + let under = self.niri.surface_under_and_global_space(touch_location); + if !handle.is_grabbed() { - let output_under_touch = self - .niri - .global_space - .output_under(touch_location) - .next() - .cloned(); - if let Some(mapped) = self.niri.window_under(touch_location) { - let window = mapped.window.clone(); + if let Some(window) = under.window { self.niri.layout.activate_window(&window); // FIXME: granular. self.niri.queue_redraw_all(); - } else if let Some(output) = output_under_touch { + } else if let Some(output) = under.output { self.niri.layout.activate_output(&output); // FIXME: granular. self.niri.queue_redraw_all(); - }; + } + self.niri.focus_layer_surface_if_on_demand(under.layer); }; let serial = SERIAL_COUNTER.next_serial(); - let under = self.niri.surface_under_and_global_space(touch_location); handle.down( self, under.surface, diff --git a/src/niri.rs b/src/niri.rs index 03d3662f..124c3558 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -59,8 +59,8 @@ use smithay::reexports::wayland_server::protocol::wl_shm; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; use smithay::reexports::wayland_server::{Display, DisplayHandle, Resource}; use smithay::utils::{ - ClockSource, Logical, Monotonic, Physical, Point, Rectangle, Scale, Size, Transform, - SERIAL_COUNTER, + ClockSource, IsAlive as _, Logical, Monotonic, Physical, Point, Rectangle, Scale, Size, + Transform, SERIAL_COUNTER, }; use smithay::wayland::compositor::{ with_states, with_surface_tree_downward, CompositorClientState, CompositorState, SurfaceData, @@ -236,6 +236,7 @@ pub struct Niri { pub bind_cooldown_timers: HashMap<Key, RegistrationToken>, pub bind_repeat_timer: Option<RegistrationToken>, pub keyboard_focus: KeyboardFocus, + pub layer_shell_on_demand_focus: Option<LayerSurface>, pub idle_inhibiting_surfaces: HashSet<WlSurface>, pub is_fdo_idle_inhibited: Arc<AtomicBool>, @@ -716,6 +717,18 @@ impl State { } pub fn update_keyboard_focus(&mut self) { + // Clean up on-demand layer surface focus if necessary. + if let Some(surface) = &self.niri.layer_shell_on_demand_focus { + // Still alive and has on-demand interactivity. + let good = surface.alive() + && surface.cached_state().keyboard_interactivity + == wlr_layer::KeyboardInteractivity::OnDemand; + if !good { + self.niri.layer_shell_on_demand_focus = None; + } + } + + // Compute the current focus. let focus = if self.niri.is_locked() { KeyboardFocus::LockScreen { surface: self.niri.lock_surface_focus(), @@ -749,10 +762,19 @@ impl State { }) }; let layer_focus = |surface: &LayerSurface| { - // FIXME: support on-demand. - let can_receive_keyboard_focus = surface.cached_state().keyboard_interactivity + let can_receive_exclusive_focus = surface.cached_state().keyboard_interactivity == wlr_layer::KeyboardInteractivity::Exclusive; - can_receive_keyboard_focus + let is_on_demand_surface = + Some(surface) == self.niri.layer_shell_on_demand_focus.as_ref(); + + (can_receive_exclusive_focus || is_on_demand_surface) + .then(|| surface.wl_surface().clone()) + .map(|surface| KeyboardFocus::LayerShell { surface }) + }; + let on_d_focus = |surface: &LayerSurface| { + let is_on_demand_surface = + Some(surface) == self.niri.layer_shell_on_demand_focus.as_ref(); + is_on_demand_surface .then(|| surface.wl_surface().clone()) .map(|surface| KeyboardFocus::LayerShell { surface }) }; @@ -773,6 +795,10 @@ impl State { surface = surface.or_else(layout_focus); } + // Bottom and background layers can receive on-demand focus only. + surface = surface.or_else(|| layers.layers_on(Layer::Bottom).find_map(on_d_focus)); + surface = surface.or_else(|| layers.layers_on(Layer::Background).find_map(on_d_focus)); + surface.unwrap_or(KeyboardFocus::Layout { surface: None }) } else { KeyboardFocus::Layout { surface: None } @@ -1668,6 +1694,7 @@ impl Niri { seat, keyboard_focus: KeyboardFocus::Layout { surface: None }, + layer_shell_on_demand_focus: None, idle_inhibiting_surfaces: HashSet::new(), is_fdo_idle_inhibited: Arc::new(AtomicBool::new(false)), cursor_manager, @@ -4107,6 +4134,31 @@ impl Niri { }); } + pub fn focus_layer_surface_if_on_demand(&mut self, surface: Option<LayerSurface>) { + if let Some(surface) = surface { + if surface.cached_state().keyboard_interactivity + == wlr_layer::KeyboardInteractivity::OnDemand + { + if self.layer_shell_on_demand_focus.as_ref() != Some(&surface) { + self.layer_shell_on_demand_focus = Some(surface); + + // FIXME: granular. + self.queue_redraw_all(); + } + + return; + } + } + + // Something else got clicked, clear on-demand layer-shell focus. + if self.layer_shell_on_demand_focus.is_some() { + self.layer_shell_on_demand_focus = None; + + // FIXME: granular. + self.queue_redraw_all(); + } + } + #[cfg(feature = "dbus")] pub fn on_ipc_outputs_changed(&self) { let _span = tracy_client::span!("Niri::on_ipc_outputs_changed"); @@ -4173,6 +4225,13 @@ impl Niri { } self.layout.activate_window(window); + self.layer_shell_on_demand_focus = None; + } + } + + if let Some(layer) = &new_focus.layer { + if current_focus.layer.as_ref() != Some(layer) { + self.layer_shell_on_demand_focus = Some(layer.clone()); } } } |
