mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-23 02:05:33 +07:00
Implement on-demand layer-shell keyboard focus
This commit is contained in:
@@ -372,6 +372,7 @@ impl ForeignToplevelHandler for State {
|
|||||||
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
|
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
|
||||||
let window = mapped.window.clone();
|
let window = mapped.window.clone();
|
||||||
self.niri.layout.activate_window(&window);
|
self.niri.layout.activate_window(&window);
|
||||||
|
self.niri.layer_shell_on_demand_focus = None;
|
||||||
self.niri.queue_redraw_all();
|
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) {
|
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&surface) {
|
||||||
let window = mapped.window.clone();
|
let window = mapped.window.clone();
|
||||||
self.niri.layout.activate_window(&window);
|
self.niri.layout.activate_window(&window);
|
||||||
|
self.niri.layer_shell_on_demand_focus = None;
|
||||||
self.niri.queue_redraw_all();
|
self.niri.queue_redraw_all();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,11 +110,13 @@ impl XdgShellHandler for State {
|
|||||||
if intersection.intersects(ResizeEdge::LEFT_RIGHT) {
|
if intersection.intersects(ResizeEdge::LEFT_RIGHT) {
|
||||||
// FIXME: don't activate once we can pass specific windows to actions.
|
// FIXME: don't activate once we can pass specific windows to actions.
|
||||||
self.niri.layout.activate_window(&window);
|
self.niri.layout.activate_window(&window);
|
||||||
|
self.niri.layer_shell_on_demand_focus = None;
|
||||||
self.niri.layout.toggle_full_width();
|
self.niri.layout.toggle_full_width();
|
||||||
}
|
}
|
||||||
if intersection.intersects(ResizeEdge::TOP_BOTTOM) {
|
if intersection.intersects(ResizeEdge::TOP_BOTTOM) {
|
||||||
// FIXME: don't activate once we can pass specific windows to actions.
|
// FIXME: don't activate once we can pass specific windows to actions.
|
||||||
self.niri.layout.activate_window(&window);
|
self.niri.layout.activate_window(&window);
|
||||||
|
self.niri.layer_shell_on_demand_focus = None;
|
||||||
self.niri.layout.reset_window_height();
|
self.niri.layout.reset_window_height();
|
||||||
}
|
}
|
||||||
// FIXME: granular.
|
// FIXME: granular.
|
||||||
@@ -184,10 +186,13 @@ impl XdgShellHandler for State {
|
|||||||
let _ = PopupManager::dismiss_popup(&root, &popup);
|
let _ = PopupManager::dismiss_popup(&root, &popup);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: popup grabs for on-demand bottom and background layers.
|
||||||
} else {
|
} else {
|
||||||
if layers.layers_on(Layer::Overlay).any(|l| {
|
if layers.layers_on(Layer::Overlay).any(|l| {
|
||||||
l.cached_state().keyboard_interactivity
|
l.cached_state().keyboard_interactivity
|
||||||
== wlr_layer::KeyboardInteractivity::Exclusive
|
== wlr_layer::KeyboardInteractivity::Exclusive
|
||||||
|
|| Some(l) == self.niri.layer_shell_on_demand_focus.as_ref()
|
||||||
}) {
|
}) {
|
||||||
let _ = PopupManager::dismiss_popup(&root, &popup);
|
let _ = PopupManager::dismiss_popup(&root, &popup);
|
||||||
return;
|
return;
|
||||||
@@ -198,6 +203,7 @@ impl XdgShellHandler for State {
|
|||||||
&& layers.layers_on(Layer::Top).any(|l| {
|
&& layers.layers_on(Layer::Top).any(|l| {
|
||||||
l.cached_state().keyboard_interactivity
|
l.cached_state().keyboard_interactivity
|
||||||
== wlr_layer::KeyboardInteractivity::Exclusive
|
== wlr_layer::KeyboardInteractivity::Exclusive
|
||||||
|
|| Some(l) == self.niri.layer_shell_on_demand_focus.as_ref()
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
let _ = PopupManager::dismiss_popup(&root, &popup);
|
let _ = PopupManager::dismiss_popup(&root, &popup);
|
||||||
|
|||||||
+15
-15
@@ -1404,6 +1404,11 @@ impl State {
|
|||||||
|
|
||||||
self.update_pointer_focus();
|
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() {
|
if let Some(button) = event.button() {
|
||||||
let pos = pointer.current_location();
|
let pos = pointer.current_location();
|
||||||
if let Some((output, _)) = self.niri.output_under(pos) {
|
if let Some((output, _)) = self.niri.output_under(pos) {
|
||||||
@@ -1685,19 +1690,19 @@ impl State {
|
|||||||
tool.tip_down(serial, event.time_msec());
|
tool.tip_down(serial, event.time_msec());
|
||||||
|
|
||||||
if let Some(pos) = self.niri.tablet_cursor_location {
|
if let Some(pos) = self.niri.tablet_cursor_location {
|
||||||
if let Some(mapped) = self.niri.window_under(pos) {
|
let under = self.niri.surface_under_and_global_space(pos);
|
||||||
let window = mapped.window.clone();
|
if let Some(window) = under.window {
|
||||||
self.niri.layout.activate_window(&window);
|
self.niri.layout.activate_window(&window);
|
||||||
|
|
||||||
// FIXME: granular.
|
// FIXME: granular.
|
||||||
self.niri.queue_redraw_all();
|
self.niri.queue_redraw_all();
|
||||||
} else if let Some((output, _)) = self.niri.output_under(pos) {
|
} else if let Some(output) = under.output {
|
||||||
let output = output.clone();
|
|
||||||
self.niri.layout.activate_output(&output);
|
self.niri.layout.activate_output(&output);
|
||||||
|
|
||||||
// FIXME: granular.
|
// FIXME: granular.
|
||||||
self.niri.queue_redraw_all();
|
self.niri.queue_redraw_all();
|
||||||
}
|
}
|
||||||
|
self.niri.focus_layer_surface_if_on_demand(under.layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TabletToolTipState::Up => {
|
TabletToolTipState::Up => {
|
||||||
@@ -2042,29 +2047,24 @@ impl State {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let under = self.niri.surface_under_and_global_space(touch_location);
|
||||||
|
|
||||||
if !handle.is_grabbed() {
|
if !handle.is_grabbed() {
|
||||||
let output_under_touch = self
|
if let Some(window) = under.window {
|
||||||
.niri
|
|
||||||
.global_space
|
|
||||||
.output_under(touch_location)
|
|
||||||
.next()
|
|
||||||
.cloned();
|
|
||||||
if let Some(mapped) = self.niri.window_under(touch_location) {
|
|
||||||
let window = mapped.window.clone();
|
|
||||||
self.niri.layout.activate_window(&window);
|
self.niri.layout.activate_window(&window);
|
||||||
|
|
||||||
// FIXME: granular.
|
// FIXME: granular.
|
||||||
self.niri.queue_redraw_all();
|
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);
|
self.niri.layout.activate_output(&output);
|
||||||
|
|
||||||
// FIXME: granular.
|
// FIXME: granular.
|
||||||
self.niri.queue_redraw_all();
|
self.niri.queue_redraw_all();
|
||||||
};
|
}
|
||||||
|
self.niri.focus_layer_surface_if_on_demand(under.layer);
|
||||||
};
|
};
|
||||||
|
|
||||||
let serial = SERIAL_COUNTER.next_serial();
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
let under = self.niri.surface_under_and_global_space(touch_location);
|
|
||||||
handle.down(
|
handle.down(
|
||||||
self,
|
self,
|
||||||
under.surface,
|
under.surface,
|
||||||
|
|||||||
+64
-5
@@ -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::protocol::wl_surface::WlSurface;
|
||||||
use smithay::reexports::wayland_server::{Display, DisplayHandle, Resource};
|
use smithay::reexports::wayland_server::{Display, DisplayHandle, Resource};
|
||||||
use smithay::utils::{
|
use smithay::utils::{
|
||||||
ClockSource, Logical, Monotonic, Physical, Point, Rectangle, Scale, Size, Transform,
|
ClockSource, IsAlive as _, Logical, Monotonic, Physical, Point, Rectangle, Scale, Size,
|
||||||
SERIAL_COUNTER,
|
Transform, SERIAL_COUNTER,
|
||||||
};
|
};
|
||||||
use smithay::wayland::compositor::{
|
use smithay::wayland::compositor::{
|
||||||
with_states, with_surface_tree_downward, CompositorClientState, CompositorState, SurfaceData,
|
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_cooldown_timers: HashMap<Key, RegistrationToken>,
|
||||||
pub bind_repeat_timer: Option<RegistrationToken>,
|
pub bind_repeat_timer: Option<RegistrationToken>,
|
||||||
pub keyboard_focus: KeyboardFocus,
|
pub keyboard_focus: KeyboardFocus,
|
||||||
|
pub layer_shell_on_demand_focus: Option<LayerSurface>,
|
||||||
pub idle_inhibiting_surfaces: HashSet<WlSurface>,
|
pub idle_inhibiting_surfaces: HashSet<WlSurface>,
|
||||||
pub is_fdo_idle_inhibited: Arc<AtomicBool>,
|
pub is_fdo_idle_inhibited: Arc<AtomicBool>,
|
||||||
|
|
||||||
@@ -716,6 +717,18 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_keyboard_focus(&mut self) {
|
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() {
|
let focus = if self.niri.is_locked() {
|
||||||
KeyboardFocus::LockScreen {
|
KeyboardFocus::LockScreen {
|
||||||
surface: self.niri.lock_surface_focus(),
|
surface: self.niri.lock_surface_focus(),
|
||||||
@@ -749,10 +762,19 @@ impl State {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
let layer_focus = |surface: &LayerSurface| {
|
let layer_focus = |surface: &LayerSurface| {
|
||||||
// FIXME: support on-demand.
|
let can_receive_exclusive_focus = surface.cached_state().keyboard_interactivity
|
||||||
let can_receive_keyboard_focus = surface.cached_state().keyboard_interactivity
|
|
||||||
== wlr_layer::KeyboardInteractivity::Exclusive;
|
== 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())
|
.then(|| surface.wl_surface().clone())
|
||||||
.map(|surface| KeyboardFocus::LayerShell { surface })
|
.map(|surface| KeyboardFocus::LayerShell { surface })
|
||||||
};
|
};
|
||||||
@@ -773,6 +795,10 @@ impl State {
|
|||||||
surface = surface.or_else(layout_focus);
|
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 })
|
surface.unwrap_or(KeyboardFocus::Layout { surface: None })
|
||||||
} else {
|
} else {
|
||||||
KeyboardFocus::Layout { surface: None }
|
KeyboardFocus::Layout { surface: None }
|
||||||
@@ -1668,6 +1694,7 @@ impl Niri {
|
|||||||
|
|
||||||
seat,
|
seat,
|
||||||
keyboard_focus: KeyboardFocus::Layout { surface: None },
|
keyboard_focus: KeyboardFocus::Layout { surface: None },
|
||||||
|
layer_shell_on_demand_focus: None,
|
||||||
idle_inhibiting_surfaces: HashSet::new(),
|
idle_inhibiting_surfaces: HashSet::new(),
|
||||||
is_fdo_idle_inhibited: Arc::new(AtomicBool::new(false)),
|
is_fdo_idle_inhibited: Arc::new(AtomicBool::new(false)),
|
||||||
cursor_manager,
|
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")]
|
#[cfg(feature = "dbus")]
|
||||||
pub fn on_ipc_outputs_changed(&self) {
|
pub fn on_ipc_outputs_changed(&self) {
|
||||||
let _span = tracy_client::span!("Niri::on_ipc_outputs_changed");
|
let _span = tracy_client::span!("Niri::on_ipc_outputs_changed");
|
||||||
@@ -4173,6 +4225,13 @@ impl Niri {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.layout.activate_window(window);
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user