diff --git a/src/input/actions.rs b/src/input/actions.rs index 3d017697d..b1e015aee 100644 --- a/src/input/actions.rs +++ b/src/input/actions.rs @@ -183,24 +183,31 @@ impl State { 0 => 9, x => x - 1, }; - let _ = self.common.shell.write().activate( + let res = self.common.shell.write().activate( ¤t_output, workspace as usize, WorkspaceDelta::new_shortcut(), &mut self.common.workspace_state.update(), ); + if res.is_ok() { + self.reconcile_focus_to_pointer(seat); + } } Action::LastWorkspace => { let current_output = seat.active_output(); let mut shell = self.common.shell.write(); let workspace = shell.workspaces.len(¤t_output).saturating_sub(1); - let _ = shell.activate( + let res = shell.activate( ¤t_output, workspace, WorkspaceDelta::new_shortcut(), &mut self.common.workspace_state.update(), ); + drop(shell); + if res.is_ok() { + self.reconcile_focus_to_pointer(seat); + } } Action::NextWorkspace => { @@ -239,6 +246,8 @@ impl State { direction, true, ) + } else if next.is_ok() { + self.reconcile_focus_to_pointer(seat); } } @@ -278,6 +287,8 @@ impl State { direction, true, ) + } else if previous.is_ok() { + self.reconcile_focus_to_pointer(seat); }; } diff --git a/src/input/mod.rs b/src/input/mod.rs index 39bff9dc0..b4b0f4260 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2073,6 +2073,56 @@ impl State { } } + /// Reconcile keyboard focus to the window under the pointer on the active workspace. + /// + /// Does nothing unless focus-follows-cursor is enabled and the pointer is + /// ungrabbed, and never clears focus when the pointer is over empty space. + pub fn reconcile_focus_to_pointer(&mut self, seat: &Seat) { + if !self.common.config.cosmic_conf.focus_follows_cursor { + return; + } + + let Some(ptr) = seat.get_pointer() else { + return; + }; + if ptr.is_grabbed() { + return; + } + + let position = ptr.current_location().as_global(); + let target = { + let shell = self.common.shell.read(); + let Some(output) = shell + .outputs() + .find(|output| output.geometry().to_f64().contains(position)) + .cloned() + else { + return; + }; + shell + .active_space(&output) + .and_then(|workspace| workspace.toplevel_element_under(position, seat)) + }; + + let Some(target) = target else { + return; + }; + + if let Some(pointer_focus_state) = self.common.pointer_focus_state.take() { + self.common + .event_loop_handle + .remove(pointer_focus_state.token); + } + + Shell::set_focus( + self, + Some(&target), + seat, + Some(SERIAL_COUNTER.next_serial()), + false, + ); + } + #[profiling::function] pub fn element_under( global_pos: Point,