From 9702aae5235af9441b6bd39413a83bf73c4daf68 Mon Sep 17 00:00:00 2001 From: Skygrango Date: Wed, 13 May 2026 20:05:39 +0800 Subject: [PATCH] input: when updating pointer in the confined mode, recalibrate to the valid coordinates --- src/input/mod.rs | 188 +++++++++++++++++++++++++++++------------------ 1 file changed, 117 insertions(+), 71 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 4e1ba33e..747ad172 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -354,27 +354,6 @@ impl State { _ => {} }); } - let original_position = position; - position += event.delta().as_global(); - - let output = shell - .outputs() - .find(|output| output.geometry().to_f64().contains(position)) - .cloned() - .unwrap_or(current_output.clone()); - - let output_geometry = output.geometry(); - position.x = position.x.clamp( - output_geometry.loc.x as f64, - (output_geometry.loc.x + output_geometry.size.w - 1) as f64, - ); - position.y = position.y.clamp( - output_geometry.loc.y as f64, - (output_geometry.loc.y + output_geometry.size.h - 1) as f64, - ); - - let new_under = State::surface_under(position, &output, &shell) - .map(|(target, pos)| (target, pos.as_logical())); std::mem::drop(shell); ptr.relative_motion( @@ -392,6 +371,25 @@ impl State { return; } + let original_position = position; + position += event.delta().as_global(); + let shell = self.common.shell.read(); + let output = shell + .outputs() + .find(|output| output.geometry().to_f64().contains(position)) + .cloned() + .unwrap_or(current_output.clone()); + drop(shell); + let output_geometry = output.geometry(); + position.x = position.x.clamp( + output_geometry.loc.x as f64, + (output_geometry.loc.x + output_geometry.size.w - 1) as f64, + ); + position.y = position.y.clamp( + output_geometry.loc.y as f64, + (output_geometry.loc.y + output_geometry.size.h - 1) as f64, + ); + if ptr.is_grabbed() { if seat .user_data() @@ -499,50 +497,90 @@ impl State { } } - // If confined, don't move pointer if it would go outside surface or region - if pointer_confined && let Some((surface, surface_loc)) = &under { - if new_under.as_ref().and_then(|(under, _)| under.wl_surface()) - != surface.wl_surface() - { - ptr.frame(self); - return; - } - match surface { - PointerFocusTarget::WlSurface { surface, .. } => { - if under_from_surface_tree( - surface, - position.as_logical() - surface_loc.to_f64(), - (0, 0), - WindowSurfaceType::ALL, - ) - .is_none() - { - ptr.frame(self); - return; - } - } - PointerFocusTarget::X11Surface { surface, .. } => { - if surface - .surface_under( + // If confined, help user to update valid coordinates can improve user experience + let shell = self.common.shell.read(); + let new_under = if pointer_confined && let Some((surface, surface_loc)) = &under + { + let is_legal = |pos: Point, shell: &Shell| { + let new_under = State::surface_under(pos, &output, shell) + .map(|(target, pos)| (target, pos.as_logical())); + + //We should only check size, not the surface, so that we can move the mouse over the OSD in confined mode + // if new_under.as_ref().and_then(|(under, _)| under.wl_surface()) + // != surface.wl_surface() + // { + // return (false, None); + // } + + match surface { + PointerFocusTarget::WlSurface { surface, .. } => { + if under_from_surface_tree( + surface, position.as_logical() - surface_loc.to_f64(), (0, 0), WindowSurfaceType::ALL, ) .is_none() - { - ptr.frame(self); - return; + { + return (false, None); + } + } + PointerFocusTarget::X11Surface { surface, .. } => { + if surface + .surface_under( + position.as_logical() - surface_loc.to_f64(), + (0, 0), + WindowSurfaceType::ALL, + ) + .is_none() + { + return (false, None); + } + } + _ => {} + } + + if let Some(region) = &confine_region + && !region + .contains((pos.as_logical() - *surface_loc).to_i32_round()) + { + return (false, None); + } + (true, new_under) + }; + + match is_legal(position, &shell) { + (true, under) => under, + _ => { + let y_only_pos = Point::new(original_position.x, position.y); + let x_only_pos = Point::new(position.x, original_position.y); + match is_legal(y_only_pos, &shell) { + (true, under) => { + position = y_only_pos; + under + } + _ => match is_legal(x_only_pos, &shell) { + (true, under) => { + position = x_only_pos; + under + } + _ => { + position = original_position; + None + } + }, } } - _ => {} - } - if let Some(region) = confine_region - && !region - .contains((position.as_logical() - *surface_loc).to_i32_round()) - { - ptr.frame(self); - return; } + } else { + State::surface_under(position, &output, &shell) + .map(|(target, pos)| (target, pos.as_logical())) + }; + + drop(shell); + if pointer_confined && new_under.is_none() { + ptr.frame(self); + return; } let serial = SERIAL_COUNTER.next_serial(); @@ -557,24 +595,32 @@ impl State { ); ptr.frame(self); - // If pointer is now in a constraint region, activate it + // If pointer is now in a constraint region and window is in focused, activate it if let Some((under, surface_location)) = new_under .and_then(|(target, loc)| Some((target.wl_surface()?.into_owned(), loc))) { - with_pointer_constraint(&under, &ptr, |constraint| match constraint { - Some(constraint) if !constraint.is_active() => { - let region = match &*constraint { - PointerConstraint::Locked(locked) => locked.region(), - PointerConstraint::Confined(confined) => confined.region(), - }; - let point = - (ptr.current_location() - surface_location).to_i32_round(); - if region.is_none_or(|region| region.contains(point)) { - constraint.activate(); + let shell = self.common.shell.read(); + let is_focused = seat + .get_keyboard() + .and_then(|k| k.current_focus()) + .is_some_and(|f| f.has_surface(&shell, &under)); + + if is_focused { + with_pointer_constraint(&under, &ptr, |constraint| match constraint { + Some(constraint) if !constraint.is_active() => { + let region = match &*constraint { + PointerConstraint::Locked(locked) => locked.region(), + PointerConstraint::Confined(confined) => confined.region(), + }; + let point = + (ptr.current_location() - surface_location).to_i32_round(); + if region.is_none_or(|region| region.contains(point)) { + constraint.activate(); + } } - } - _ => {} - }); + _ => {} + }); + } } let mut shell = self.common.shell.write();