From ca6f05c58534b275cd26db5259abcf09db7cdfcd Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 20 Aug 2024 19:05:51 -0700 Subject: [PATCH] Update pointer focus before motion occurs If the surface under the pointer has changed, we should send an `enter` event immediately, instead of waiting for the next motion event. This seems to fix that, without producing unnecessary events. Instead of `time: 0`, this and other synthesized events should probably use `CLOCK_MONOTONIC`. It seems libinput does document that it uses that. --- src/shell/focus/mod.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index 2ff1799a..eda6d100 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -7,7 +7,7 @@ use crate::{ use indexmap::IndexSet; use smithay::{ desktop::{layer_map_for_output, PopupUngrabStrategy}, - input::Seat, + input::{pointer::MotionEvent, Seat}, output::Output, reexports::wayland_server::Resource, utils::{IsAlive, Serial, SERIAL_COUNTER}, @@ -261,6 +261,8 @@ impl Common { .cloned() .collect::>(); for seat in &seats { + update_pointer_focus(state, &seat); + let mut shell = state.common.shell.write().unwrap(); let output = seat.active_output(); if !shell.outputs().any(|o| o == &output) { @@ -481,6 +483,30 @@ fn update_focus_target( } } +fn update_pointer_focus(state: &mut State, seat: &Seat) { + if let Some(pointer) = seat.get_pointer() { + let output = seat.active_output(); + let position = pointer.current_location().as_global(); + + let mut shell = state.common.shell.write().unwrap(); + let under = State::surface_under(position, &output, &mut shell) + .map(|(target, pos)| (target, pos.as_logical())); + drop(shell); + + if pointer.current_focus().as_ref() != under.as_ref().map(|(target, _)| target) { + pointer.motion( + state, + under, + &MotionEvent { + location: pointer.current_location(), + serial: SERIAL_COUNTER.next_serial(), + time: 0, + }, + ); + } + } +} + // Get the top-most layer, if any, with at least one surface with exclusive keyboard interactivity. // Only considers surface in `Top` or `Overlay` layer. fn exclusive_layer_surface_layer(shell: &Shell) -> Option {