From 103bd656a3d4bd0289589f0549440536bab6b052 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 19 Sep 2022 21:36:46 +0100 Subject: [PATCH] Let applications report if they handled input events The callback given to `AndroidApp::input_events()` is now expected to return `InputStatus::Handled` or `InputStatus::Unhandled`. When running with NativeActivity then if we know an input event hasn't been handled we can notify the InputQueue which may result in fallback handling. Although the status is currently ignored with the GameActivity backend. Since this is a breaking change that also affects the current Winit backend this updates the winit based examples to stick with the 0.3 release of android-activity for now. Fixes: #31 --- android-activity/CHANGELOG.md | 3 ++- android-activity/src/game_activity/mod.rs | 6 ++++-- android-activity/src/lib.rs | 14 +++++++++++-- android-activity/src/native_activity/mod.rs | 23 +++++++++++---------- examples/agdk-cpal/src/lib.rs | 3 ++- examples/agdk-eframe/Cargo.lock | 2 ++ examples/agdk-eframe/Cargo.toml | 15 ++++++++++---- examples/agdk-egui/Cargo.lock | 2 ++ examples/agdk-egui/Cargo.toml | 15 ++++++++++---- examples/agdk-mainloop/src/lib.rs | 3 ++- examples/agdk-oboe/src/audio.rs | 7 +++++-- examples/agdk-oboe/src/lib.rs | 3 ++- examples/agdk-winit-wgpu/Cargo.lock | 2 ++ examples/agdk-winit-wgpu/Cargo.toml | 15 ++++++++++---- examples/na-mainloop/src/lib.rs | 3 ++- examples/na-subclass-jni/src/lib.rs | 3 ++- examples/na-winit-wgpu/Cargo.lock | 2 ++ examples/na-winit-wgpu/Cargo.toml | 15 ++++++++++---- 18 files changed, 97 insertions(+), 39 deletions(-) diff --git a/android-activity/CHANGELOG.md b/android-activity/CHANGELOG.md index 34a3071..0aa349c 100644 --- a/android-activity/CHANGELOG.md +++ b/android-activity/CHANGELOG.md @@ -3,7 +3,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] - +### Changed +- *Breaking*: `input_events` callback now return whether an event was handled or not to allow for fallback handling ([#31](https://github.com/rib/android-activity/issues/31)) ## [0.3] - 2022-09-15 ### Added diff --git a/android-activity/src/game_activity/mod.rs b/android-activity/src/game_activity/mod.rs index 47e4912..21755ef 100644 --- a/android-activity/src/game_activity/mod.rs +++ b/android-activity/src/game_activity/mod.rs @@ -23,7 +23,9 @@ use ndk::asset::AssetManager; use ndk::configuration::Configuration; use ndk::native_window::NativeWindow; -use crate::{util, AndroidApp, ConfigurationRef, MainEvent, PollEvent, Rect, WindowManagerFlags}; +use crate::{ + util, AndroidApp, ConfigurationRef, InputStatus, MainEvent, PollEvent, Rect, WindowManagerFlags, +}; mod ffi; @@ -391,7 +393,7 @@ impl AndroidAppInner { pub fn input_events<'b, F>(&self, mut callback: F) where - F: FnMut(&InputEvent), + F: FnMut(&InputEvent) -> InputStatus, { let buf = unsafe { let app_ptr = self.native_app.as_ptr(); diff --git a/android-activity/src/lib.rs b/android-activity/src/lib.rs index 8e559d0..ff6aeb6 100644 --- a/android-activity/src/lib.rs +++ b/android-activity/src/lib.rs @@ -171,6 +171,12 @@ pub enum PollEvent<'a> { Main(MainEvent<'a>), } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum InputStatus { + Handled, + Unhandled, +} + use activity_impl::AndroidAppInner; pub use activity_impl::AndroidAppWaker; @@ -473,6 +479,10 @@ impl AndroidApp { /// Query and process all out-standing input event /// + /// `callback` should return [`InputStatus::Unhandled`] for any input events that aren't directly + /// handled by the application, or else [`InputStatus::Handled`]. Unhandled events may lead to a + /// fallback interpretation of the event. + /// /// Applications are generally either expected to call this in-sync with their rendering or /// in response to a [`MainEvent::InputAvailable`] event being delivered. _Note though that your /// application is will only be delivered a single [`MainEvent::InputAvailable`] event between calls @@ -482,9 +492,9 @@ impl AndroidApp { /// and other axis should be enabled explicitly via [`Self::enable_motion_axis`]. pub fn input_events<'b, F>(&self, callback: F) where - F: FnMut(&input::InputEvent), + F: FnMut(&input::InputEvent) -> InputStatus, { - self.inner.read().unwrap().input_events(callback); + self.inner.read().unwrap().input_events(callback) } /// The user-visible SDK version of the framework diff --git a/android-activity/src/native_activity/mod.rs b/android-activity/src/native_activity/mod.rs index af2cbf2..4456001 100644 --- a/android-activity/src/native_activity/mod.rs +++ b/android-activity/src/native_activity/mod.rs @@ -21,7 +21,9 @@ use ndk::configuration::Configuration; use ndk::input_queue::InputQueue; use ndk::native_window::NativeWindow; -use crate::{util, AndroidApp, ConfigurationRef, MainEvent, PollEvent, Rect, WindowManagerFlags}; +use crate::{ + util, AndroidApp, ConfigurationRef, InputStatus, MainEvent, PollEvent, Rect, WindowManagerFlags, +}; mod ffi; @@ -415,7 +417,7 @@ impl AndroidAppInner { pub fn input_events<'b, F>(&self, mut callback: F) where - F: FnMut(&input::InputEvent), + F: FnMut(&input::InputEvent) -> InputStatus, { let queue = unsafe { let app_ptr = self.native_app.as_ptr(); @@ -444,20 +446,19 @@ impl AndroidAppInner { ndk::event::InputEvent::MotionEvent(e) => input::InputEvent::MotionEvent(e), ndk::event::InputEvent::KeyEvent(e) => input::InputEvent::KeyEvent(e), }; - callback(&event); + 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), }; - - // Always report events as 'handled'. This means we won't get - // so called 'fallback' events generated (such as converting trackball - // events into emulated keypad events), but we could conceivably - // implement similar emulation somewhere else in the stack if - // necessary, and this will be more consistent with the GameActivity - // input handling that doesn't do any kind of emulation. - queue.finish_event(ndk_event, true); + queue.finish_event( + ndk_event, + match handled { + InputStatus::Handled => true, + _ => false, + }, + ); } } } diff --git a/examples/agdk-cpal/src/lib.rs b/examples/agdk-cpal/src/lib.rs index 886aac3..b69511d 100644 --- a/examples/agdk-cpal/src/lib.rs +++ b/examples/agdk-cpal/src/lib.rs @@ -1,4 +1,4 @@ -use android_activity::{AndroidApp, MainEvent, PollEvent}; +use android_activity::{AndroidApp, InputStatus, MainEvent, PollEvent}; use log::info; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; @@ -131,6 +131,7 @@ fn android_main(app: AndroidApp) { // Handle input app.input_events(|event| { info!("Input Event: {event:?}"); + InputStatus::Unhandled }); info!("Render..."); diff --git a/examples/agdk-eframe/Cargo.lock b/examples/agdk-eframe/Cargo.lock index 006cd6b..57e4e63 100644 --- a/examples/agdk-eframe/Cargo.lock +++ b/examples/agdk-eframe/Cargo.lock @@ -75,6 +75,8 @@ dependencies = [ [[package]] name = "android-activity" version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe057899cd59c5254279b74382bf8132d683f8619ea50592c096710d65d03902" dependencies = [ "android-properties", "bitflags", diff --git a/examples/agdk-eframe/Cargo.toml b/examples/agdk-eframe/Cargo.toml index 4787542..eb1a040 100644 --- a/examples/agdk-eframe/Cargo.toml +++ b/examples/agdk-eframe/Cargo.toml @@ -38,10 +38,17 @@ winit = { git = "https://github.com/rib/winit", branch = "android-activity" } # entrypoint for a native Rust application there can only be a single # implementation of the crate linked with the application. # -# Since Winit also depends on android-activity (version = "0.2") but we'd like -# to build against the local version of android-activity in this repo then we -# use a [patch] to ensure we only end up with a single implementation. -android-activity = { path = "../../android-activity" } +# By default the Winit-based examples use released versions of android-activity +# to help keep the version in sync with the Winit backend. +# +# If you'd like to build this example against the local checkout of +# android-activity you should specify a patch here to make sure you also affect +# the version that Winit uses. +# +# Note: also check that the local android-activity/Cargo.toml version matches +# the android-activity version that Winit depends on (in case you need to check +# out a release branch locally to be compatible) +#android-activity = { path = "../../android-activity" } # Egui 0.19 is missing some fixes for Android so we need to build against # git master for now diff --git a/examples/agdk-egui/Cargo.lock b/examples/agdk-egui/Cargo.lock index 52352ec..bd3b6dc 100644 --- a/examples/agdk-egui/Cargo.lock +++ b/examples/agdk-egui/Cargo.lock @@ -76,6 +76,8 @@ dependencies = [ [[package]] name = "android-activity" version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe057899cd59c5254279b74382bf8132d683f8619ea50592c096710d65d03902" dependencies = [ "android-properties", "bitflags", diff --git a/examples/agdk-egui/Cargo.toml b/examples/agdk-egui/Cargo.toml index 52303d1..9d4637d 100644 --- a/examples/agdk-egui/Cargo.toml +++ b/examples/agdk-egui/Cargo.toml @@ -39,10 +39,17 @@ winit = { git = "https://github.com/rib/winit", branch = "android-activity" } # entrypoint for a native Rust application there can only be a single # implementation of the crate linked with the application. # -# Since Winit also depends on android-activity (version = "0.2") but we'd like -# to build against the local version of android-activity in this repo then we -# use a [patch] to ensure we only end up with a single implementation. -android-activity = { path = "../../android-activity" } +# By default the Winit-based examples use released versions of android-activity +# to help keep the version in sync with the Winit backend. +# +# If you'd like to build this example against the local checkout of +# android-activity you should specify a patch here to make sure you also affect +# the version that Winit uses. +# +# Note: also check that the local android-activity/Cargo.toml version matches +# the android-activity version that Winit depends on (in case you need to check +# out a release branch locally to be compatible) +#android-activity = { path = "../../android-activity" } [features] default = [] diff --git a/examples/agdk-mainloop/src/lib.rs b/examples/agdk-mainloop/src/lib.rs index c45f186..b043f87 100644 --- a/examples/agdk-mainloop/src/lib.rs +++ b/examples/agdk-mainloop/src/lib.rs @@ -1,4 +1,4 @@ -use android_activity::{AndroidApp, MainEvent, PollEvent}; +use android_activity::{AndroidApp, InputStatus, MainEvent, PollEvent}; use log::info; #[no_mangle] @@ -71,6 +71,7 @@ fn android_main(app: AndroidApp) { // Handle input app.input_events(|event| { info!("Input Event: {event:?}"); + InputStatus::Unhandled }); info!("Render..."); diff --git a/examples/agdk-oboe/src/audio.rs b/examples/agdk-oboe/src/audio.rs index 9cc7f34..c2a0571 100644 --- a/examples/agdk-oboe/src/audio.rs +++ b/examples/agdk-oboe/src/audio.rs @@ -43,6 +43,7 @@ impl SineGen { } /// Pause audio stream + #[allow(dead_code)] pub fn try_pause(&mut self) { if let Some(stream) = &mut self.stream { log::debug!("pause stream: {:?}", stream); @@ -92,6 +93,7 @@ impl SineParam { ); } + #[allow(dead_code)] fn set_frequency(&self, frequency: f32) { let sample_rate = self.sample_rate.load(Ordering::Relaxed); let delta = frequency * 2.0 * PI / sample_rate; @@ -100,6 +102,7 @@ impl SineParam { self.frequency.store(frequency, Ordering::Relaxed); } + #[allow(dead_code)] fn set_gain(&self, gain: f32) { self.gain.store(gain, Ordering::Relaxed); } @@ -151,7 +154,7 @@ impl AudioOutputCallback for SineWave { fn on_audio_ready( &mut self, - stream: &mut dyn AudioOutputStreamSafe, + _stream: &mut dyn AudioOutputStreamSafe, frames: &mut [f32], ) -> DataCallbackResult { for frame in frames { @@ -166,7 +169,7 @@ impl AudioOutputCallback for SineWave { fn on_audio_ready( &mut self, - stream: &mut dyn AudioOutputStreamSafe, + _stream: &mut dyn AudioOutputStreamSafe, frames: &mut [(f32, f32)], ) -> DataCallbackResult { for frame in frames { diff --git a/examples/agdk-oboe/src/lib.rs b/examples/agdk-oboe/src/lib.rs index d03d044..ee48149 100644 --- a/examples/agdk-oboe/src/lib.rs +++ b/examples/agdk-oboe/src/lib.rs @@ -1,4 +1,4 @@ -use android_activity::{AndroidApp, MainEvent, PollEvent}; +use android_activity::{AndroidApp, MainEvent, PollEvent, InputStatus}; use log::info; mod audio; @@ -75,6 +75,7 @@ fn android_main(app: AndroidApp) { // Handle input app.input_events(|event| { info!("Input Event: {event:?}"); + InputStatus::Unhandled }); info!("Render..."); diff --git a/examples/agdk-winit-wgpu/Cargo.lock b/examples/agdk-winit-wgpu/Cargo.lock index 4829f0f..9587df9 100644 --- a/examples/agdk-winit-wgpu/Cargo.lock +++ b/examples/agdk-winit-wgpu/Cargo.lock @@ -60,6 +60,8 @@ dependencies = [ [[package]] name = "android-activity" version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe057899cd59c5254279b74382bf8132d683f8619ea50592c096710d65d03902" dependencies = [ "android-properties", "bitflags", diff --git a/examples/agdk-winit-wgpu/Cargo.toml b/examples/agdk-winit-wgpu/Cargo.toml index ed8932e..e11b286 100644 --- a/examples/agdk-winit-wgpu/Cargo.toml +++ b/examples/agdk-winit-wgpu/Cargo.toml @@ -34,10 +34,17 @@ winit = { git = "https://github.com/rib/winit", branch = "android-activity-0.27" # entrypoint for a native Rust application there can only be a single # implementation of the crate linked with the application. # -# Since Winit also depends on android-activity (version = "0.2") but we'd like -# to build against the local version of android-activity in this repo then we -# use a [patch] to ensure we only end up with a single implementation. -android-activity = { path="../../android-activity" } +# By default the Winit-based examples use released versions of android-activity +# to help keep the version in sync with the Winit backend. +# +# If you'd like to build this example against the local checkout of +# android-activity you should specify a patch here to make sure you also affect +# the version that Winit uses. +# +# Note: also check that the local android-activity/Cargo.toml version matches +# the android-activity version that Winit depends on (in case you need to check +# out a release branch locally to be compatible) +#android-activity = { path = "../../android-activity" } [features] default = [] diff --git a/examples/na-mainloop/src/lib.rs b/examples/na-mainloop/src/lib.rs index c45f186..ef8e4cc 100644 --- a/examples/na-mainloop/src/lib.rs +++ b/examples/na-mainloop/src/lib.rs @@ -1,4 +1,4 @@ -use android_activity::{AndroidApp, MainEvent, PollEvent}; +use android_activity::{AndroidApp, MainEvent, PollEvent, InputStatus}; use log::info; #[no_mangle] @@ -71,6 +71,7 @@ fn android_main(app: AndroidApp) { // Handle input app.input_events(|event| { info!("Input Event: {event:?}"); + InputStatus::Unhandled }); info!("Render..."); diff --git a/examples/na-subclass-jni/src/lib.rs b/examples/na-subclass-jni/src/lib.rs index 95fcca3..748249c 100644 --- a/examples/na-subclass-jni/src/lib.rs +++ b/examples/na-subclass-jni/src/lib.rs @@ -1,4 +1,4 @@ -use android_activity::{AndroidApp, MainEvent, PollEvent}; +use android_activity::{AndroidApp, MainEvent, PollEvent, InputStatus}; use log::Level; use log::{info, trace}; use serde::{Deserialize, Serialize}; @@ -77,6 +77,7 @@ fn android_main(app: AndroidApp) { // Handle input app.input_events(|event| { info!("Input Event: {event:?}"); + InputStatus::Unhandled }); // Render... diff --git a/examples/na-winit-wgpu/Cargo.lock b/examples/na-winit-wgpu/Cargo.lock index 1cae06a..bf89815 100644 --- a/examples/na-winit-wgpu/Cargo.lock +++ b/examples/na-winit-wgpu/Cargo.lock @@ -47,6 +47,8 @@ dependencies = [ [[package]] name = "android-activity" version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe057899cd59c5254279b74382bf8132d683f8619ea50592c096710d65d03902" dependencies = [ "android-properties", "bitflags", diff --git a/examples/na-winit-wgpu/Cargo.toml b/examples/na-winit-wgpu/Cargo.toml index cf29ef6..d778f68 100644 --- a/examples/na-winit-wgpu/Cargo.toml +++ b/examples/na-winit-wgpu/Cargo.toml @@ -34,10 +34,17 @@ winit = { git = "https://github.com/rib/winit", branch = "android-activity-0.27" # entrypoint for a native Rust application there can only be a single # implementation of the crate linked with the application. # -# Since Winit also depends on android-activity (version = "0.2") but we'd like -# to build against the local version of android-activity in this repo then we -# use a [patch] to ensure we only end up with a single implementation. -android-activity = { path="../../android-activity" } +# By default the Winit-based examples use released versions of android-activity +# to help keep the version in sync with the Winit backend. +# +# If you'd like to build this example against the local checkout of +# android-activity you should specify a patch here to make sure you also affect +# the version that Winit uses. +# +# Note: also check that the local android-activity/Cargo.toml version matches +# the android-activity version that Winit depends on (in case you need to check +# out a release branch locally to be compatible) +#android-activity = { path = "../../android-activity" } [features] default = []