Skip to content

Commit

Permalink
Let applications report if they handled input events
Browse files Browse the repository at this point in the history
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
  • Loading branch information
rib committed Sep 19, 2022
1 parent 6245866 commit 103bd65
Show file tree
Hide file tree
Showing 18 changed files with 97 additions and 39 deletions.
3 changes: 2 additions & 1 deletion android-activity/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions android-activity/src/game_activity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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();
Expand Down
14 changes: 12 additions & 2 deletions android-activity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
23 changes: 12 additions & 11 deletions android-activity/src/native_activity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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,
},
);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion examples/agdk-cpal/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -131,6 +131,7 @@ fn android_main(app: AndroidApp) {
// Handle input
app.input_events(|event| {
info!("Input Event: {event:?}");
InputStatus::Unhandled
});

info!("Render...");
Expand Down
2 changes: 2 additions & 0 deletions examples/agdk-eframe/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions examples/agdk-eframe/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions examples/agdk-egui/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions examples/agdk-egui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down
3 changes: 2 additions & 1 deletion examples/agdk-mainloop/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use android_activity::{AndroidApp, MainEvent, PollEvent};
use android_activity::{AndroidApp, InputStatus, MainEvent, PollEvent};
use log::info;

#[no_mangle]
Expand Down Expand Up @@ -71,6 +71,7 @@ fn android_main(app: AndroidApp) {
// Handle input
app.input_events(|event| {
info!("Input Event: {event:?}");
InputStatus::Unhandled
});

info!("Render...");
Expand Down
7 changes: 5 additions & 2 deletions examples/agdk-oboe/src/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -151,7 +154,7 @@ impl AudioOutputCallback for SineWave<f32, Mono> {

fn on_audio_ready(
&mut self,
stream: &mut dyn AudioOutputStreamSafe,
_stream: &mut dyn AudioOutputStreamSafe,
frames: &mut [f32],
) -> DataCallbackResult {
for frame in frames {
Expand All @@ -166,7 +169,7 @@ impl AudioOutputCallback for SineWave<f32, Stereo> {

fn on_audio_ready(
&mut self,
stream: &mut dyn AudioOutputStreamSafe,
_stream: &mut dyn AudioOutputStreamSafe,
frames: &mut [(f32, f32)],
) -> DataCallbackResult {
for frame in frames {
Expand Down
3 changes: 2 additions & 1 deletion examples/agdk-oboe/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use android_activity::{AndroidApp, MainEvent, PollEvent};
use android_activity::{AndroidApp, MainEvent, PollEvent, InputStatus};
use log::info;

mod audio;
Expand Down Expand Up @@ -75,6 +75,7 @@ fn android_main(app: AndroidApp) {
// Handle input
app.input_events(|event| {
info!("Input Event: {event:?}");
InputStatus::Unhandled
});

info!("Render...");
Expand Down
2 changes: 2 additions & 0 deletions examples/agdk-winit-wgpu/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions examples/agdk-winit-wgpu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down
3 changes: 2 additions & 1 deletion examples/na-mainloop/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use android_activity::{AndroidApp, MainEvent, PollEvent};
use android_activity::{AndroidApp, MainEvent, PollEvent, InputStatus};
use log::info;

#[no_mangle]
Expand Down Expand Up @@ -71,6 +71,7 @@ fn android_main(app: AndroidApp) {
// Handle input
app.input_events(|event| {
info!("Input Event: {event:?}");
InputStatus::Unhandled
});

info!("Render...");
Expand Down
3 changes: 2 additions & 1 deletion examples/na-subclass-jni/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -77,6 +77,7 @@ fn android_main(app: AndroidApp) {
// Handle input
app.input_events(|event| {
info!("Input Event: {event:?}");
InputStatus::Unhandled
});

// Render...
Expand Down
2 changes: 2 additions & 0 deletions examples/na-winit-wgpu/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions examples/na-winit-wgpu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down

0 comments on commit 103bd65

Please sign in to comment.