From 8124bc786dcce3640550877c6f4c080c9ba4c08b Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Fri, 20 Mar 2026 16:08:08 +0000 Subject: [PATCH] Add support for `MotionEvent` pointer history This exposes `MotionEvent` pointer history via a `PointerHistoryIter`, got via `Pointer::history()`, that yields `HistoricPointer`'s that give access to sub-sample timestamps and axis values between events. This adds consistent pointer history support for both the `native-activity` and `game-activity` backends. For example, historical pointer samples can be iterated like: ```rust let num_pointers = motion_event.pointer_count(); for i in 0..num_pointers { let pointer = motion_event.pointer_at_index(i); println!( "Pointer[{i}]: id={}, time={}, x={}, y={}", pointer.pointer_id(), motion_event.event_time(), pointer.x(), pointer.y(), ); for sample in pointer.history() { println!( " History[{}]: x={}, y={}, time={:?}", sample.history_index(), sample.x(), sample.y(), sample.event_time() ); } } ``` The documentation clarifies that each pointer will have the same number of historic samples, and the timestamps for corresponding samples will match. The `PointerHistoryIter` supports forwards and/or backwards iteration and can be queried for its `.len()`. Fixes: #207 --- android-activity/CHANGELOG.md | 3 + android-activity/src/game_activity/input.rs | 369 ++++-------------- android-activity/src/input.rs | 122 +++++- android-activity/src/native_activity/input.rs | 192 +++++++-- 4 files changed, 350 insertions(+), 336 deletions(-) diff --git a/android-activity/CHANGELOG.md b/android-activity/CHANGELOG.md index 373ad87..6657758 100644 --- a/android-activity/CHANGELOG.md +++ b/android-activity/CHANGELOG.md @@ -50,6 +50,9 @@ fn android_on_create(state: &OnCreateState) { } ``` +- Support for `MotionEvent` history, providing higher fidelity input data for things like stylus input (`native-activity` + `game-activity` backends). ([#218](https://github.com/rust-mobile/android-activity/pull/218)) + + ### Changed - rust-version bumped to 1.85.0 ([#193](https://github.com/rust-mobile/android-activity/pull/193), [#219](https://github.com/rust-mobile/android-activity/pull/219)) diff --git a/android-activity/src/game_activity/input.rs b/android-activity/src/game_activity/input.rs index 167b782..767f4b2 100644 --- a/android-activity/src/game_activity/input.rs +++ b/android-activity/src/game_activity/input.rs @@ -13,7 +13,9 @@ // The `Class` was also bound differently to `android-ndk-rs` considering how the class is defined // by masking bits from the `Source`. -use crate::activity_impl::ffi::{GameActivityKeyEvent, GameActivityMotionEvent}; +use std::iter::FusedIterator; + +use super::ffi::{self, GameActivityKeyEvent, GameActivityMotionEvent}; use crate::input::{ Axis, Button, ButtonState, EdgeFlags, KeyAction, KeyEventFlags, Keycode, MetaState, MotionAction, MotionEventFlags, Pointer, PointersIter, Source, ToolType, @@ -141,28 +143,6 @@ impl<'a> MotionEvent<'a> { } } - /* - /// Returns the size of the history contained in this event. - /// - /// See [the NDK - /// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_gethistorysize) - #[inline] - pub fn history_size(&self) -> usize { - unsafe { ndk_sys::AMotionEvent_getHistorySize(self.ga_event.ptr.as_ptr()) as usize } - } - - /// An iterator over the historical events contained in this event. - #[inline] - pub fn history(&self) -> HistoricalMotionEventsIter<'_> { - HistoricalMotionEventsIter { - event: self.ga_event.ptr, - next_history_index: 0, - history_size: self.history_size(), - _marker: std::marker::PhantomData, - } - } - */ - /// Returns the state of any modifier keys that were pressed during the event. /// /// See [the NDK @@ -206,7 +186,7 @@ impl<'a> MotionEvent<'a> { /// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_geteventtime) #[inline] pub fn event_time(&self) -> i64 { - self.ga_event.eventTime + self.ga_event.eventTime * 1_000_000 // Convert from milliseconds to nanoseconds } /// The flags associated with a motion event. @@ -301,6 +281,18 @@ impl PointerImpl<'_> { let tool_type = pointer.toolType as u32; tool_type.into() } + + pub fn history(&self) -> crate::input::PointerHistoryIter<'_> { + let history_size = self.event.ga_event.historySize as usize; + crate::input::PointerHistoryIter { + inner: PointerHistoryIterImpl { + event: self.event.ga_event, + pointer_index: self.index, + front: 0, + back: history_size, + }, + } + } } /// An iterator over the pointers in a [`MotionEvent`]. @@ -334,136 +326,34 @@ impl<'a> Iterator for PointersIterImpl<'a> { } } -impl ExactSizeIterator for PointersIterImpl<'_> { - fn len(&self) -> usize { - self.count - self.next_index - } -} - -/* -/// Represents a view into a past moment of a motion event -#[derive(Debug)] -pub struct HistoricalMotionEvent<'a> { - event: NonNull, - history_index: usize, - _marker: std::marker::PhantomData<&'a MotionEvent>, -} - -// TODO: thread safety? - -impl<'a> HistoricalMotionEvent<'a> { - /// Returns the "history index" associated with this historical event. Older events have smaller indices. - #[inline] - pub fn history_index(&self) -> usize { - self.history_index - } - - /// Returns the time of the historical event, in the `java.lang.System.nanoTime()` time base - /// - /// See [the NDK - /// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_gethistoricaleventtime) - #[inline] - pub fn event_time(&self) -> i64 { - unsafe { - ndk_sys::AMotionEvent_getHistoricalEventTime( - self.event.as_ptr(), - self.history_index as ndk_sys::size_t, - ) - } - } - - /// An iterator over the pointers of this historical motion event - #[inline] - pub fn pointers(&self) -> HistoricalPointersIter<'a> { - HistoricalPointersIter { - event: self.event, - history_index: self.history_index, - next_pointer_index: 0, - pointer_count: unsafe { - ndk_sys::AMotionEvent_getPointerCount(self.event.as_ptr()) as usize - }, - _marker: std::marker::PhantomData, - } - } -} - -/// An iterator over all the historical moments in a [`MotionEvent`]. -/// -/// It iterates from oldest to newest. -#[derive(Debug)] -pub struct HistoricalMotionEventsIter<'a> { - event: NonNull, - next_history_index: usize, - history_size: usize, - _marker: std::marker::PhantomData<&'a MotionEvent>, -} - -// TODO: thread safety? - -impl<'a> Iterator for HistoricalMotionEventsIter<'a> { - type Item = HistoricalMotionEvent<'a>; - - fn next(&mut self) -> Option> { - if self.next_history_index < self.history_size { - let res = HistoricalMotionEvent { - event: self.event, - history_index: self.next_history_index, - _marker: std::marker::PhantomData, - }; - self.next_history_index += 1; - Some(res) - } else { - None - } - } - - fn size_hint(&self) -> (usize, Option) { - let size = self.history_size - self.next_history_index; - (size, Some(size)) - } -} -impl ExactSizeIterator for HistoricalMotionEventsIter<'_> { - fn len(&self) -> usize { - self.history_size - self.next_history_index - } -} -impl<'a> DoubleEndedIterator for HistoricalMotionEventsIter<'a> { - fn next_back(&mut self) -> Option> { - if self.next_history_index < self.history_size { - self.history_size -= 1; - Some(HistoricalMotionEvent { - event: self.event, - history_index: self.history_size, - _marker: std::marker::PhantomData, - }) - } else { - None - } - } -} +impl ExactSizeIterator for PointersIterImpl<'_> {} /// A view into a pointer at a historical moment #[derive(Debug)] -pub struct HistoricalPointer<'a> { - event: NonNull, +pub struct HistoricalPointerImpl<'a> { + event: &'a GameActivityMotionEvent, pointer_index: usize, history_index: usize, - _marker: std::marker::PhantomData<&'a MotionEvent>, } -// TODO: thread safety? - -impl<'a> HistoricalPointer<'a> { +impl<'a> HistoricalPointerImpl<'a> { #[inline] pub fn pointer_index(&self) -> usize { self.pointer_index } + /// Returns the time of the historical event, in the `java.lang.System.nanoTime()` time base + /// + /// See [`MotionEvent.getHistoricalEventTimeNanos`](https://developer.android.com/reference/android/view/MotionEvent#getHistoricalEventTimeNanos(int)) SDK docs + #[inline] + pub fn event_time(&self) -> i64 { + unsafe { *self.event.historicalEventTimesNanos.add(self.history_index) } + } + #[inline] pub fn pointer_id(&self) -> i32 { - unsafe { - ndk_sys::AMotionEvent_getPointerId(self.event.as_ptr(), self.pointer_index as ndk_sys::size_t) - } + let pointer = &self.event.pointers[self.pointer_index]; + pointer.id } #[inline] @@ -474,179 +364,68 @@ impl<'a> HistoricalPointer<'a> { #[inline] pub fn axis_value(&self, axis: Axis) -> f32 { unsafe { - ndk_sys::AMotionEvent_getHistoricalAxisValue( - self.event.as_ptr(), - axis as i32, - self.pointer_index as ndk_sys::size_t, - self.history_index as ndk_sys::size_t, - ) - } - } - - #[inline] - pub fn orientation(&self) -> f32 { - unsafe { - ndk_sys::AMotionEvent_getHistoricalOrientation( - self.event.as_ptr(), - self.pointer_index as ndk_sys::size_t, - self.history_index as ndk_sys::size_t, - ) - } - } - - #[inline] - pub fn pressure(&self) -> f32 { - unsafe { - ndk_sys::AMotionEvent_getHistoricalPressure( - self.event.as_ptr(), - self.pointer_index as ndk_sys::size_t, - self.history_index as ndk_sys::size_t, - ) - } - } - - #[inline] - pub fn raw_x(&self) -> f32 { - unsafe { - ndk_sys::AMotionEvent_getHistoricalRawX( - self.event.as_ptr(), - self.pointer_index as ndk_sys::size_t, - self.history_index as ndk_sys::size_t, - ) - } - } - - #[inline] - pub fn raw_y(&self) -> f32 { - unsafe { - ndk_sys::AMotionEvent_getHistoricalRawY( - self.event.as_ptr(), - self.pointer_index as ndk_sys::size_t, - self.history_index as ndk_sys::size_t, - ) - } - } - - #[inline] - pub fn x(&self) -> f32 { - unsafe { - ndk_sys::AMotionEvent_getHistoricalX( - self.event.as_ptr(), - self.pointer_index as ndk_sys::size_t, - self.history_index as ndk_sys::size_t, - ) - } - } - - #[inline] - pub fn y(&self) -> f32 { - unsafe { - ndk_sys::AMotionEvent_getHistoricalY( - self.event.as_ptr(), - self.pointer_index as ndk_sys::size_t, - self.history_index as ndk_sys::size_t, - ) - } - } - - #[inline] - pub fn size(&self) -> f32 { - unsafe { - ndk_sys::AMotionEvent_getHistoricalSize( - self.event.as_ptr(), - self.pointer_index as ndk_sys::size_t, - self.history_index as ndk_sys::size_t, - ) - } - } - - #[inline] - pub fn tool_major(&self) -> f32 { - unsafe { - ndk_sys::AMotionEvent_getHistoricalToolMajor( - self.event.as_ptr(), - self.pointer_index as ndk_sys::size_t, - self.history_index as ndk_sys::size_t, - ) - } - } - - #[inline] - pub fn tool_minor(&self) -> f32 { - unsafe { - ndk_sys::AMotionEvent_getHistoricalToolMinor( - self.event.as_ptr(), - self.pointer_index as ndk_sys::size_t, - self.history_index as ndk_sys::size_t, - ) - } - } - - #[inline] - pub fn touch_major(&self) -> f32 { - unsafe { - ndk_sys::AMotionEvent_getHistoricalTouchMajor( - self.event.as_ptr(), - self.pointer_index as ndk_sys::size_t, - self.history_index as ndk_sys::size_t, - ) - } - } - - #[inline] - pub fn touch_minor(&self) -> f32 { - unsafe { - ndk_sys::AMotionEvent_getHistoricalTouchMinor( - self.event.as_ptr(), - self.pointer_index as ndk_sys::size_t, - self.history_index as ndk_sys::size_t, + ffi::GameActivityMotionEvent_getHistoricalAxisValue( + self.event, + Into::::into(axis) as i32, + self.pointer_index as i32, + self.history_index as i32, ) } } } -/// An iterator over the pointers in a historical motion event +/// An iterator over the historical points of a specific pointer in a [`MotionEvent`]. #[derive(Debug)] -pub struct HistoricalPointersIter<'a> { - event: NonNull, - history_index: usize, - next_pointer_index: usize, - pointer_count: usize, - _marker: std::marker::PhantomData<&'a MotionEvent>, +pub struct PointerHistoryIterImpl<'a> { + event: &'a GameActivityMotionEvent, + pointer_index: usize, + front: usize, + back: usize, } -// TODO: thread safety? +impl<'a> Iterator for PointerHistoryIterImpl<'a> { + type Item = crate::input::HistoricalPointer<'a>; -impl<'a> Iterator for HistoricalPointersIter<'a> { - type Item = HistoricalPointer<'a>; - - fn next(&mut self) -> Option> { - if self.next_pointer_index < self.pointer_count { - let ptr = HistoricalPointer { - event: self.event, - history_index: self.history_index, - pointer_index: self.next_pointer_index, - _marker: std::marker::PhantomData, - }; - self.next_pointer_index += 1; - Some(ptr) - } else { - None + fn next(&mut self) -> Option> { + if self.front == self.back { + return None; } + + let history_index = self.front; + self.front += 1; + Some(crate::input::HistoricalPointer { + inner: crate::input::HistoricalPointerImpl { + event: self.event, + history_index, + pointer_index: self.pointer_index, + }, + }) } fn size_hint(&self) -> (usize, Option) { - let size = self.pointer_count - self.next_pointer_index; + let size = self.back - self.front; (size, Some(size)) } } -impl ExactSizeIterator for HistoricalPointersIter<'_> { - fn len(&self) -> usize { - self.pointer_count - self.next_pointer_index +impl<'a> DoubleEndedIterator for PointerHistoryIterImpl<'a> { + fn next_back(&mut self) -> Option> { + if self.front == self.back { + return None; + } + + self.back -= 1; + let history_index = self.back; + Some(crate::input::HistoricalPointer { + inner: crate::input::HistoricalPointerImpl { + event: self.event, + history_index, + pointer_index: self.pointer_index, + }, + }) } } - -*/ +impl ExactSizeIterator for PointerHistoryIterImpl<'_> {} +impl FusedIterator for PointerHistoryIterImpl<'_> {} /// A key event. /// diff --git a/android-activity/src/input.rs b/android-activity/src/input.rs index b772a86..8bcf211 100644 --- a/android-activity/src/input.rs +++ b/android-activity/src/input.rs @@ -1,3 +1,5 @@ +use std::iter::FusedIterator; + use bitflags::bitflags; pub use crate::activity_impl::input::*; @@ -1416,6 +1418,20 @@ impl Pointer<'_> { pub fn tool_type(&self) -> ToolType { self.inner.tool_type() } + + /// Gives access to the historical data of a pointer in a [`MotionEvent`]. + /// + /// This provides access to higher-frequency data points that were recorded + /// between the current event and the previous event, which can be used for + /// more accurate gesture detection and smoother animations. + /// + /// For a single [`MotionEvent`] each pointer will have the same number of + /// historical events, and the corresponding historical events will have the + /// same timestamps. + #[inline] + pub fn history(&self) -> PointerHistoryIter<'_> { + self.inner.history() + } } /// An iterator over the pointers in a [`MotionEvent`]. @@ -1435,8 +1451,108 @@ impl<'a> Iterator for PointersIter<'a> { } } -impl ExactSizeIterator for PointersIter<'_> { - fn len(&self) -> usize { - self.inner.len() +impl ExactSizeIterator for PointersIter<'_> {} + +/// An iterator over the historical data of a pointer in a [`MotionEvent`]. +/// +/// This provides access to higher-frequency data points that were recorded +/// between the current event and the previous event, which can be used for more +/// accurate gesture detection and smoother animations. +/// +/// For a single [`MotionEvent`] each pointer will have the same number of +/// historical events, and the corresponding historical events will have the +/// same timestamps. +/// +#[derive(Debug)] +pub struct PointerHistoryIter<'a> { + pub(crate) inner: PointerHistoryIterImpl<'a>, +} + +impl<'a> Iterator for PointerHistoryIter<'a> { + type Item = HistoricalPointer<'a>; + fn next(&mut self) -> Option> { + self.inner.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} +impl<'a> DoubleEndedIterator for PointerHistoryIter<'a> { + fn next_back(&mut self) -> Option> { + self.inner.next_back() + } +} +impl ExactSizeIterator for PointerHistoryIter<'_> {} +impl FusedIterator for PointerHistoryIter<'_> {} + +pub struct HistoricalPointer<'a> { + pub(crate) inner: HistoricalPointerImpl<'a>, +} + +impl HistoricalPointer<'_> { + #[inline] + pub fn history_index(&self) -> usize { + self.inner.history_index() + } + + #[inline] + pub fn pointer_index(&self) -> usize { + self.inner.pointer_index() + } + + #[inline] + pub fn event_time(&self) -> i64 { + self.inner.event_time() + } + + #[inline] + pub fn axis_value(&self, axis: Axis) -> f32 { + self.inner.axis_value(axis) + } + + #[inline] + pub fn orientation(&self) -> f32 { + self.axis_value(Axis::Orientation) + } + + #[inline] + pub fn pressure(&self) -> f32 { + self.axis_value(Axis::Pressure) + } + + #[inline] + pub fn x(&self) -> f32 { + self.axis_value(Axis::X) + } + + #[inline] + pub fn y(&self) -> f32 { + self.axis_value(Axis::Y) + } + + #[inline] + pub fn size(&self) -> f32 { + self.axis_value(Axis::Size) + } + + #[inline] + pub fn tool_major(&self) -> f32 { + self.axis_value(Axis::ToolMajor) + } + + #[inline] + pub fn tool_minor(&self) -> f32 { + self.axis_value(Axis::ToolMinor) + } + + #[inline] + pub fn touch_major(&self) -> f32 { + self.axis_value(Axis::TouchMajor) + } + + #[inline] + pub fn touch_minor(&self) -> f32 { + self.axis_value(Axis::TouchMinor) } } diff --git a/android-activity/src/native_activity/input.rs b/android-activity/src/native_activity/input.rs index 00e088b..760ccbf 100644 --- a/android-activity/src/native_activity/input.rs +++ b/android-activity/src/native_activity/input.rs @@ -1,4 +1,4 @@ -use std::marker::PhantomData; +use std::{iter::FusedIterator, marker::PhantomData, ptr::NonNull}; use crate::input::{ Axis, Button, ButtonState, EdgeFlags, KeyAction, Keycode, MetaState, MotionAction, @@ -111,7 +111,10 @@ impl MotionEvent<'_> { pub fn pointers(&self) -> PointersIter<'_> { PointersIter { inner: PointersIterImpl { - ndk_pointers_iter: self.ndk_event.pointers(), + event: self.ndk_event.ptr(), + pointer_index: 0, + pointer_count: self.ndk_event.pointer_count(), + _marker: std::marker::PhantomData, }, } } @@ -123,31 +126,13 @@ impl MotionEvent<'_> { pub fn pointer_at_index(&self, index: usize) -> Pointer<'_> { Pointer { inner: PointerImpl { - ndk_pointer: self.ndk_event.pointer_at_index(index), + event: self.ndk_event.ptr(), + pointer_index: index, + _marker: std::marker::PhantomData, }, } } - /* - XXX: Not currently supported with GameActivity so we don't currently expose for NativeActivity - either, for consistency. - - /// Returns the size of the history contained in this event. - /// - /// See [the NDK - /// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_gethistorysize) - #[inline] - pub fn history_size(&self) -> usize { - self.ndk_event.history_size() - } - - /// An iterator over the historical events contained in this event. - #[inline] - pub fn history(&self) -> HistoricalMotionEventsIter<'_> { - self.ndk_event.history() - } - */ - /// Returns the state of any modifier keys that were pressed during the event. /// /// See [the NDK @@ -245,69 +230,200 @@ impl MotionEvent<'_> { /// A view into the data of a specific pointer in a motion event. #[derive(Debug)] pub(crate) struct PointerImpl<'a> { - ndk_pointer: ndk::event::Pointer<'a>, + event: NonNull, + pointer_index: usize, + _marker: std::marker::PhantomData<&'a MotionEvent<'a>>, } impl PointerImpl<'_> { #[inline] pub fn pointer_index(&self) -> usize { - self.ndk_pointer.pointer_index() + self.pointer_index } #[inline] pub fn pointer_id(&self) -> i32 { - self.ndk_pointer.pointer_id() + unsafe { ndk_sys::AMotionEvent_getPointerId(self.event.as_ptr(), self.pointer_index) } } #[inline] pub fn axis_value(&self, axis: Axis) -> f32 { let value: u32 = axis.into(); let value = value as i32; - self.ndk_pointer.axis_value(value.into()) + unsafe { + ndk_sys::AMotionEvent_getAxisValue(self.event.as_ptr(), value, self.pointer_index) + } } #[inline] pub fn raw_x(&self) -> f32 { - self.ndk_pointer.raw_x() + unsafe { ndk_sys::AMotionEvent_getRawX(self.event.as_ptr(), self.pointer_index) } } #[inline] pub fn raw_y(&self) -> f32 { - self.ndk_pointer.raw_y() + unsafe { ndk_sys::AMotionEvent_getRawY(self.event.as_ptr(), self.pointer_index) } } #[inline] pub fn tool_type(&self) -> ToolType { - let value: i32 = self.ndk_pointer.tool_type().into(); + let value = + unsafe { ndk_sys::AMotionEvent_getToolType(self.event.as_ptr(), self.pointer_index) }; let value = value as u32; value.into() } + + pub fn history(&self) -> crate::input::PointerHistoryIter<'_> { + let history_size = + unsafe { ndk_sys::AMotionEvent_getHistorySize(self.event.as_ptr()) } as usize; + crate::input::PointerHistoryIter { + inner: PointerHistoryIterImpl { + event: self.event, + pointer_index: self.pointer_index, + front: 0, + back: history_size, + _marker: std::marker::PhantomData, + }, + } + } } -/// An iterator over the pointers in a [`MotionEvent`]. #[derive(Debug)] pub(crate) struct PointersIterImpl<'a> { - ndk_pointers_iter: ndk::event::PointersIter<'a>, + event: NonNull, + pointer_count: usize, + pointer_index: usize, + _marker: std::marker::PhantomData<&'a MotionEvent<'a>>, } impl<'a> Iterator for PointersIterImpl<'a> { type Item = Pointer<'a>; fn next(&mut self) -> Option> { - self.ndk_pointers_iter.next().map(|ndk_pointer| Pointer { - inner: PointerImpl { ndk_pointer }, + if self.pointer_index == self.pointer_count { + return None; + } + let pointer = Pointer { + inner: PointerImpl { + event: self.event, + pointer_index: self.pointer_index, + _marker: std::marker::PhantomData, + }, + }; + self.pointer_index += 1; + Some(pointer) + } + + fn size_hint(&self) -> (usize, Option) { + let remaining = self.pointer_count - self.pointer_index; + (remaining, Some(remaining)) + } +} + +impl ExactSizeIterator for PointersIterImpl<'_> {} + +/// A view into a pointer at a historical moment +#[derive(Debug)] +pub struct HistoricalPointerImpl<'a> { + event: NonNull, + pointer_index: usize, + history_index: usize, + _marker: std::marker::PhantomData<&'a MotionEvent<'a>>, +} + +impl<'a> HistoricalPointerImpl<'a> { + #[inline] + pub fn pointer_index(&self) -> usize { + self.pointer_index + } + + /// Returns the time of the historical event, in the `java.lang.System.nanoTime()` time base + /// + /// See [`MotionEvent.getHistoricalEventTimeNanos`](https://developer.android.com/reference/android/view/MotionEvent#getHistoricalEventTimeNanos(int)) SDK docs + #[inline] + pub fn event_time(&self) -> i64 { + unsafe { + ndk_sys::AMotionEvent_getHistoricalEventTime(self.event.as_ptr(), self.history_index) + } + } + + #[inline] + pub fn pointer_id(&self) -> i32 { + unsafe { ndk_sys::AMotionEvent_getPointerId(self.event.as_ptr(), self.pointer_index) } + } + + #[inline] + pub fn history_index(&self) -> usize { + self.history_index + } + + #[inline] + pub fn axis_value(&self, axis: Axis) -> f32 { + unsafe { + ndk_sys::AMotionEvent_getHistoricalAxisValue( + self.event.as_ptr(), + Into::::into(axis) as i32, + self.pointer_index, + self.history_index, + ) + } + } +} + +/// An iterator over the historical points of a specific pointer in a [`MotionEvent`]. +#[derive(Debug)] +pub struct PointerHistoryIterImpl<'a> { + event: NonNull, + pointer_index: usize, + front: usize, + back: usize, + _marker: std::marker::PhantomData<&'a MotionEvent<'a>>, +} + +impl<'a> Iterator for PointerHistoryIterImpl<'a> { + type Item = crate::input::HistoricalPointer<'a>; + + fn next(&mut self) -> Option> { + if self.front == self.back { + return None; + } + + let history_index = self.front; + self.front += 1; + Some(crate::input::HistoricalPointer { + inner: crate::input::HistoricalPointerImpl { + event: self.event, + history_index, + pointer_index: self.pointer_index, + _marker: std::marker::PhantomData, + }, }) } fn size_hint(&self) -> (usize, Option) { - self.ndk_pointers_iter.size_hint() + let size = self.back - self.front; + (size, Some(size)) } } +impl<'a> DoubleEndedIterator for PointerHistoryIterImpl<'a> { + fn next_back(&mut self) -> Option> { + if self.front == self.back { + return None; + } -impl ExactSizeIterator for PointersIterImpl<'_> { - fn len(&self) -> usize { - self.ndk_pointers_iter.len() + self.back -= 1; + let history_index = self.back; + Some(crate::input::HistoricalPointer { + inner: crate::input::HistoricalPointerImpl { + event: self.event, + history_index, + pointer_index: self.pointer_index, + _marker: std::marker::PhantomData, + }, + }) } } +impl ExactSizeIterator for PointerHistoryIterImpl<'_> {} +impl FusedIterator for PointerHistoryIterImpl<'_> {} /// A key event ///