add pointer location hint (#1551)
Some checks failed
Continuous Integration / format (push) Has been cancelled
Continuous Integration / clippy-check (push) Has been cancelled
Continuous Integration / smithay-check-features (push) Has been cancelled
Continuous Integration / check-msrv (push) Has been cancelled
Continuous Integration / check-minimal (push) Has been cancelled
Continuous Integration / smithay-tests (push) Has been cancelled
Continuous Integration / smallvil-check (push) Has been cancelled
Continuous Integration / anvil-check-features (push) Has been cancelled
Continuous Integration / WLCS: Bad Buffer Test (push) Has been cancelled
Continuous Integration / WLCS: Core tests (push) Has been cancelled
Continuous Integration / WLCS: Output tests (push) Has been cancelled
Continuous Integration / WLCS: Pointer input tests (push) Has been cancelled
Continuous Integration / Documentation on Github Pages (push) Has been cancelled

* add pointer location hint

* describe why `PointerConstraint::commit()` returns a point

* implement pointer location hint in anvil

* set_location_hint -> set_location

* add note that you should frequently be using `PointerHandle::motion`
This commit is contained in:
sodiboo 2024-09-27 19:27:16 +02:00 committed by GitHub
parent 3b0ecce6c1
commit debffed7ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 84 additions and 9 deletions

View file

@ -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>);

View file

@ -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.

View file

@ -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
@ -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
})
}
}
}
@ -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.
@ -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>,