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
use keyboard::KeyboardFocusTarget;
use smithay::{desktop::space::SpaceElement, output::Output, utils::SERIAL_COUNTER};
use crate::{
@ -13,35 +14,38 @@ pub mod pointer;
impl State {
/// Update the keyboard focus.
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 {
assert!(!win.is_x11_override_redirect());
let current_focused_window = self.pinnacle.focused_window(output);
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() {
win.set_activate(false);
if keyboard_focus_is_same {
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() {
toplevel.send_configure();
toplevel.send_pending_configure();
}
}
win.set_activate(true);
if let Some(toplevel) = win.toplevel() {
toplevel.send_configure();
}
}
self.pinnacle
.seat
.get_keyboard()
.expect("no keyboard")
.set_focus(
self,
current_focus.map(|win| win.into()),
SERIAL_COUNTER.next_serial(),
);
keyboard.set_focus(
self,
current_focused_window.map(|win| win.into()),
SERIAL_COUNTER.next_serial(),
);
}
}

View file

@ -62,7 +62,10 @@ use smithay::{
SelectionHandler, SelectionSource, SelectionTarget,
},
shell::{
wlr_layer::{self, Layer, LayerSurfaceData, WlrLayerShellHandler, WlrLayerShellState},
wlr_layer::{
self, Layer, LayerSurfaceCachedState, LayerSurfaceData, WlrLayerShellHandler,
WlrLayerShellState,
},
xdg::{PopupSurface, XdgPopupSurfaceData, XdgToplevelSurfaceData},
},
shm::{ShmHandler, ShmState},
@ -680,6 +683,12 @@ impl WlrLayerShellHandler for State {
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 {
self.pinnacle.loop_handle.insert_idle(move |state| {
state.pinnacle.request_layout(&output);

View file

@ -32,7 +32,7 @@ use smithay::{
pointer::{AxisFrame, ButtonEvent, MotionEvent, RelativeMotionEvent},
},
reexports::input::{self, Led},
utils::{IsAlive, Logical, Point, Rectangle, SERIAL_COUNTER},
utils::{Logical, Point, Rectangle, SERIAL_COUNTER},
wayland::{
compositor::{self, RegionAttributes, SurfaceAttributes},
pointer_constraints::{with_pointer_constraint, PointerConstraint},
@ -118,12 +118,6 @@ pub struct InputState {
/// All libinput devices that have been connected
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>>,
// 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() {
// 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() {
let data = compositor::with_states(layer.wl_surface(), |states| {
*states.cached_state.current::<LayerSurfaceCachedState>()
@ -456,58 +451,21 @@ impl State {
{
let layer_surface = self.pinnacle.space.outputs().find_map(|op| {
let map = layer_map_for_output(op);
let cloned = map.layers().find(|l| l.layer_surface() == &layer).cloned();
cloned
let layer = map.layers().find(|l| l.layer_surface() == &layer).cloned();
layer
});
if let Some(layer_surface) = layer_surface {
match self.pinnacle.input_state.exclusive_layer_focus_stack.last() {
Some(focus) => {
let layer_focus = KeyboardFocusTarget::LayerSurface(layer_surface);
if &layer_focus != focus {
self.pinnacle
.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));
}
}
exclusive_layers_exist = true;
keyboard.set_focus(
self,
Some(KeyboardFocusTarget::LayerSurface(layer_surface)),
serial,
);
break;
}
}
}
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;
}
}
} else {
// We don't want anything but lock surfaces getting keyboard input when locked
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(window) = focus.window_for(self) {
self.pinnacle.raise_window(window.clone(), true);
if let Some(output) = window.output(&self.pinnacle) {
output.with_state_mut(|state| state.focus_stack.set_focus(window.clone()));
}
}
if !matches!(
focus.window_for(self),
Some(window) if window.is_x11_override_redirect()
) && focus.popup_for(self).is_none()
{
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 {
if let Some(focused_op) = self.pinnacle.focused_output() {
focused_op.with_state_mut(|state| {
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();
}
if !window.is_x11_override_redirect() {
if let Some(output) = window.output(&self.pinnacle) {
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 layer.can_receive_keyboard_focus() {
keyboard.set_focus(
self,
Some(KeyboardFocusTarget::LayerSurface(layer)),
serial,
);
}
}
keyboard.set_focus(self, None, serial);
} else if let Some(focused_op) = self.pinnacle.focused_output().cloned() {
focused_op.with_state_mut(|state| {
state.focus_stack.unset_focus();
});
self.update_keyboard_focus(&focused_op);
}
};