Improve keyboard focus changing
Some checks failed
CI (Pinnacle) / Build (push) Has been cancelled
CI (Pinnacle) / Run tests (push) Has been cancelled
CI (Pinnacle) / Check formatting (push) Has been cancelled
CI (Pinnacle) / Clippy check (push) Has been cancelled

Fixes #248, fixes #250
This commit is contained in:
Ottatop 2024-06-19 19:23:50 -05:00
parent 0a6984f713
commit 71e4c1f159
3 changed files with 67 additions and 105 deletions

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
use keyboard::KeyboardFocusTarget;
use smithay::{desktop::space::SpaceElement, output::Output, utils::SERIAL_COUNTER}; use smithay::{desktop::space::SpaceElement, output::Output, utils::SERIAL_COUNTER};
use crate::{ use crate::{
@ -13,33 +14,36 @@ pub mod pointer;
impl State { impl State {
/// Update the keyboard focus. /// Update the keyboard focus.
pub fn update_keyboard_focus(&mut self, output: &Output) { pub fn update_keyboard_focus(&mut self, output: &Output) {
let current_focus = self.pinnacle.focused_window(output); let Some(keyboard) = self.pinnacle.seat.get_keyboard() else {
return;
};
if let Some(win) = &current_focus { let current_focused_window = self.pinnacle.focused_window(output);
assert!(!win.is_x11_override_redirect());
let wins = output.with_state(|state| state.focus_stack.stack.clone()); let keyboard_focus_is_same = keyboard
.current_focus()
.is_some_and(|foc| {
matches!(foc, KeyboardFocusTarget::Window(win) if Some(&win) == current_focused_window.as_ref())
});
for win in wins.iter() { if keyboard_focus_is_same {
win.set_activate(false); return;
}
if let Some(focused_win) = &current_focused_window {
assert!(!focused_win.is_x11_override_redirect());
for win in self.pinnacle.windows.iter() {
win.set_activate(win == focused_win);
if let Some(toplevel) = win.toplevel() { if let Some(toplevel) = win.toplevel() {
toplevel.send_configure(); toplevel.send_pending_configure();
}
} }
} }
win.set_activate(true); keyboard.set_focus(
if let Some(toplevel) = win.toplevel() {
toplevel.send_configure();
}
}
self.pinnacle
.seat
.get_keyboard()
.expect("no keyboard")
.set_focus(
self, self,
current_focus.map(|win| win.into()), current_focused_window.map(|win| win.into()),
SERIAL_COUNTER.next_serial(), SERIAL_COUNTER.next_serial(),
); );
} }

View file

@ -62,7 +62,10 @@ use smithay::{
SelectionHandler, SelectionSource, SelectionTarget, SelectionHandler, SelectionSource, SelectionTarget,
}, },
shell::{ shell::{
wlr_layer::{self, Layer, LayerSurfaceData, WlrLayerShellHandler, WlrLayerShellState}, wlr_layer::{
self, Layer, LayerSurfaceCachedState, LayerSurfaceData, WlrLayerShellHandler,
WlrLayerShellState,
},
xdg::{PopupSurface, XdgPopupSurfaceData, XdgToplevelSurfaceData}, xdg::{PopupSurface, XdgPopupSurfaceData, XdgToplevelSurfaceData},
}, },
shm::{ShmHandler, ShmState}, shm::{ShmHandler, ShmState},
@ -680,6 +683,12 @@ impl WlrLayerShellHandler for State {
output = Some(op.clone()); output = Some(op.clone());
} }
let focused_output = self.pinnacle.focused_output().cloned();
if let Some(focused_output) = focused_output {
self.update_keyboard_focus(&focused_output);
}
if let Some(output) = output { if let Some(output) = output {
self.pinnacle.loop_handle.insert_idle(move |state| { self.pinnacle.loop_handle.insert_idle(move |state| {
state.pinnacle.request_layout(&output); state.pinnacle.request_layout(&output);

View file

@ -32,7 +32,7 @@ use smithay::{
pointer::{AxisFrame, ButtonEvent, MotionEvent, RelativeMotionEvent}, pointer::{AxisFrame, ButtonEvent, MotionEvent, RelativeMotionEvent},
}, },
reexports::input::{self, Led}, reexports::input::{self, Led},
utils::{IsAlive, Logical, Point, Rectangle, SERIAL_COUNTER}, utils::{Logical, Point, Rectangle, SERIAL_COUNTER},
wayland::{ wayland::{
compositor::{self, RegionAttributes, SurfaceAttributes}, compositor::{self, RegionAttributes, SurfaceAttributes},
pointer_constraints::{with_pointer_constraint, PointerConstraint}, pointer_constraints::{with_pointer_constraint, PointerConstraint},
@ -118,12 +118,6 @@ pub struct InputState {
/// All libinput devices that have been connected /// All libinput devices that have been connected
pub libinput_devices: Vec<input::Device>, pub libinput_devices: Vec<input::Device>,
/// A keyboard focus target stack that is used when there are exclusive keyboard layer
/// surfaces. When used, the first item is the previous focus before there were any
/// exclusive layer surfaces.
// TODO: make that a type or something
exclusive_layer_focus_stack: Vec<KeyboardFocusTarget>,
locked_pointer_position_hint: Option<Point<f64, Logical>>, locked_pointer_position_hint: Option<Point<f64, Logical>>,
// Keys that were used in a keybind and should not be released // Keys that were used in a keybind and should not be released
@ -443,7 +437,8 @@ impl State {
} }
if self.pinnacle.lock_state.is_unlocked() { if self.pinnacle.lock_state.is_unlocked() {
// Handle exclusive layers // Focus the topmost exclusive layer, if any
let mut exclusive_layers_exist = false;
for layer in self.pinnacle.layer_shell_state.layer_surfaces().rev() { for layer in self.pinnacle.layer_shell_state.layer_surfaces().rev() {
let data = compositor::with_states(layer.wl_surface(), |states| { let data = compositor::with_states(layer.wl_surface(), |states| {
*states.cached_state.current::<LayerSurfaceCachedState>() *states.cached_state.current::<LayerSurfaceCachedState>()
@ -456,58 +451,21 @@ impl State {
{ {
let layer_surface = self.pinnacle.space.outputs().find_map(|op| { let layer_surface = self.pinnacle.space.outputs().find_map(|op| {
let map = layer_map_for_output(op); let map = layer_map_for_output(op);
let cloned = map.layers().find(|l| l.layer_surface() == &layer).cloned(); let layer = map.layers().find(|l| l.layer_surface() == &layer).cloned();
cloned layer
}); });
if let Some(layer_surface) = layer_surface { if let Some(layer_surface) = layer_surface {
match self.pinnacle.input_state.exclusive_layer_focus_stack.last() { exclusive_layers_exist = true;
Some(focus) => { keyboard.set_focus(
let layer_focus = KeyboardFocusTarget::LayerSurface(layer_surface); self,
if &layer_focus != focus { Some(KeyboardFocusTarget::LayerSurface(layer_surface)),
self.pinnacle serial,
.input_state );
.exclusive_layer_focus_stack
.push(layer_focus);
}
}
// Push the previous focus on as this is the first exclusive layer surface
// on screen. This lets us restore it when that layer surface goes away.
None => {
self.pinnacle
.input_state
.exclusive_layer_focus_stack
.extend(keyboard.current_focus());
self.pinnacle
.input_state
.exclusive_layer_focus_stack
.push(KeyboardFocusTarget::LayerSurface(layer_surface));
}
}
}
}
}
while let Some(last) = self.pinnacle.input_state.exclusive_layer_focus_stack.pop() {
if last.alive() {
// If it's not empty then there's another exclusive layer surface
// underneath. Otherwise `last` is the previous keyboard focus
// and we don't need the stack anymore.
if !self
.pinnacle
.input_state
.exclusive_layer_focus_stack
.is_empty()
{
self.pinnacle
.input_state
.exclusive_layer_focus_stack
.push(last.clone());
}
keyboard.set_focus(self, Some(last), serial);
break; break;
} }
} }
}
} else { } else {
// We don't want anything but lock surfaces getting keyboard input when locked // We don't want anything but lock surfaces getting keyboard input when locked
let lock_surface = self let lock_surface = self
@ -670,37 +628,28 @@ impl State {
if let Some((focus, _)) = self.pinnacle.pointer_focus_target_under(pointer_loc) { if let Some((focus, _)) = self.pinnacle.pointer_focus_target_under(pointer_loc) {
if let Some(window) = focus.window_for(self) { if let Some(window) = focus.window_for(self) {
self.pinnacle.raise_window(window.clone(), true); self.pinnacle.raise_window(window.clone(), true);
if !window.is_x11_override_redirect() {
if let Some(output) = window.output(&self.pinnacle) { if let Some(output) = window.output(&self.pinnacle) {
output.with_state_mut(|state| state.focus_stack.set_focus(window.clone())); output.with_state_mut(|state| {
state.focus_stack.set_focus(window.clone())
});
self.update_keyboard_focus(&output);
} }
} }
} else if let Some(layer) = focus.layer_for(self) {
if !matches!( if layer.can_receive_keyboard_focus() {
focus.window_for(self), keyboard.set_focus(
Some(window) if window.is_x11_override_redirect() self,
) && focus.popup_for(self).is_none() Some(KeyboardFocusTarget::LayerSurface(layer)),
{ serial,
keyboard.set_focus(self, focus.to_keyboard_focus_target(self), serial); );
}
for window in self.pinnacle.space.elements() {
if let Some(toplevel) = window.toplevel() {
toplevel.send_pending_configure();
} }
} }
} else { } else if let Some(focused_op) = self.pinnacle.focused_output().cloned() {
if let Some(focused_op) = self.pinnacle.focused_output() {
focused_op.with_state_mut(|state| { focused_op.with_state_mut(|state| {
state.focus_stack.unset_focus(); state.focus_stack.unset_focus();
for window in state.focus_stack.stack.iter() {
window.set_activate(false);
if let Some(toplevel) = window.toplevel() {
toplevel.send_pending_configure();
}
}
}); });
} self.update_keyboard_focus(&focused_op);
keyboard.set_focus(self, None, serial);
} }
}; };