mirror of
https://github.com/rust-mobile/android-activity.git
synced 2026-07-04 05:47:26 +00:00
Thin MotionEvent + KeyEvent types with lifetimes
This patch is addressing two notable issues: 1. MotionEvents with the GameActivity backend were extremely large and forced us to copy lots of redundant padding from the internal circular buffer of events when calling an apps `input_events()` callback. 2. MotionEvents + KeyEvents with the NativeActivity backend (re-exported from the ndk crate) simply wrapped a raw pointer but had no lifetime that would stop applications from keeping them too long and then potentially dereferencing an invalid pointer. The common change is that `InputEvent` is now defined with a lifetime parameter which makes it possible for Motion and Key events to hold references that let them avoid copying large amounts of state. The `Source` and `Class` enums were also moved out of the GameActivity backend so they could be shared since there was some inconsistency between the backends with these types. Overall this doesn't, practically, affect the public API GameActivity ============ On the GameActivity side the events will now point into the internal circular buffer that is iterated during `input_events()` and so the `MotionEvent` type is now a thin wrapper over a reference. This removes the `Iterator` implementations we had internally for iterating key/motion events because we effectively need a lending iterator now. It's also noted that while our MSRV is 1.60 we can't use GATs to implement the interation in terms of a LendingIterator trait. (This is fine in practice because this iteration is a private implementation detail and so we can have lending iteration without any traits for now) NativeActivity ============== On the NativeActivity side we now have newtype wrappers around MotionEvent and KeyEvent from the ndk crate. These newtypes add a lifetime and implement all the same passthrough methods as the corresponding GameActivity types. This added more boilerplate to the NativeActivity backend but it also improves consistency between the backends. Fixes: #40 Fixes: #41
This commit is contained in:
@@ -13,91 +13,19 @@
|
||||
// The `Class` was also bound differently to `android-ndk-rs` considering how the class is defined
|
||||
// by masking bits from the `Source`.
|
||||
|
||||
use crate::game_activity::ffi::{GameActivityKeyEvent, GameActivityMotionEvent};
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use std::{convert::TryInto, ops::Deref};
|
||||
|
||||
use bitflags::bitflags;
|
||||
use crate::game_activity::ffi::{GameActivityKeyEvent, GameActivityMotionEvent};
|
||||
use crate::input::{Class, Source};
|
||||
|
||||
// Note: try to keep this wrapper API compatible with the AInputEvent API if possible
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub enum InputEvent {
|
||||
MotionEvent(MotionEvent),
|
||||
KeyEvent(KeyEvent),
|
||||
}
|
||||
|
||||
/// An enum representing the source of an [`MotionEvent`] or [`KeyEvent`]
|
||||
///
|
||||
/// See [the InputDevice docs](https://developer.android.com/reference/android/view/InputDevice#SOURCE_ANY)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(i32)]
|
||||
pub enum Source {
|
||||
BluetoothStylus = 0x0000c002,
|
||||
Dpad = 0x00000201,
|
||||
/// Either a gamepad or a joystick
|
||||
Gamepad = 0x00000401,
|
||||
Hdmi = 0x02000001,
|
||||
/// Either a gamepad or a joystick
|
||||
Joystick = 0x01000010,
|
||||
/// Pretty much any device with buttons. Query the keyboard type to determine
|
||||
/// if it has alphabetic keys and can be used for text entry.
|
||||
Keyboard = 0x00000101,
|
||||
/// A pointing device, such as a mouse or trackpad
|
||||
Mouse = 0x00002002,
|
||||
/// A pointing device, such as a mouse or trackpad whose relative motions should be treated as navigation events
|
||||
MouseRelative = 0x00020004,
|
||||
/// An input device akin to a scroll wheel
|
||||
RotaryEncoder = 0x00400000,
|
||||
Sensor = 0x04000000,
|
||||
Stylus = 0x00004002,
|
||||
Touchpad = 0x00100008,
|
||||
Touchscreen = 0x00001002,
|
||||
TouchNavigation = 0x00200000,
|
||||
Trackball = 0x00010004,
|
||||
|
||||
Unknown = 0,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct SourceFlags: u32 {
|
||||
const CLASS_MASK = 0x000000ff;
|
||||
|
||||
const BUTTON = 0x00000001;
|
||||
const POINTER = 0x00000002;
|
||||
const TRACKBALL = 0x00000004;
|
||||
const POSITION = 0x00000008;
|
||||
const JOYSTICK = 0x00000010;
|
||||
const NONE = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// An enum representing the class of a [`MotionEvent`] or [`KeyEvent`] source
|
||||
///
|
||||
/// See [the InputDevice docs](https://developer.android.com/reference/android/view/InputDevice#SOURCE_CLASS_MASK)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Class {
|
||||
None,
|
||||
Button,
|
||||
Pointer,
|
||||
Trackball,
|
||||
Position,
|
||||
Joystick,
|
||||
}
|
||||
|
||||
impl From<i32> for Class {
|
||||
fn from(source: i32) -> Self {
|
||||
let class = SourceFlags::from_bits_truncate(source as u32);
|
||||
match class {
|
||||
SourceFlags::BUTTON => Class::Button,
|
||||
SourceFlags::POINTER => Class::Pointer,
|
||||
SourceFlags::TRACKBALL => Class::Trackball,
|
||||
SourceFlags::POSITION => Class::Position,
|
||||
SourceFlags::JOYSTICK => Class::Joystick,
|
||||
_ => Class::None,
|
||||
}
|
||||
}
|
||||
pub enum InputEvent<'a> {
|
||||
MotionEvent(MotionEvent<'a>),
|
||||
KeyEvent(KeyEvent<'a>),
|
||||
}
|
||||
|
||||
/// A bitfield representing the state of modifier keys during an event.
|
||||
@@ -182,15 +110,15 @@ impl MetaState {
|
||||
/// For general discussion of motion events in Android, see [the relevant
|
||||
/// javadoc](https://developer.android.com/reference/android/view/MotionEvent).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MotionEvent {
|
||||
ga_event: GameActivityMotionEvent,
|
||||
pub struct MotionEvent<'a> {
|
||||
ga_event: &'a GameActivityMotionEvent,
|
||||
}
|
||||
|
||||
impl Deref for MotionEvent {
|
||||
impl<'a> Deref for MotionEvent<'a> {
|
||||
type Target = GameActivityMotionEvent;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.ga_event
|
||||
self.ga_event
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,8 +286,8 @@ impl MotionEventFlags {
|
||||
}
|
||||
}
|
||||
|
||||
impl MotionEvent {
|
||||
pub(crate) fn new(ga_event: GameActivityMotionEvent) -> Self {
|
||||
impl<'a> MotionEvent<'a> {
|
||||
pub(crate) fn new(ga_event: &'a GameActivityMotionEvent) -> Self {
|
||||
Self { ga_event }
|
||||
}
|
||||
|
||||
@@ -367,14 +295,15 @@ impl MotionEvent {
|
||||
///
|
||||
#[inline]
|
||||
pub fn source(&self) -> Source {
|
||||
self.source.try_into().unwrap_or(Source::Unknown)
|
||||
let source = self.source as u32;
|
||||
source.try_into().unwrap_or(Source::Unknown)
|
||||
}
|
||||
|
||||
/// Get the class of the event source.
|
||||
///
|
||||
#[inline]
|
||||
pub fn class(&self) -> Class {
|
||||
Class::from(self.source)
|
||||
Class::from(self.source())
|
||||
}
|
||||
|
||||
/// Get the device id associated with the event.
|
||||
@@ -569,7 +498,7 @@ impl MotionEvent {
|
||||
/// A view into the data of a specific pointer in a motion event.
|
||||
#[derive(Debug)]
|
||||
pub struct Pointer<'a> {
|
||||
event: &'a MotionEvent,
|
||||
event: &'a MotionEvent<'a>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
@@ -659,7 +588,7 @@ impl<'a> Pointer<'a> {
|
||||
/// An iterator over the pointers in a [`MotionEvent`].
|
||||
#[derive(Debug)]
|
||||
pub struct PointersIter<'a> {
|
||||
event: &'a MotionEvent,
|
||||
event: &'a MotionEvent<'a>,
|
||||
next_index: usize,
|
||||
count: usize,
|
||||
}
|
||||
@@ -1004,15 +933,15 @@ impl ExactSizeIterator for HistoricalPointersIter<'_> {
|
||||
/// For general discussion of key events in Android, see [the relevant
|
||||
/// javadoc](https://developer.android.com/reference/android/view/KeyEvent).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KeyEvent {
|
||||
ga_event: GameActivityKeyEvent,
|
||||
pub struct KeyEvent<'a> {
|
||||
ga_event: &'a GameActivityKeyEvent,
|
||||
}
|
||||
|
||||
impl Deref for KeyEvent {
|
||||
impl<'a> Deref for KeyEvent<'a> {
|
||||
type Target = GameActivityKeyEvent;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.ga_event
|
||||
self.ga_event
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1324,8 +1253,8 @@ pub enum Keycode {
|
||||
ProfileSwitch = ndk_sys::AKEYCODE_PROFILE_SWITCH,
|
||||
}
|
||||
|
||||
impl KeyEvent {
|
||||
pub(crate) fn new(ga_event: GameActivityKeyEvent) -> Self {
|
||||
impl<'a> KeyEvent<'a> {
|
||||
pub(crate) fn new(ga_event: &'a GameActivityKeyEvent) -> Self {
|
||||
Self { ga_event }
|
||||
}
|
||||
|
||||
@@ -1333,14 +1262,15 @@ impl KeyEvent {
|
||||
///
|
||||
#[inline]
|
||||
pub fn source(&self) -> Source {
|
||||
self.source.try_into().unwrap_or(Source::Unknown)
|
||||
let source = self.source as u32;
|
||||
source.try_into().unwrap_or(Source::Unknown)
|
||||
}
|
||||
|
||||
/// Get the class of the event source.
|
||||
///
|
||||
#[inline]
|
||||
pub fn class(&self) -> Class {
|
||||
Class::from(self.source)
|
||||
Class::from(self.source())
|
||||
}
|
||||
|
||||
/// Get the device id associated with the event.
|
||||
@@ -1461,7 +1391,7 @@ impl KeyEventFlags {
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyEvent {
|
||||
impl<'a> KeyEvent<'a> {
|
||||
/// Flags associated with this [`KeyEvent`].
|
||||
///
|
||||
/// See [the NDK docs](https://developer.android.com/ndk/reference/group/input#akeyevent_getflags)
|
||||
|
||||
@@ -404,10 +404,12 @@ impl AndroidAppInner {
|
||||
InputBuffer::from_ptr(NonNull::new_unchecked(input_buffer))
|
||||
};
|
||||
|
||||
for key_event in buf.key_events_iter() {
|
||||
let mut keys_iter = KeyEventsLendingIterator::new(&buf);
|
||||
while let Some(key_event) = keys_iter.next() {
|
||||
callback(&InputEvent::KeyEvent(key_event));
|
||||
}
|
||||
for motion_event in buf.motion_events_iter() {
|
||||
let mut motion_iter = MotionEventsLendingIterator::new(&buf);
|
||||
while let Some(motion_event) = motion_iter.next() {
|
||||
callback(&InputEvent::MotionEvent(motion_event));
|
||||
}
|
||||
}
|
||||
@@ -434,46 +436,58 @@ impl AndroidAppInner {
|
||||
}
|
||||
}
|
||||
|
||||
struct MotionEventsIterator<'a> {
|
||||
struct MotionEventsLendingIterator<'a> {
|
||||
pos: usize,
|
||||
count: usize,
|
||||
buffer: &'a InputBuffer<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for MotionEventsIterator<'a> {
|
||||
type Item = MotionEvent;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// A kind of lending iterator but since our MSRV is 1.60 we can't handle this
|
||||
// via a generic trait. The iteration of motion events is entirely private
|
||||
// though so this is ok for now.
|
||||
impl<'a> MotionEventsLendingIterator<'a> {
|
||||
fn new(buffer: &'a InputBuffer<'a>) -> Self {
|
||||
Self {
|
||||
pos: 0,
|
||||
count: buffer.motion_events_count(),
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
fn next(&mut self) -> Option<MotionEvent<'a>> {
|
||||
if self.pos < self.count {
|
||||
unsafe {
|
||||
let ga_event = (*self.buffer.ptr.as_ptr()).motionEvents[self.pos];
|
||||
let event = MotionEvent::new(ga_event);
|
||||
self.pos += 1;
|
||||
Some(event)
|
||||
}
|
||||
let ga_event = unsafe { &(*self.buffer.ptr.as_ptr()).motionEvents[self.pos] };
|
||||
let event = MotionEvent::new(ga_event);
|
||||
self.pos += 1;
|
||||
Some(event)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct KeyEventsIterator<'a> {
|
||||
struct KeyEventsLendingIterator<'a> {
|
||||
pos: usize,
|
||||
count: usize,
|
||||
buffer: &'a InputBuffer<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for KeyEventsIterator<'a> {
|
||||
type Item = KeyEvent;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// A kind of lending iterator but since our MSRV is 1.60 we can't handle this
|
||||
// via a generic trait. The iteration of key events is entirely private
|
||||
// though so this is ok for now.
|
||||
impl<'a> KeyEventsLendingIterator<'a> {
|
||||
fn new(buffer: &'a InputBuffer<'a>) -> Self {
|
||||
Self {
|
||||
pos: 0,
|
||||
count: buffer.key_events_count(),
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
fn next(&mut self) -> Option<KeyEvent<'a>> {
|
||||
if self.pos < self.count {
|
||||
unsafe {
|
||||
let ga_event = (*self.buffer.ptr.as_ptr()).keyEvents[self.pos];
|
||||
let event = KeyEvent::new(ga_event);
|
||||
self.pos += 1;
|
||||
Some(event)
|
||||
}
|
||||
let ga_event = unsafe { &(*self.buffer.ptr.as_ptr()).keyEvents[self.pos] };
|
||||
let event = KeyEvent::new(ga_event);
|
||||
self.pos += 1;
|
||||
Some(event)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -493,29 +507,12 @@ impl<'a> InputBuffer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: It's really not ideal here that Rust iterators can't yield values
|
||||
// that borrow from the iterator, so we implicitly have to copy the
|
||||
// events as we iterate...
|
||||
pub fn motion_events_iter(&self) -> MotionEventsIterator {
|
||||
unsafe {
|
||||
let count = (*self.ptr.as_ptr()).motionEventsCount as usize;
|
||||
MotionEventsIterator {
|
||||
pos: 0,
|
||||
count,
|
||||
buffer: self,
|
||||
}
|
||||
}
|
||||
pub fn motion_events_count(&self) -> usize {
|
||||
unsafe { (*self.ptr.as_ptr()).motionEventsCount as usize }
|
||||
}
|
||||
|
||||
pub fn key_events_iter(&self) -> KeyEventsIterator {
|
||||
unsafe {
|
||||
let count = (*self.ptr.as_ptr()).keyEventsCount as usize;
|
||||
KeyEventsIterator {
|
||||
pos: 0,
|
||||
count,
|
||||
buffer: self,
|
||||
}
|
||||
}
|
||||
pub fn key_events_count(&self) -> usize {
|
||||
unsafe { (*self.ptr.as_ptr()).keyEventsCount as usize }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
use bitflags::bitflags;
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
|
||||
pub use crate::activity_impl::input::*;
|
||||
|
||||
/// An enum representing the source of an [`MotionEvent`] or [`KeyEvent`]
|
||||
///
|
||||
/// See [the InputDevice docs](https://developer.android.com/reference/android/view/InputDevice#SOURCE_ANY)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum Source {
|
||||
BluetoothStylus = 0x0000c002,
|
||||
Dpad = 0x00000201,
|
||||
/// Either a gamepad or a joystick
|
||||
Gamepad = 0x00000401,
|
||||
Hdmi = 0x02000001,
|
||||
/// Either a gamepad or a joystick
|
||||
Joystick = 0x01000010,
|
||||
/// Pretty much any device with buttons. Query the keyboard type to determine
|
||||
/// if it has alphabetic keys and can be used for text entry.
|
||||
Keyboard = 0x00000101,
|
||||
/// A pointing device, such as a mouse or trackpad
|
||||
Mouse = 0x00002002,
|
||||
/// A pointing device, such as a mouse or trackpad whose relative motions should be treated as navigation events
|
||||
MouseRelative = 0x00020004,
|
||||
/// An input device akin to a scroll wheel
|
||||
RotaryEncoder = 0x00400000,
|
||||
Sensor = 0x04000000,
|
||||
Stylus = 0x00004002,
|
||||
Touchpad = 0x00100008,
|
||||
Touchscreen = 0x00001002,
|
||||
TouchNavigation = 0x00200000,
|
||||
Trackball = 0x00010004,
|
||||
|
||||
Unknown = 0,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct SourceFlags: u32 {
|
||||
const CLASS_MASK = 0x000000ff;
|
||||
|
||||
const BUTTON = 0x00000001;
|
||||
const POINTER = 0x00000002;
|
||||
const TRACKBALL = 0x00000004;
|
||||
const POSITION = 0x00000008;
|
||||
const JOYSTICK = 0x00000010;
|
||||
const NONE = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// An enum representing the class of a [`MotionEvent`] or [`KeyEvent`] source
|
||||
///
|
||||
/// See [the InputDevice docs](https://developer.android.com/reference/android/view/InputDevice#SOURCE_CLASS_MASK)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Class {
|
||||
None,
|
||||
Button,
|
||||
Pointer,
|
||||
Trackball,
|
||||
Position,
|
||||
Joystick,
|
||||
}
|
||||
|
||||
impl From<u32> for Class {
|
||||
fn from(source: u32) -> Self {
|
||||
let class = SourceFlags::from_bits_truncate(source as u32);
|
||||
match class {
|
||||
SourceFlags::BUTTON => Class::Button,
|
||||
SourceFlags::POINTER => Class::Pointer,
|
||||
SourceFlags::TRACKBALL => Class::Trackball,
|
||||
SourceFlags::POSITION => Class::Position,
|
||||
SourceFlags::JOYSTICK => Class::Joystick,
|
||||
_ => Class::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Source> for Class {
|
||||
fn from(source: Source) -> Self {
|
||||
let source: u32 = source.into();
|
||||
source.into()
|
||||
}
|
||||
}
|
||||
@@ -106,7 +106,7 @@ mod game_activity;
|
||||
#[cfg(feature = "game-activity")]
|
||||
use game_activity as activity_impl;
|
||||
|
||||
pub use activity_impl::input;
|
||||
pub mod input;
|
||||
|
||||
mod config;
|
||||
pub use config::ConfigurationRef;
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub use ndk::event::{
|
||||
Axis, ButtonState, EdgeFlags, KeyAction, KeyEventFlags, Keycode, MetaState, MotionAction,
|
||||
MotionEventFlags, Pointer, PointersIter,
|
||||
};
|
||||
|
||||
use crate::input::{Class, Source};
|
||||
|
||||
/// A motion event
|
||||
///
|
||||
/// For general discussion of motion events in Android, see [the relevant
|
||||
/// javadoc](https://developer.android.com/reference/android/view/MotionEvent).
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct MotionEvent<'a> {
|
||||
ndk_event: ndk::event::MotionEvent,
|
||||
_lifetime: PhantomData<&'a ndk::event::MotionEvent>,
|
||||
}
|
||||
impl<'a> MotionEvent<'a> {
|
||||
pub(crate) fn new(ndk_event: ndk::event::MotionEvent) -> Self {
|
||||
Self {
|
||||
ndk_event,
|
||||
_lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
pub(crate) fn into_ndk_event(self) -> ndk::event::MotionEvent {
|
||||
self.ndk_event
|
||||
}
|
||||
|
||||
/// Get the source of the event.
|
||||
///
|
||||
#[inline]
|
||||
pub fn source(&self) -> Source {
|
||||
// XXX: we use `AInputEvent_getSource` directly (instead of calling
|
||||
// ndk_event.source()) since we have our own `Source` enum that we
|
||||
// share between backends, which may not exactly match the ndk crate's
|
||||
// `Source` enum.
|
||||
let source =
|
||||
unsafe { ndk_sys::AInputEvent_getSource(self.ndk_event.ptr().as_ptr()) as u32 };
|
||||
source.try_into().unwrap_or(Source::Unknown)
|
||||
}
|
||||
|
||||
/// Get the class of the event source.
|
||||
///
|
||||
#[inline]
|
||||
pub fn class(&self) -> Class {
|
||||
Class::from(self.source())
|
||||
}
|
||||
|
||||
/// Get the device id associated with the event.
|
||||
///
|
||||
#[inline]
|
||||
pub fn device_id(&self) -> i32 {
|
||||
self.ndk_event.device_id()
|
||||
}
|
||||
|
||||
/// Returns the motion action associated with the event.
|
||||
///
|
||||
/// See [the MotionEvent docs](https://developer.android.com/reference/android/view/MotionEvent#getActionMasked())
|
||||
#[inline]
|
||||
pub fn action(&self) -> MotionAction {
|
||||
self.ndk_event.action()
|
||||
}
|
||||
|
||||
/// Returns the pointer index of an `Up` or `Down` event.
|
||||
///
|
||||
/// Pointer indices can change per motion event. For an identifier that stays the same, see
|
||||
/// [`Pointer::pointer_id()`].
|
||||
///
|
||||
/// This only has a meaning when the [action](Self::action) is one of [`Up`](MotionAction::Up),
|
||||
/// [`Down`](MotionAction::Down), [`PointerUp`](MotionAction::PointerUp),
|
||||
/// or [`PointerDown`](MotionAction::PointerDown).
|
||||
#[inline]
|
||||
pub fn pointer_index(&self) -> usize {
|
||||
self.ndk_event.pointer_index()
|
||||
}
|
||||
|
||||
/*
|
||||
/// Returns the pointer id associated with the given pointer index.
|
||||
///
|
||||
/// See [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_getpointerid)
|
||||
// TODO: look at output with out-of-range pointer index
|
||||
// Probably -1 though
|
||||
pub fn pointer_id_for(&self, pointer_index: usize) -> i32 {
|
||||
unsafe { ndk_sys::AMotionEvent_getPointerId(self.ndk_event.ptr.as_ptr(), pointer_index) }
|
||||
}
|
||||
*/
|
||||
|
||||
/// Returns the number of pointers in this event
|
||||
///
|
||||
/// See [the MotionEvent docs](https://developer.android.com/reference/android/view/MotionEvent#getPointerCount())
|
||||
#[inline]
|
||||
pub fn pointer_count(&self) -> usize {
|
||||
self.ndk_event.pointer_count()
|
||||
}
|
||||
|
||||
/// An iterator over the pointers in this motion event
|
||||
#[inline]
|
||||
pub fn pointers(&self) -> PointersIter<'_> {
|
||||
self.ndk_event.pointers()
|
||||
}
|
||||
|
||||
/// The pointer at a given pointer index. Panics if the pointer index is out of bounds.
|
||||
///
|
||||
/// If you need to loop over all the pointers, prefer the [`pointers()`](Self::pointers) method.
|
||||
#[inline]
|
||||
pub fn pointer_at_index(&self, index: usize) -> Pointer<'_> {
|
||||
self.ndk_event.pointer_at_index(index)
|
||||
}
|
||||
|
||||
/*
|
||||
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
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_getmetastate)
|
||||
#[inline]
|
||||
pub fn meta_state(&self) -> MetaState {
|
||||
self.ndk_event.meta_state()
|
||||
}
|
||||
|
||||
/// Returns the button state during this event, as a bitfield.
|
||||
///
|
||||
/// See [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_getbuttonstate)
|
||||
#[inline]
|
||||
pub fn button_state(&self) -> ButtonState {
|
||||
self.ndk_event.button_state()
|
||||
}
|
||||
|
||||
/// Returns the time of the start of this gesture, in the `java.lang.System.nanoTime()` time
|
||||
/// base
|
||||
///
|
||||
/// See [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_getdowntime)
|
||||
#[inline]
|
||||
pub fn down_time(&self) -> i64 {
|
||||
self.ndk_event.down_time()
|
||||
}
|
||||
|
||||
/// Returns a bitfield indicating which edges were touched by this event.
|
||||
///
|
||||
/// See [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_getedgeflags)
|
||||
#[inline]
|
||||
pub fn edge_flags(&self) -> EdgeFlags {
|
||||
self.ndk_event.edge_flags()
|
||||
}
|
||||
|
||||
/// Returns the time of this event, in the `java.lang.System.nanoTime()` time base
|
||||
///
|
||||
/// See [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_geteventtime)
|
||||
#[inline]
|
||||
pub fn event_time(&self) -> i64 {
|
||||
self.ndk_event.event_time()
|
||||
}
|
||||
|
||||
/// The flags associated with a motion event.
|
||||
///
|
||||
/// See [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_getflags)
|
||||
#[inline]
|
||||
pub fn flags(&self) -> MotionEventFlags {
|
||||
self.ndk_event.flags()
|
||||
}
|
||||
|
||||
/* Missing from GameActivity currently...
|
||||
/// Returns the offset in the x direction between the coordinates and the raw coordinates
|
||||
///
|
||||
/// See [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_getxoffset)
|
||||
#[inline]
|
||||
pub fn x_offset(&self) -> f32 {
|
||||
self.ndk_event.x_offset()
|
||||
}
|
||||
|
||||
/// Returns the offset in the y direction between the coordinates and the raw coordinates
|
||||
///
|
||||
/// See [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_getyoffset)
|
||||
#[inline]
|
||||
pub fn y_offset(&self) -> f32 {
|
||||
self.ndk_event.y_offset()
|
||||
}
|
||||
*/
|
||||
|
||||
/// Returns the precision of the x value of the coordinates
|
||||
///
|
||||
/// See [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_getxprecision)
|
||||
#[inline]
|
||||
pub fn x_precision(&self) -> f32 {
|
||||
self.ndk_event.x_precision()
|
||||
}
|
||||
|
||||
/// Returns the precision of the y value of the coordinates
|
||||
///
|
||||
/// See [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#amotionevent_getyprecision)
|
||||
#[inline]
|
||||
pub fn y_precision(&self) -> f32 {
|
||||
self.ndk_event.y_precision()
|
||||
}
|
||||
}
|
||||
|
||||
/// A key event
|
||||
///
|
||||
/// For general discussion of key events in Android, see [the relevant
|
||||
/// javadoc](https://developer.android.com/reference/android/view/KeyEvent).
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct KeyEvent<'a> {
|
||||
ndk_event: ndk::event::KeyEvent,
|
||||
_lifetime: PhantomData<&'a ndk::event::KeyEvent>,
|
||||
}
|
||||
impl<'a> KeyEvent<'a> {
|
||||
pub(crate) fn new(ndk_event: ndk::event::KeyEvent) -> Self {
|
||||
Self {
|
||||
ndk_event,
|
||||
_lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
pub(crate) fn into_ndk_event(self) -> ndk::event::KeyEvent {
|
||||
self.ndk_event
|
||||
}
|
||||
|
||||
/// Get the source of the event.
|
||||
///
|
||||
#[inline]
|
||||
pub fn source(&self) -> Source {
|
||||
// XXX: we use `AInputEvent_getSource` directly (instead of calling
|
||||
// ndk_event.source()) since we have our own `Source` enum that we
|
||||
// share between backends, which may not exactly match the ndk crate's
|
||||
// `Source` enum.
|
||||
let source =
|
||||
unsafe { ndk_sys::AInputEvent_getSource(self.ndk_event.ptr().as_ptr()) as u32 };
|
||||
source.try_into().unwrap_or(Source::Unknown)
|
||||
}
|
||||
|
||||
/// Get the class of the event source.
|
||||
///
|
||||
#[inline]
|
||||
pub fn class(&self) -> Class {
|
||||
Class::from(self.source())
|
||||
}
|
||||
|
||||
/// Get the device id associated with the event.
|
||||
///
|
||||
#[inline]
|
||||
pub fn device_id(&self) -> i32 {
|
||||
self.ndk_event.device_id()
|
||||
}
|
||||
|
||||
/// Returns the key action associated with the event.
|
||||
///
|
||||
/// See [the KeyEvent docs](https://developer.android.com/reference/android/view/KeyEvent#getAction())
|
||||
#[inline]
|
||||
pub fn action(&self) -> KeyAction {
|
||||
self.ndk_event.action()
|
||||
}
|
||||
|
||||
/// Returns the last time the key was pressed. This is on the scale of
|
||||
/// `java.lang.System.nanoTime()`, which has nanosecond precision, but no defined start time.
|
||||
///
|
||||
/// See [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#akeyevent_getdowntime)
|
||||
#[inline]
|
||||
pub fn down_time(&self) -> i64 {
|
||||
self.ndk_event.down_time()
|
||||
}
|
||||
|
||||
/// Returns the time this event occured. This is on the scale of
|
||||
/// `java.lang.System.nanoTime()`, which has nanosecond precision, but no defined start time.
|
||||
///
|
||||
/// See [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#akeyevent_geteventtime)
|
||||
#[inline]
|
||||
pub fn event_time(&self) -> i64 {
|
||||
self.ndk_event.event_time()
|
||||
}
|
||||
|
||||
/// Returns the keycode associated with this key event
|
||||
///
|
||||
/// See [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#akeyevent_getkeycode)
|
||||
#[inline]
|
||||
pub fn key_code(&self) -> Keycode {
|
||||
self.ndk_event.key_code()
|
||||
}
|
||||
|
||||
/// Returns the number of repeats of a key.
|
||||
///
|
||||
/// See [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#akeyevent_getrepeatcount)
|
||||
#[inline]
|
||||
pub fn repeat_count(&self) -> i32 {
|
||||
self.ndk_event.repeat_count()
|
||||
}
|
||||
|
||||
/// Returns the hardware keycode of a key. This varies from device to device.
|
||||
///
|
||||
/// See [the NDK
|
||||
/// docs](https://developer.android.com/ndk/reference/group/input#akeyevent_getscancode)
|
||||
#[inline]
|
||||
pub fn scan_code(&self) -> i32 {
|
||||
self.ndk_event.scan_code()
|
||||
}
|
||||
}
|
||||
|
||||
// We use our own wrapper type for input events to have better consistency
|
||||
// with GameActivity and ensure the enum can be extended without needing a
|
||||
// semver bump
|
||||
/// Enum of possible input events
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum InputEvent<'a> {
|
||||
MotionEvent(self::MotionEvent<'a>),
|
||||
KeyEvent(self::KeyEvent<'a>),
|
||||
}
|
||||
@@ -17,26 +17,10 @@ use crate::{
|
||||
util, AndroidApp, ConfigurationRef, InputStatus, MainEvent, PollEvent, Rect, WindowManagerFlags,
|
||||
};
|
||||
|
||||
use self::glue::NativeActivityGlue;
|
||||
|
||||
pub mod input {
|
||||
pub use ndk::event::{
|
||||
Axis, ButtonState, EdgeFlags, KeyAction, KeyEvent, KeyEventFlags, Keycode, MetaState,
|
||||
MotionAction, MotionEvent, MotionEventFlags, Pointer, Source,
|
||||
};
|
||||
|
||||
// We use our own wrapper type for input events to have better consistency
|
||||
// with GameActivity and ensure the enum can be extended without needing a
|
||||
// semver bump
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum InputEvent {
|
||||
MotionEvent(self::MotionEvent),
|
||||
KeyEvent(self::KeyEvent),
|
||||
}
|
||||
}
|
||||
pub mod input;
|
||||
|
||||
mod glue;
|
||||
use self::glue::NativeActivityGlue;
|
||||
|
||||
pub const LOOPER_ID_MAIN: libc::c_int = 1;
|
||||
pub const LOOPER_ID_INPUT: libc::c_int = 2;
|
||||
@@ -385,14 +369,22 @@ impl AndroidAppInner {
|
||||
while let Ok(Some(event)) = queue.get_event() {
|
||||
if let Some(ndk_event) = queue.pre_dispatch(event) {
|
||||
let event = match ndk_event {
|
||||
ndk::event::InputEvent::MotionEvent(e) => input::InputEvent::MotionEvent(e),
|
||||
ndk::event::InputEvent::KeyEvent(e) => input::InputEvent::KeyEvent(e),
|
||||
ndk::event::InputEvent::MotionEvent(e) => {
|
||||
input::InputEvent::MotionEvent(input::MotionEvent::new(e))
|
||||
}
|
||||
ndk::event::InputEvent::KeyEvent(e) => {
|
||||
input::InputEvent::KeyEvent(input::KeyEvent::new(e))
|
||||
}
|
||||
};
|
||||
let handled = callback(&event);
|
||||
|
||||
let ndk_event = match event {
|
||||
input::InputEvent::MotionEvent(e) => ndk::event::InputEvent::MotionEvent(e),
|
||||
input::InputEvent::KeyEvent(e) => ndk::event::InputEvent::KeyEvent(e),
|
||||
input::InputEvent::MotionEvent(e) => {
|
||||
ndk::event::InputEvent::MotionEvent(e.into_ndk_event())
|
||||
}
|
||||
input::InputEvent::KeyEvent(e) => {
|
||||
ndk::event::InputEvent::KeyEvent(e.into_ndk_event())
|
||||
}
|
||||
};
|
||||
queue.finish_event(ndk_event, matches!(handled, InputStatus::Handled));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user