Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

add pointer location hint #1551

Merged
merged 5 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions anvil/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,29 @@ impl<BackendData: Backend> PointerConstraintsHandler for AnvilState<BackendData>
});
}
}

fn cursor_position_hint(
&mut self,
surface: &WlSurface,
pointer: &PointerHandle<Self>,
location: Point<f64, Logical>,
) {
if with_pointer_constraint(surface, pointer, |constraint| {
constraint.map_or(false, |c| c.is_active())
}) {
let origin = self
.space
.elements()
.find_map(|window| {
(window.wl_surface().as_deref() == Some(surface)).then(|| window.geometry())
})
.unwrap_or_default()
.loc
.to_f64();

pointer.set_location(origin + location);
}
}
}
delegate_pointer_constraints!(@<BackendData: Backend + 'static> AnvilState<BackendData>);

Expand Down
20 changes: 20 additions & 0 deletions src/input/pointer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,26 @@ impl<D: SeatHandler + 'static> PointerHandle<D> {
self.inner.lock().unwrap().location
}

/// Update the current location of this pointer in the global space,
/// without sending any event and without updating the focus.
///
/// If you want to update the location, and update the focus,
/// and send events, use [Self::motion] instead of this.
///
/// This is useful when the pointer is only moved by relative events,
/// such as when a pointer lock is held by the focused surface.
/// The client can give us a cursor position hint, which corresponds to
/// the actual location the client may be rendering a pointer at.
/// This position hint should be used as the initial location
/// when the pointer lock is deactivated.
///
/// The next time [Self::motion] is called, the focus will be
/// updated accordingly as if this function was never called.
/// Clients will never be notified of a location hint.
pub fn set_location(&self, location: Point<f64, Logical>) {
self.inner.lock().unwrap().location = location;
}

/// Access the [`Serial`] of the last `pointer_enter` event, if that focus is still active.
///
/// In other words this will return `None` again, once a `pointer_leave` event occurred.
Expand Down
50 changes: 41 additions & 9 deletions src/wayland/pointer_constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ pub trait PointerConstraintsHandler: SeatHandler {
///
/// Use [`with_pointer_constraint`] to access the constraint.
fn new_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle<Self>);

/// The client holding a LockedPointer has commited a cursor position hint.
///
/// This is emitted upon a surface commit if the cursor position hint has been updated.
///
/// Use [`with_pointer_constraint`] to access the constraint and check if it is active.
fn cursor_position_hint(
&mut self,
surface: &WlSurface,
pointer: &PointerHandle<Self>,
location: Point<f64, Logical>,
);
}

/// Constraint confining pointer to a region of the surface
Expand Down Expand Up @@ -171,14 +183,19 @@ impl PointerConstraint {
}
}

fn commit(&mut self) {
/// Commits the pending state of the constraint, and returns the cursor position hint if it has changed.
fn commit(&mut self) -> Option<Point<f64, Logical>> {
match self {
Self::Confined(confined) => {
confined.region.clone_from(&confined.pending_region);
None
}
Self::Locked(locked) => {
locked.region.clone_from(&locked.pending_region);
locked.cursor_position_hint = locked.pending_cursor_position_hint;
locked.pending_cursor_position_hint.take().map(|hint| {
locked.cursor_position_hint = Some(hint);
hint
})
}
}
}
Expand Down Expand Up @@ -243,13 +260,28 @@ pub fn with_pointer_constraint<
})
}

fn commit_hook<D: SeatHandler + 'static>(_: &mut D, _dh: &DisplayHandle, surface: &WlSurface) {
with_constraint_data::<D, _, _>(surface, |data| {
fn commit_hook<D: SeatHandler + PointerConstraintsHandler + 'static>(
state: &mut D,
_dh: &DisplayHandle,
surface: &WlSurface,
) {
// `with_constraint_data` locks the pointer constraints,
// so we collect the hints first into a Vec, then release the mutex
// and only once the mutex is released, we call the handler method.
//
// This is to avoid deadlocks when the handler method might try to access the constraints again.
// It's not a hypothetical, it bit me while implementing the position hint functionality.
let position_hints = with_constraint_data::<D, _, _>(surface, |data| {
let data = data.unwrap();
for constraint in data.constraints.values_mut() {
constraint.commit();
}
})
data.constraints
.iter_mut()
.filter_map(|(pointer, constraint)| constraint.commit().map(|hint| (pointer.clone(), hint)))
.collect::<Vec<_>>()
});

for (pointer, hint) in position_hints {
state.cursor_position_hint(surface, &pointer, hint);
}
}

/// Get `PointerConstraintData` associated with a surface, if any.
Expand All @@ -272,7 +304,7 @@ fn with_constraint_data<
}

/// Add constraint for surface, or raise protocol error if one exists
fn add_constraint<D: SeatHandler + 'static>(
fn add_constraint<D: SeatHandler + PointerConstraintsHandler + 'static>(
pointer_constraints: &ZwpPointerConstraintsV1,
surface: &WlSurface,
pointer: &PointerHandle<D>,
Expand Down
Loading