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:
parent
53b6072bd9
commit
7f27257b51
1 changed files with 50 additions and 46 deletions
|
|
@ -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
|
// 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.
|
// 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 {
|
match self {
|
||||||
Self::Enter {
|
Self::Enter {
|
||||||
serial,
|
serial,
|
||||||
|
|
@ -551,53 +585,20 @@ impl Event for client::wl_pointer::Event {
|
||||||
surface_x,
|
surface_x,
|
||||||
surface_y,
|
surface_y,
|
||||||
} => {
|
} => {
|
||||||
let pending_enter = state.world.get::<&PendingEnter>(target).ok();
|
if !handle_pending_enter(target, state, "motion") {
|
||||||
match pending_enter.as_deref() {
|
return;
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
drop(pending_enter);
|
|
||||||
let (server, scale) = state
|
|
||||||
.world
|
|
||||||
.query_one_mut::<(&WlPointer, &SurfaceScaleFactor)>(target)
|
|
||||||
.unwrap();
|
|
||||||
trace!(
|
|
||||||
target: "pointer_position",
|
|
||||||
"pointer motion {} {}",
|
|
||||||
surface_x * scale.0,
|
|
||||||
surface_y * scale.0
|
|
||||||
);
|
|
||||||
server.motion(time, surface_x * scale.0, surface_y * scale.0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let (server, scale) = state
|
||||||
|
.world
|
||||||
|
.query_one_mut::<(&WlPointer, &SurfaceScaleFactor)>(target)
|
||||||
|
.unwrap();
|
||||||
|
trace!(
|
||||||
|
target: "pointer_position",
|
||||||
|
"pointer motion {} {}",
|
||||||
|
surface_x * scale.0,
|
||||||
|
surface_y * scale.0
|
||||||
|
);
|
||||||
|
server.motion(time, surface_x * scale.0, surface_y * scale.0);
|
||||||
}
|
}
|
||||||
client::wl_pointer::Event::Button {
|
client::wl_pointer::Event::Button {
|
||||||
serial,
|
serial,
|
||||||
|
|
@ -605,6 +606,9 @@ impl Event for client::wl_pointer::Event {
|
||||||
button,
|
button,
|
||||||
state: button_state,
|
state: button_state,
|
||||||
} => {
|
} => {
|
||||||
|
if !handle_pending_enter(target, state, "click") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let mut cmd = CommandBuffer::new();
|
let mut cmd = CommandBuffer::new();
|
||||||
let (server, seat, CurrentSurface(surface)) = state
|
let (server, seat, CurrentSurface(surface)) = state
|
||||||
.world
|
.world
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue