From 969ba5adf9fb0670035c2841a8ee964c58c58ac0 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sun, 15 Oct 2023 17:42:29 +0100 Subject: [PATCH] Improve forwards compatibility of input API This adds a `#[doc(hidden)]` `__Unknown(u32)` variant to the various enums to keep them extensible without requiring API breaks. We need to consider that most enums that are based on Android SDK enums may be extended across different versions of Android (i.e. effectively at runtime) or extended in new versions of `android-activity` when we pull in the latest NDK/SDK constants. In particular in the case that there is some unknown variant we at least want to be able to preserve the integer value to allow the values to be either passed back into the SDK (it doesn't always matter whether we know the semantics of a variant at compile time) or passed on to something downstream that could be independently updated to know the semantics. We don't want it to be an API break to extend these enums in future releases of `android-activity`. It's not enough to rely on `#[non-exhaustive]` because that only really helps when adding new variants in sync with android-activity releases. On the other hand we also can't rely on a catch-all `Unknown(u32)` that only really helps with unknown variants seen at runtime. (If code were to have an exhaustive match that would include matching on `Unknown(_)` values then they wouldn't be compatible with new versions of android-activity that would promote unknown values to known ones). What we aim for instead is to have a hidden catch-all variant that is considered (practically) unmatchable so code is forced to have a `unknown => {}` catch-all pattern match that will cover unknown variants either in the form of Rust variants added in future versions or in the form of an `__Unknown(u32)` integer that represents an unknown variant seen at runtime. Any `unknown => {}` pattern match can rely on `IntoPrimitive` to convert the `unknown` variant to the integer that comes from the Android SDK in case that values needs to be passed on, even without knowing it's semantic meaning at compile time. Instead of adding an `__Unknown(u32)` variant to the `Class` enum though this enum has been removed in favour of adding methods like `is_button_class()` and `is_pointer_class()` to the `Source` type, since the class flags aren't guaranteed to be mutually exclusive and since they are an attribute of the `Source`. This removes some reliance `try_into().unwrap()` that was put in place anticipating that we would support `into()` via `num_enum`, once we could update our rust-version. --- android-activity/src/game_activity/input.rs | 37 +--- android-activity/src/game_activity/mod.rs | 2 + android-activity/src/input.rs | 200 ++++++++++++------ android-activity/src/input/sdk.rs | 48 ++--- android-activity/src/lib.rs | 43 ++++ android-activity/src/native_activity/input.rs | 60 +++--- 6 files changed, 244 insertions(+), 146 deletions(-) diff --git a/android-activity/src/game_activity/input.rs b/android-activity/src/game_activity/input.rs index 957e24b..2f53612 100644 --- a/android-activity/src/game_activity/input.rs +++ b/android-activity/src/game_activity/input.rs @@ -13,12 +13,10 @@ // The `Class` was also bound differently to `android-ndk-rs` considering how the class is defined // by masking bits from the `Source`. -use std::convert::TryInto; - use crate::activity_impl::ffi::{GameActivityKeyEvent, GameActivityMotionEvent}; use crate::input::{ - Axis, ButtonState, Class, EdgeFlags, KeyAction, KeyEventFlags, Keycode, MetaState, - MotionAction, MotionEventFlags, Pointer, PointersIter, Source, ToolType, + Axis, ButtonState, EdgeFlags, KeyAction, KeyEventFlags, Keycode, MetaState, MotionAction, + MotionEventFlags, Pointer, PointersIter, Source, ToolType, }; // Note: try to keep this wrapper API compatible with the AInputEvent API if possible @@ -50,14 +48,7 @@ impl<'a> MotionEvent<'a> { #[inline] pub fn source(&self) -> Source { let source = self.ga_event.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()) + source.into() } /// Get the device id associated with the event. @@ -73,7 +64,7 @@ impl<'a> MotionEvent<'a> { #[inline] pub fn action(&self) -> MotionAction { let action = self.ga_event.action as u32 & ndk_sys::AMOTION_EVENT_ACTION_MASK; - action.try_into().unwrap() + action.into() } /// Returns the pointer index of an `Up` or `Down` event. @@ -275,7 +266,8 @@ impl<'a> PointerImpl<'a> { #[inline] pub fn axis_value(&self, axis: Axis) -> f32 { let pointer = &self.event.ga_event.pointers[self.index]; - pointer.axisValues[axis as u32 as usize] + let axis: u32 = axis.into(); + pointer.axisValues[axis as usize] } #[inline] @@ -294,7 +286,7 @@ impl<'a> PointerImpl<'a> { pub fn tool_type(&self) -> ToolType { let pointer = &self.event.ga_event.pointers[self.index]; let tool_type = pointer.toolType as u32; - tool_type.try_into().unwrap() + tool_type.into() } } @@ -662,14 +654,7 @@ impl<'a> KeyEvent<'a> { #[inline] pub fn source(&self) -> Source { let source = self.ga_event.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()) + source.into() } /// Get the device id associated with the event. @@ -685,13 +670,13 @@ impl<'a> KeyEvent<'a> { #[inline] pub fn action(&self) -> KeyAction { let action = self.ga_event.action as u32; - action.try_into().unwrap() + action.into() } #[inline] pub fn action_button(&self) -> KeyAction { let action = self.ga_event.action as u32; - action.try_into().unwrap() + action.into() } /// Returns the last time the key was pressed. This is on the scale of @@ -721,7 +706,7 @@ impl<'a> KeyEvent<'a> { #[inline] pub fn key_code(&self) -> Keycode { let keycode = self.ga_event.keyCode as u32; - keycode.try_into().unwrap_or(Keycode::Unknown) + keycode.into() } /// Returns the number of repeats of a key. diff --git a/android-activity/src/game_activity/mod.rs b/android-activity/src/game_activity/mod.rs index 5809e9b..f9904d1 100644 --- a/android-activity/src/game_activity/mod.rs +++ b/android-activity/src/game_activity/mod.rs @@ -548,10 +548,12 @@ impl AndroidAppInner { } pub fn enable_motion_axis(&mut self, axis: Axis) { + let axis: u32 = axis.into(); unsafe { ffi::GameActivityPointerAxes_enableAxis(axis as i32) } } pub fn disable_motion_axis(&mut self, axis: Axis) { + let axis: u32 = axis.into(); unsafe { ffi::GameActivityPointerAxes_disableAxis(axis as i32) } } diff --git a/android-activity/src/input.rs b/android-activity/src/input.rs index 55390f8..95fe17a 100644 --- a/android-activity/src/input.rs +++ b/android-activity/src/input.rs @@ -1,5 +1,4 @@ use bitflags::bitflags; -use num_enum::{IntoPrimitive, TryFromPrimitive}; pub use crate::activity_impl::input::*; use crate::InputStatus; @@ -10,7 +9,17 @@ pub use sdk::*; /// 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)] +/// +/// # Android Extensible Enum +/// +/// This is a runtime [extensible enum](`crate#android-extensible-enums`) and +/// should be handled similar to a `#[non_exhaustive]` enum to maintain +/// forwards compatibility. +/// +/// This implements `Into` and `From` for converting to/from Android +/// SDK integer values. +/// +#[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive, num_enum::IntoPrimitive)] #[repr(u32)] pub enum Source { BluetoothStylus = 0x0000c002, @@ -36,9 +45,36 @@ pub enum Source { TouchNavigation = 0x00200000, Trackball = 0x00010004, - Unknown = 0, -} - + // We need to consider that the enum variants may be extended across + // different versions of Android (i.e. effectively at runtime) but at the + // same time we don't want it to be an API break to extend this enum in + // future releases of `android-activity` with new variants from the latest + // NDK/SDK. + // + // We can't just use `#[non_exhaustive]` because that only really helps + // when adding new variants in sync with android-activity releases. + // + // On the other hand we also can't rely on a catch-all `Unknown(u32)` that + // only really helps with unknown variants seen at runtime. + // + // What we aim for instead is to have a hidden catch-all variant that + // is considered (practically) unmatchable so code is forced to have + // a `unknown => {}` catch-all pattern match that will cover unknown variants + // either in the form of Rust variants added in future versions or + // in the form of an `__Unknown(u32)` integer that represents an unknown + // variant seen at runtime. + // + // Any `unknown => {}` pattern match can rely on `IntoPrimitive` to convert + // the `unknown` variant to the integer that comes from the Android SDK + // in case that values needs to be passed on, even without knowing its + // semantic meaning at compile time. + #[doc(hidden)] + #[num_enum(catch_all)] + __Unknown(u32), +} + +// ndk_sys doesn't currently have the `TRACKBALL` flag so we define our +// own internal class constants for now bitflags! { #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] struct SourceFlags: u32 { @@ -53,37 +89,31 @@ bitflags! { } } -/// 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 for Class { - fn from(source: u32) -> Self { - let class = SourceFlags::from_bits_truncate(source); - 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 Source { + #[inline] + pub fn is_button_class(self) -> bool { + let class = SourceFlags::from_bits_truncate(self.into()); + class.contains(SourceFlags::BUTTON) } -} - -impl From for Class { - fn from(source: Source) -> Self { - let source: u32 = source.into(); - source.into() + #[inline] + pub fn is_pointer_class(self) -> bool { + let class = SourceFlags::from_bits_truncate(self.into()); + class.contains(SourceFlags::POINTER) + } + #[inline] + pub fn is_trackball_class(self) -> bool { + let class = SourceFlags::from_bits_truncate(self.into()); + class.contains(SourceFlags::TRACKBALL) + } + #[inline] + pub fn is_position_class(self) -> bool { + let class = SourceFlags::from_bits_truncate(self.into()); + class.contains(SourceFlags::POSITION) + } + #[inline] + pub fn is_joystick_class(self) -> bool { + let class = SourceFlags::from_bits_truncate(self.into()); + class.contains(SourceFlags::JOYSTICK) } } @@ -174,7 +204,17 @@ impl From for MetaState { /// /// See [the NDK /// docs](https://developer.android.com/ndk/reference/group/input#anonymous-enum-29) -#[derive(Copy, Clone, Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] +/// +/// # Android Extensible Enum +/// +/// This is a runtime [extensible enum](`crate#android-extensible-enums`) and +/// should be handled similar to a `#[non_exhaustive]` enum to maintain +/// forwards compatibility. +/// +/// This implements `Into` and `From` for converting to/from Android +/// SDK integer values. +/// +#[derive(Copy, Clone, Debug, PartialEq, Eq, num_enum::FromPrimitive, num_enum::IntoPrimitive)] #[repr(u32)] pub enum MotionAction { Down = ndk_sys::AMOTION_EVENT_ACTION_DOWN, @@ -190,19 +230,26 @@ pub enum MotionAction { HoverExit = ndk_sys::AMOTION_EVENT_ACTION_HOVER_EXIT, ButtonPress = ndk_sys::AMOTION_EVENT_ACTION_BUTTON_PRESS, ButtonRelease = ndk_sys::AMOTION_EVENT_ACTION_BUTTON_RELEASE, -} -impl From for MotionAction { - fn from(value: ndk::event::MotionAction) -> Self { - let inner: u32 = value.into(); - inner.try_into().unwrap() // TODO: use into() when we bump the MSRV to 1.68 - } + #[doc(hidden)] + #[num_enum(catch_all)] + __Unknown(u32), } /// An axis of a motion event. /// /// See [the NDK docs](https://developer.android.com/ndk/reference/group/input#anonymous-enum-32) -#[derive(Copy, Clone, Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] +/// +/// # Android Extensible Enum +/// +/// This is a runtime [extensible enum](`crate#android-extensible-enums`) and +/// should be handled similar to a `#[non_exhaustive]` enum to maintain +/// forwards compatibility. +/// +/// This implements `Into` and `From` for converting to/from Android +/// SDK integer values. +/// +#[derive(Copy, Clone, Debug, PartialEq, Eq, num_enum::FromPrimitive, num_enum::IntoPrimitive)] #[repr(u32)] pub enum Axis { X = ndk_sys::AMOTION_EVENT_AXIS_X, @@ -250,19 +297,26 @@ pub enum Axis { Generic14 = ndk_sys::AMOTION_EVENT_AXIS_GENERIC_14, Generic15 = ndk_sys::AMOTION_EVENT_AXIS_GENERIC_15, Generic16 = ndk_sys::AMOTION_EVENT_AXIS_GENERIC_16, -} -impl From for Axis { - fn from(value: ndk::event::Axis) -> Self { - let inner: u32 = value.into(); - inner.try_into().unwrap() // TODO: replace with into() when we can bump MSRV to 1.68! - } + #[doc(hidden)] + #[num_enum(catch_all)] + __Unknown(u32), } /// The tool type of a pointer. /// /// See [the NDK docs](https://developer.android.com/ndk/reference/group/input#anonymous-enum-48) -#[derive(Copy, Clone, Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] +/// +/// # Android Extensible Enum +/// +/// This is a runtime [extensible enum](`crate#android-extensible-enums`) and +/// should be handled similar to a `#[non_exhaustive]` enum to maintain +/// forwards compatibility. +/// +/// Implements `Into` and `From` for converting to/from Android SDK +/// integer values. +/// +#[derive(Copy, Clone, Debug, PartialEq, Eq, num_enum::FromPrimitive, num_enum::IntoPrimitive)] #[repr(u32)] pub enum ToolType { /// Unknown tool type. @@ -284,6 +338,10 @@ pub enum ToolType { /// The tool is a palm and should be rejected Palm = ndk_sys::AMOTION_EVENT_TOOL_TYPE_PALM, + + #[doc(hidden)] + #[num_enum(catch_all)] + __Unknown(u32), } /// A bitfield representing the state of buttons during a motion event. @@ -382,25 +440,42 @@ impl From for MotionEventFlags { /// Key actions. /// /// See [the NDK docs](https://developer.android.com/ndk/reference/group/input#anonymous-enum-27) -#[derive(Copy, Clone, Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] +/// +/// # Android Extensible Enum +/// +/// This is a runtime [extensible enum](`crate#android-extensible-enums`) and +/// should be handled similar to a `#[non_exhaustive]` enum to maintain +/// forwards compatibility. +/// +/// Implements `Into` and `From` for converting to/from Android SDK +/// integer values. +/// +#[derive(Copy, Clone, Debug, PartialEq, Eq, num_enum::FromPrimitive, num_enum::IntoPrimitive)] #[repr(u32)] pub enum KeyAction { Down = ndk_sys::AKEY_EVENT_ACTION_DOWN, Up = ndk_sys::AKEY_EVENT_ACTION_UP, Multiple = ndk_sys::AKEY_EVENT_ACTION_MULTIPLE, -} -impl From for KeyAction { - fn from(value: ndk::event::KeyAction) -> Self { - let inner: u32 = value.into(); - inner.try_into().unwrap() // TODO: replace with into() when we can bump MSRV to 1.68! - } + #[doc(hidden)] + #[num_enum(catch_all)] + __Unknown(u32), } /// Key codes. /// /// See [the NDK docs](https://developer.android.com/ndk/reference/group/input#anonymous-enum-39) -#[derive(Copy, Clone, Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] +/// +/// # Android Extensible Enum +/// +/// This is a runtime [extensible enum](`crate#android-extensible-enums`) and +/// should be handled similar to a `#[non_exhaustive]` enum to maintain +/// forwards compatibility. +/// +/// Implements `Into` and `From` for converting to/from Android SDK +/// integer values. +/// +#[derive(Copy, Clone, Debug, PartialEq, Eq, num_enum::FromPrimitive, num_enum::IntoPrimitive)] #[repr(u32)] pub enum Keycode { Unknown = ndk_sys::AKEYCODE_UNKNOWN, @@ -692,13 +767,10 @@ pub enum Keycode { ThumbsUp = ndk_sys::AKEYCODE_THUMBS_UP, ThumbsDown = ndk_sys::AKEYCODE_THUMBS_DOWN, ProfileSwitch = ndk_sys::AKEYCODE_PROFILE_SWITCH, -} -impl From for Keycode { - fn from(value: ndk::event::Keycode) -> Self { - let inner: u32 = value.into(); - inner.try_into().unwrap() // TODO: replace with into() when we can bump MSRV to 1.68! - } + #[doc(hidden)] + #[num_enum(catch_all)] + __Unknown(u32), } /// Flags associated with [`KeyEvent`]. diff --git a/android-activity/src/input/sdk.rs b/android-activity/src/input/sdk.rs index f03a41b..ad4517d 100644 --- a/android-activity/src/input/sdk.rs +++ b/android-activity/src/input/sdk.rs @@ -17,7 +17,22 @@ use crate::{ jni_utils, }; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +/// An enum representing the types of keyboards that may generate key events +/// +/// See [getKeyboardType() docs](https://developer.android.com/reference/android/view/KeyCharacterMap#getKeyboardType()) +/// +/// # Android Extensible Enum +/// +/// This is a runtime [extensible enum](`crate#android-extensible-enums`) and +/// should be handled similar to a `#[non_exhaustive]` enum to maintain +/// forwards compatibility. +/// +/// This implements `Into` and `From` for converting to/from Android +/// SDK integer values. +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, num_enum::FromPrimitive, num_enum::IntoPrimitive, +)] +#[repr(u32)] pub enum KeyboardType { /// A numeric (12-key) keyboard. /// @@ -50,33 +65,9 @@ pub enum KeyboardType { /// A special function keyboard consists only of non-printing keys such as HOME and POWER that are not actually used for typing. SpecialFunction, - /// An unknown type of keyboard - Unknown(i32), -} - -impl From for KeyboardType { - fn from(value: i32) -> Self { - match value { - 1 => KeyboardType::Numeric, - 2 => KeyboardType::Predictive, - 3 => KeyboardType::Alpha, - 4 => KeyboardType::Full, - 5 => KeyboardType::SpecialFunction, - unknown => KeyboardType::Unknown(unknown), - } - } -} -impl From for i32 { - fn from(value: KeyboardType) -> i32 { - match value { - KeyboardType::Numeric => 1, - KeyboardType::Predictive => 2, - KeyboardType::Alpha => 3, - KeyboardType::Full => 4, - KeyboardType::SpecialFunction => 5, - KeyboardType::Unknown(unknown) => unknown, - } - } + #[doc(hidden)] + #[num_enum(catch_all)] + __Unknown(u32), } /// Either represents, a unicode character or combining accent from a @@ -353,6 +344,7 @@ impl KeyCharacterMap { let keyboard_type = self .binding .get_keyboard_type(&mut env, self.key_map.as_obj())?; + let keyboard_type = keyboard_type as u32; Ok(keyboard_type.into()) } } diff --git a/android-activity/src/lib.rs b/android-activity/src/lib.rs index cbbe7d2..448fc57 100644 --- a/android-activity/src/lib.rs +++ b/android-activity/src/lib.rs @@ -51,6 +51,49 @@ //! //! These are undone after `android_main()` returns //! +//! # Android Extensible Enums +//! +//! There are numerous enums in the `android-activity` API which are effectively +//! bindings to enums declared in the Android SDK which need to be considered +//! _runtime_ extensible. +//! +//! Any enum variants that come from the Android SDK may be extended in future +//! versions of Android and your code could be exposed to new variants if you +//! build an application that might be installed on new versions of Android. +//! +//! This crate follows a convention of adding a hidden `__Unknown(u32)` variant +//! to these enum to ensure we can always do lossless conversions between the +//! integers from the SDK and our corresponding Rust enums. This can be +//! important in case you need to pass certain variants back to the SDK +//! regardless of whether you knew about that variants specific semantics at +//! compile time. +//! +//! You should never include this `__Unknown(u32)` variant within any exhaustive +//! pattern match and should instead treat the enums like `#[non_exhaustive]` +//! enums that require you to add a catch-all for any `unknown => {}` values. +//! +//! Any code that would exhaustively include the `__Unknown(u32)` variant when +//! pattern matching can not be guaranteed to be forwards compatible with new +//! releases of `android-activity` which may add new Rust variants to these +//! enums without requiring a breaking semver bump. +//! +//! You can (infallibly) convert these enums to and from primitive `u32` values +//! using `.into()`: +//! +//! For example, here is how you could ensure forwards compatibility with both +//! compile-time and runtime extensions of a `SomeEnum` enum: +//! +//! ```rust +//! match some_enum { +//! SomeEnum::Foo => {}, +//! SomeEnum::Bar => {}, +//! unhandled => { +//! let sdk_val: u32 = unhandled.into(); +//! println!("Unhandled enum variant {some_enum:?} has SDK value: {sdk_val}"); +//! } +//! } +//! ``` +//! //! [`Activity`]: https://developer.android.com/reference/android/app/Activity //! [`NativeActivity`]: https://developer.android.com/reference/android/app/NativeActivity //! [ndk_concepts]: https://developer.android.com/ndk/guides/concepts#naa diff --git a/android-activity/src/native_activity/input.rs b/android-activity/src/native_activity/input.rs index 8a2d0b2..ddf9928 100644 --- a/android-activity/src/native_activity/input.rs +++ b/android-activity/src/native_activity/input.rs @@ -1,8 +1,8 @@ use std::marker::PhantomData; use crate::input::{ - Axis, ButtonState, Class, EdgeFlags, KeyAction, Keycode, MetaState, MotionAction, - MotionEventFlags, Pointer, PointersIter, Source, ToolType, + Axis, ButtonState, EdgeFlags, KeyAction, Keycode, MetaState, MotionAction, MotionEventFlags, + Pointer, PointersIter, Source, ToolType, }; /// A motion event @@ -32,18 +32,11 @@ impl<'a> MotionEvent<'a> { 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. + // share between backends, which may also capture unknown variants + // added in new versions of Android. 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()) + source.into() } /// Get the device id associated with the event. @@ -58,7 +51,11 @@ impl<'a> MotionEvent<'a> { /// See [the MotionEvent docs](https://developer.android.com/reference/android/view/MotionEvent#getActionMasked()) #[inline] pub fn action(&self) -> MotionAction { - self.ndk_event.action().into() + // XXX: we use `AInputEvent_getAction` directly since we have our own + // `KeyAction` enum that we share between backends, which may also + // capture unknown variants added in new versions of Android. + let action = unsafe { ndk_sys::AKeyEvent_getAction(self.ndk_event.ptr().as_ptr()) as u32 }; + action.into() } /// Returns the pointer index of an `Up` or `Down` event. @@ -250,8 +247,13 @@ impl<'a> PointerImpl<'a> { #[inline] pub fn axis_value(&self, axis: Axis) -> f32 { let value: u32 = axis.into(); - let ndk_axis = value.try_into().unwrap(); - self.ndk_pointer.axis_value(ndk_axis) + if let Ok(ndk_axis) = value.try_into() { + self.ndk_pointer.axis_value(ndk_axis) + } else { + // FIXME: We should also be able to query `Axis::__Unknown(u32)` values + // that can't currently be queried via the `ndk` `Pointer` API + 0.0f32 + } } #[inline] @@ -267,7 +269,7 @@ impl<'a> PointerImpl<'a> { #[inline] pub fn tool_type(&self) -> ToolType { let value: u32 = self.ndk_pointer.tool_type().into(); - value.try_into().unwrap() + value.into() } } @@ -323,18 +325,11 @@ impl<'a> KeyEvent<'a> { 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. + // share between backends, which may also capture unknown variants + // added in new versions of Android. 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()) + source.into() } /// Get the device id associated with the event. @@ -349,7 +344,11 @@ impl<'a> KeyEvent<'a> { /// See [the KeyEvent docs](https://developer.android.com/reference/android/view/KeyEvent#getAction()) #[inline] pub fn action(&self) -> KeyAction { - self.ndk_event.action().into() + // XXX: we use `AInputEvent_getAction` directly since we have our own + // `KeyAction` enum that we share between backends, which may also + // capture unknown variants added in new versions of Android. + let action = unsafe { ndk_sys::AKeyEvent_getAction(self.ndk_event.ptr().as_ptr()) as u32 }; + action.into() } /// Returns the last time the key was pressed. This is on the scale of @@ -378,7 +377,12 @@ impl<'a> KeyEvent<'a> { /// docs](https://developer.android.com/ndk/reference/group/input#akeyevent_getkeycode) #[inline] pub fn key_code(&self) -> Keycode { - self.ndk_event.key_code().into() + // XXX: we use `AInputEvent_getKeyCode` directly since we have our own + // `Keycode` enum that we share between backends, which may also + // capture unknown variants added in new versions of Android. + let keycode = + unsafe { ndk_sys::AKeyEvent_getKeyCode(self.ndk_event.ptr().as_ptr()) as u32 }; + keycode.into() } /// Returns the number of repeats of a key.