Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 11 additions & 13 deletions android-activity/src/game_activity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ use log::{error, trace};

use jni::sys::*;

use ndk_sys::ALooper_wake;
use ndk_sys::{ALooper, ALooper_pollAll};
use ndk_sys::{ALooper, ALooper_pollOnce, ALooper_wake};

use ndk::asset::AssetManager;
use ndk::configuration::Configuration;
Expand Down Expand Up @@ -346,17 +345,16 @@ impl AndroidAppInner {
} else {
-1
};
trace!("Calling ALooper_pollAll, timeout = {timeout_milliseconds}");
let id = ALooper_pollAll(
trace!("Calling ALooper_pollOnce, timeout = {timeout_milliseconds}");
let id = ALooper_pollOnce(
timeout_milliseconds,
&mut fd,
&mut events,
&mut source as *mut *mut core::ffi::c_void,
);
match id {
ffi::ALOOPER_POLL_WAKE => {
trace!("ALooper_pollAll returned POLL_WAKE");

trace!("ALooper_pollOnce returned POLL_WAKE");
if ffi::android_app_input_available_wake_up(native_app.as_ptr()) {
log::debug!("Notifying Input Available");
callback(PollEvent::Main(MainEvent::InputAvailable));
Expand All @@ -365,23 +363,23 @@ impl AndroidAppInner {
callback(PollEvent::Wake);
}
ffi::ALOOPER_POLL_CALLBACK => {
// ALooper_pollAll is documented to handle all callback sources internally so it should
// ALooper_pollOnce is documented to handle all callback sources internally so it should
// never return a _CALLBACK source id...
error!("Spurious ALOOPER_POLL_CALLBACK from ALopper_pollAll() (ignored)");
error!("Spurious ALOOPER_POLL_CALLBACK from ALooper_pollOnce() (ignored)");
}
ffi::ALOOPER_POLL_TIMEOUT => {
trace!("ALooper_pollAll returned POLL_TIMEOUT");
trace!("ALooper_pollOnce returned POLL_TIMEOUT");
callback(PollEvent::Timeout);
}
ffi::ALOOPER_POLL_ERROR => {
// If we have an IO error with our pipe to the main Java thread that's surely
// not something we can recover from
panic!("ALooper_pollAll returned POLL_ERROR");
panic!("ALooper_pollOnce returned POLL_ERROR");
}
id if id >= 0 => {
match id as ffi::NativeAppGlueLooperId {
ffi::NativeAppGlueLooperId_LOOPER_ID_MAIN => {
trace!("ALooper_pollAll returned ID_MAIN");
trace!("ALooper_pollOnce returned ID_MAIN");
let source: *mut ffi::android_poll_source = source.cast();
if !source.is_null() {
let cmd_i = ffi::android_app_read_cmd(native_app.as_ptr());
Expand Down Expand Up @@ -485,7 +483,7 @@ impl AndroidAppInner {
trace!("Calling android_app_post_exec_cmd({cmd_i})");
ffi::android_app_post_exec_cmd(native_app.as_ptr(), cmd_i);
} else {
panic!("ALooper_pollAll returned ID_MAIN event with NULL android_poll_source!");
panic!("ALooper_pollOnce returned ID_MAIN event with NULL android_poll_source!");
}
}
_ => {
Expand All @@ -494,7 +492,7 @@ impl AndroidAppInner {
}
}
_ => {
error!("Spurious ALooper_pollAll return value {id} (ignored)");
error!("Spurious ALooper_pollOnce return value {id} (ignored)");
}
}
}
Expand Down
46 changes: 34 additions & 12 deletions android-activity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,24 +607,46 @@ impl AndroidApp {
self.inner.read().unwrap().activity_as_ptr()
}

/// Polls for any events associated with this [AndroidApp] and processes those events
/// (such as lifecycle events) via the given `callback`.
///
/// It's important to use this API for polling, and not call [`ALooper_pollAll`] directly since
/// some events require pre- and post-processing either side of the callback. For correct
/// behavior events should be handled immediately, before returning from the callback and
/// not simply queued for batch processing later. For example the existing [`NativeWindow`]
/// is accessible during a [`MainEvent::TerminateWindow`] callback and will be
/// set to `None` once the callback returns, and this is also synchronized with the Java
/// main thread. The [`MainEvent::SaveState`] event is also synchronized with the
/// Polls for any events associated with this [AndroidApp] and processes
/// those events (such as lifecycle events) via the given `callback`.
///
/// It's important to use this API for polling, and not call
/// [`ALooper_pollAll`] or [`ALooper_pollOnce`] directly since some events
/// require pre- and post-processing either side of the callback. For
/// correct behavior events should be handled immediately, before returning
/// from the callback and not simply queued for batch processing later. For
/// example the existing [`NativeWindow`] is accessible during a
/// [`MainEvent::TerminateWindow`] callback and will be set to `None` once
/// the callback returns, and this is also synchronized with the Java main
/// thread. The [`MainEvent::SaveState`] event is also synchronized with the
/// Java main thread.
///
/// Internally this is based on [`ALooper_pollOnce`] and will only poll
/// file descriptors once per invocation.
///
/// # Wake Events
///
/// Note that although there is an explicit [PollEvent::Wake] that _can_
/// indicate that the main loop was explicitly woken up (E.g. via
/// [`AndroidAppWaker::wake`]) it's possible that there will be
/// more-specific events that will be delivered after a wake up.
///
/// In other words you should only expect to explicitly see
/// [`PollEvent::Wake`] events after an early wake up if there were no
/// other, more-specific, events that could be delivered after the wake up.
///
/// Again, said another way - it's possible that _any_ event could
/// effectively be delivered after an early wake up so don't assume there is
/// a 1:1 relationship between invoking a wake up via
/// [`AndroidAppWaker::wake`] and the delivery of [PollEvent::Wake].
///
/// # Panics
///
/// This must only be called from your `android_main()` thread and it may panic if called
/// from another thread.
/// This must only be called from your `android_main()` thread and it may
/// panic if called from another thread.
///
/// [`ALooper_pollAll`]: ndk::looper::ThreadLooper::poll_all
/// [`ALooper_pollOnce`]: ndk::looper::ThreadLooper::poll_once
pub fn poll_events<F>(&self, timeout: Option<Duration>, callback: F)
where
F: FnMut(PollEvent<'_>),
Expand Down
22 changes: 11 additions & 11 deletions android-activity/src/native_activity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,42 +194,42 @@ impl AndroidAppInner {
-1
};

trace!("Calling ALooper_pollAll, timeout = {timeout_milliseconds}");
trace!("Calling ALooper_pollOnce, timeout = {timeout_milliseconds}");
assert_eq!(
ndk_sys::ALooper_forThread(),
self.looper.ptr,
"Application tried to poll events from non-main thread"
);
let id = ndk_sys::ALooper_pollAll(
let id = ndk_sys::ALooper_pollOnce(
timeout_milliseconds,
&mut fd,
&mut events,
&mut source as *mut *mut c_void,
);
trace!("pollAll id = {id}");
trace!("pollOnce id = {id}");
match id {
ndk_sys::ALOOPER_POLL_WAKE => {
trace!("ALooper_pollAll returned POLL_WAKE");
trace!("ALooper_pollOnce returned POLL_WAKE");
callback(PollEvent::Wake);
}
ndk_sys::ALOOPER_POLL_CALLBACK => {
// ALooper_pollAll is documented to handle all callback sources internally so it should
// ALooper_pollOnce is documented to handle all callback sources internally so it should
// never return a _CALLBACK source id...
error!("Spurious ALOOPER_POLL_CALLBACK from ALopper_pollAll() (ignored)");
error!("Spurious ALOOPER_POLL_CALLBACK from ALooper_pollOnce() (ignored)");
}
ndk_sys::ALOOPER_POLL_TIMEOUT => {
trace!("ALooper_pollAll returned POLL_TIMEOUT");
trace!("ALooper_pollOnce returned POLL_TIMEOUT");
callback(PollEvent::Timeout);
}
ndk_sys::ALOOPER_POLL_ERROR => {
// If we have an IO error with our pipe to the main Java thread that's surely
// not something we can recover from
panic!("ALooper_pollAll returned POLL_ERROR");
panic!("ALooper_pollOnce returned POLL_ERROR");
}
id if id >= 0 => {
match id {
LOOPER_ID_MAIN => {
trace!("ALooper_pollAll returned ID_MAIN");
trace!("ALooper_pollOnce returned ID_MAIN");
if let Some(ipc_cmd) = self.native_activity.read_cmd() {
let main_cmd = match ipc_cmd {
// We don't forward info about the AInputQueue to apps since it's
Expand Down Expand Up @@ -283,7 +283,7 @@ impl AndroidAppInner {
}
}
LOOPER_ID_INPUT => {
trace!("ALooper_pollAll returned ID_INPUT");
trace!("ALooper_pollOnce returned ID_INPUT");

// To avoid spamming the application with event loop iterations notifying them of
// input events then we only send one `InputAvailable` per iteration of input
Expand All @@ -298,7 +298,7 @@ impl AndroidAppInner {
}
}
_ => {
error!("Spurious ALooper_pollAll return value {id} (ignored)");
error!("Spurious ALooper_pollOnce return value {id} (ignored)");
}
}
}
Expand Down