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>); 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 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. /// 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. /// 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. /// Use [`with_pointer_constraint`] to access the constraint.
fn new_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle<Self>); 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 /// 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 { match self {
Self::Confined(confined) => { Self::Confined(confined) => {
confined.region.clone_from(&confined.pending_region); confined.region.clone_from(&confined.pending_region);
None
} }
Self::Locked(locked) => { Self::Locked(locked) => {
locked.region.clone_from(&locked.pending_region); 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) { fn commit_hook<D: SeatHandler + PointerConstraintsHandler + 'static>(
with_constraint_data::<D, _, _>(surface, |data| { 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(); let data = data.unwrap();
for constraint in data.constraints.values_mut() { data.constraints
constraint.commit(); .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. /// 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 /// 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, pointer_constraints: &ZwpPointerConstraintsV1,
surface: &WlSurface, surface: &WlSurface,
pointer: &PointerHandle<D>, pointer: &PointerHandle<D>,