From 71e4c1f1593b353e36989460ac172f439f1da4d9 Mon Sep 17 00:00:00 2001 From: Ottatop Date: Wed, 19 Jun 2024 19:23:50 -0500 Subject: [PATCH] Improve keyboard focus changing Fixes #248, fixes #250 --- src/focus.rs | 46 ++++++++++--------- src/handlers.rs | 11 ++++- src/input.rs | 115 ++++++++++++++---------------------------------- 3 files changed, 67 insertions(+), 105 deletions(-) diff --git a/src/focus.rs b/src/focus.rs index 4edb3dc..12f07ce 100644 --- a/src/focus.rs +++ b/src/focus.rs @@ -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) = ¤t_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) = ¤t_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(), + ); } } diff --git a/src/handlers.rs b/src/handlers.rs index 3992c06..3ff6acd 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -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); diff --git a/src/input.rs b/src/input.rs index f8a5cbd..497937a 100644 --- a/src/input.rs +++ b/src/input.rs @@ -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, - /// 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, - locked_pointer_position_hint: Option>, // 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::() @@ -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); } };