fix: handle pending enter events before clicks

An unwrap was hit when a pointer event with no CurrentSurface was
processed. To resolve this, the same logic from the Event::Motion case
has been abstracted to a `handle_pending enter` lambda to provide a
valid CurrentSurface or return early if none could be entered.

Though the effect of `handle_pending_enter` remains, its flow has been
refactored, both to move the `None` case code outside the lambda and
to remove the unnecessary recursion. I would have used a `while let`
loop instead of the `loop let else` construct, but the borrow checker
did some mental gymnastics and rejected it.
This commit is contained in:
En-En 2025-10-23 12:29:15 +00:00 committed by Supreeeme
parent 53b6072bd9
commit 7f27257b51

View file

@ -460,6 +460,40 @@ impl Event for client::wl_pointer::Event {
// Niri). Other compositors do not run into this problem because they appear to not send
// wl_pointer.enter until the user actually moves the mouse in the popup.
let handle_pending_enter = |target: Entity, state: &mut ServerState<C>, event_str: &str| loop {
let Ok(pe) = state.world.get::<&PendingEnter>(target) else {
return true;
};
let PendingEnter(client::wl_pointer::Event::Enter {
serial,
surface,
surface_x,
surface_y,
}) = pe.deref()
else {
unreachable!();
};
if surface
.data()
.copied()
.is_some_and(|key| state.world.contains(key))
{
trace!("resending enter ({serial}) before {}", event_str);
let enter_event = client::wl_pointer::Event::Enter {
serial: *serial,
surface: surface.clone(),
surface_x: *surface_x,
surface_y: *surface_y,
};
drop(pe);
Self::handle(enter_event, target, state);
} else {
warn!("could not move pointer to surface: stale surface");
return false;
}
};
match self {
Self::Enter {
serial,
@ -551,40 +585,9 @@ impl Event for client::wl_pointer::Event {
surface_x,
surface_y,
} => {
let pending_enter = state.world.get::<&PendingEnter>(target).ok();
match pending_enter.as_deref() {
Some(p) => {
let PendingEnter(client::wl_pointer::Event::Enter {
serial,
surface,
surface_x,
surface_y,
}) = p
else {
unreachable!();
};
if surface
.data()
.copied()
.is_some_and(|key| state.world.contains(key))
{
trace!("resending enter ({serial}) before motion");
let enter_event = client::wl_pointer::Event::Enter {
serial: *serial,
surface: surface.clone(),
surface_x: *surface_x,
surface_y: *surface_y,
};
drop(pending_enter);
Self::handle(enter_event, target, state);
Self::handle(self, target, state);
} else {
warn!("could not move pointer to surface ({serial}): stale surface");
if !handle_pending_enter(target, state, "motion") {
return;
}
}
None => {
drop(pending_enter);
let (server, scale) = state
.world
.query_one_mut::<(&WlPointer, &SurfaceScaleFactor)>(target)
@ -597,14 +600,15 @@ impl Event for client::wl_pointer::Event {
);
server.motion(time, surface_x * scale.0, surface_y * scale.0);
}
}
}
client::wl_pointer::Event::Button {
serial,
time,
button,
state: button_state,
} => {
if !handle_pending_enter(target, state, "click") {
return;
}
let mut cmd = CommandBuffer::new();
let (server, seat, CurrentSurface(surface)) = state
.world