aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2024-07-06 18:20:19 +0400
committerIvan Molodetskikh <yalterz@gmail.com>2024-07-06 18:20:19 +0400
commite394a7ff2006985b27903bdf694573543f0f6268 (patch)
treebe8c2bf4e707cb8a93d4935fe58788ae77ce199c /src
parent921ed632041e3f6e3e3a6be26fcafd301c845004 (diff)
downloadniri-e394a7ff2006985b27903bdf694573543f0f6268.tar.gz
niri-e394a7ff2006985b27903bdf694573543f0f6268.tar.bz2
niri-e394a7ff2006985b27903bdf694573543f0f6268.zip
Implement on-demand layer-shell keyboard focus
Diffstat (limited to 'src')
-rw-r--r--src/handlers/mod.rs2
-rw-r--r--src/handlers/xdg_shell.rs6
-rw-r--r--src/input/mod.rs30
-rw-r--r--src/niri.rs69
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());
}
}
}