diff --git a/android-activity/Cargo.toml b/android-activity/Cargo.toml index 70223dd..f0de17e 100644 --- a/android-activity/Cargo.toml +++ b/android-activity/Cargo.toml @@ -17,7 +17,7 @@ license = "MIT OR Apache-2.0" # # In general it's only the final application crate that needs # to decide on a backend. -default=[] +default = [] game-activity = [] native-activity = [] @@ -43,4 +43,4 @@ targets = [ "x86_64-linux-android", ] -rustdoc-args = ["--cfg", "docsrs" ] \ No newline at end of file +rustdoc-args = ["--cfg", "docsrs"] diff --git a/android-activity/src/native_activity/glue.rs b/android-activity/src/native_activity/glue.rs index 131878d..4e2b863 100644 --- a/android-activity/src/native_activity/glue.rs +++ b/android-activity/src/native_activity/glue.rs @@ -14,7 +14,6 @@ use std::{ use log::Level; use ndk::{configuration::Configuration, input_queue::InputQueue, native_window::NativeWindow}; -use ndk_sys::ANativeActivity; use crate::ConfigurationRef; @@ -99,7 +98,7 @@ impl Deref for NativeActivityGlue { impl NativeActivityGlue { pub fn new( - activity: *mut ANativeActivity, + activity: *mut ndk_sys::ANativeActivity, saved_state: *const libc::c_void, saved_state_size: libc::size_t, ) -> Self { @@ -126,9 +125,13 @@ impl NativeActivityGlue { (*(*activity).callbacks).onLowMemory = Some(on_low_memory); (*(*activity).callbacks).onWindowFocusChanged = Some(on_window_focus_changed); (*(*activity).callbacks).onNativeWindowCreated = Some(on_native_window_created); + (*(*activity).callbacks).onNativeWindowResized = Some(on_native_window_resized); + (*(*activity).callbacks).onNativeWindowRedrawNeeded = + Some(on_native_window_redraw_needed); (*(*activity).callbacks).onNativeWindowDestroyed = Some(on_native_window_destroyed); (*(*activity).callbacks).onInputQueueCreated = Some(on_input_queue_created); (*(*activity).callbacks).onInputQueueDestroyed = Some(on_input_queue_destroyed); + (*(*activity).callbacks).onContentRectChanged = Some(on_content_rect_changed); } glue @@ -145,7 +148,7 @@ impl NativeActivityGlue { self.inner.mutex.lock().unwrap().read_cmd() } - /// For the Rust main thread to get an ndk::InputQueue that wraps the AInputQueue pointer + /// For the Rust main thread to get an [`InputQueue`] that wraps the AInputQueue pointer /// we have and at the same time ensure that the input queue is attached to the given looper. /// /// NB: it's expected that the input queue is detached as soon as we know there is new @@ -208,7 +211,6 @@ pub struct NativeActivityState { pub redraw_needed: bool, pub pending_input_queue: *mut ndk_sys::AInputQueue, pub pending_window: Option, - pub pending_content_rect: ndk_sys::ARect, } impl NativeActivityState { @@ -355,7 +357,6 @@ impl WaitableNativeActivityState { redraw_needed: false, pending_input_queue: ptr::null_mut(), pending_window: None, - pending_content_rect: Rect::empty().into(), }), cond: Condvar::new(), } @@ -396,6 +397,26 @@ impl WaitableNativeActivityState { }); } + pub fn notify_window_resized(&self, native_window: *mut ndk_sys::ANativeWindow) { + let mut guard = self.mutex.lock().unwrap(); + // set_window always syncs .pending_window back to .window before returning. This callback + // from Android can never arrive at an interim state, and validates that Android: + // 1. Only provides resizes in between onNativeWindowCreated and onNativeWindowDestroyed; + // 2. Doesn't call it on a bogus window pointer that we don't know about. + debug_assert_eq!(guard.window.as_ref().unwrap().ptr().as_ptr(), native_window); + guard.write_cmd(AppCmd::WindowResized); + } + + pub fn notify_window_redraw_needed(&self, native_window: *mut ndk_sys::ANativeWindow) { + let mut guard = self.mutex.lock().unwrap(); + // set_window always syncs .pending_window back to .window before returning. This callback + // from Android can never arrive at an interim state, and validates that Android: + // 1. Only provides resizes in between onNativeWindowCreated and onNativeWindowDestroyed; + // 2. Doesn't call it on a bogus window pointer that we don't know about. + debug_assert_eq!(guard.window.as_ref().unwrap().ptr().as_ptr(), native_window); + guard.write_cmd(AppCmd::WindowRedrawNeeded); + } + unsafe fn set_input(&self, input_queue: *mut ndk_sys::AInputQueue) { let mut guard = self.mutex.lock().unwrap(); @@ -436,6 +457,12 @@ impl WaitableNativeActivityState { guard.pending_window = None; } + unsafe fn set_content_rect(&self, rect: *const ndk_sys::ARect) { + let mut guard = self.mutex.lock().unwrap(); + guard.content_rect = *rect; + guard.write_cmd(AppCmd::ContentRectChanged); + } + unsafe fn set_activity_state(&self, state: State) { let mut guard = self.mutex.lock().unwrap(); @@ -694,13 +721,33 @@ unsafe extern "C" fn on_native_window_created( ) { log::debug!("NativeWindowCreated: {:p} -- {:p}\n", activity, window); try_with_waitable_activity_ref(activity, |waitable_activity| { - // It's important that we use ::clone_from_ptr() here because NativeWindow - // has a Drop implementation that will unconditionally _release() the native window + // Use clone_from_ptr to acquire additional ownership on the NativeWindow, + // which will unconditionally be _release()'d on Drop. let window = NativeWindow::clone_from_ptr(NonNull::new_unchecked(window)); waitable_activity.set_window(Some(window)); }); } +unsafe extern "C" fn on_native_window_resized( + activity: *mut ndk_sys::ANativeActivity, + window: *mut ndk_sys::ANativeWindow, +) { + log::debug!("NativeWindowResized: {:p} -- {:p}\n", activity, window); + try_with_waitable_activity_ref(activity, |waitable_activity| { + waitable_activity.notify_window_resized(window); + }); +} + +unsafe extern "C" fn on_native_window_redraw_needed( + activity: *mut ndk_sys::ANativeActivity, + window: *mut ndk_sys::ANativeWindow, +) { + log::debug!("NativeWindowRedrawNeeded: {:p} -- {:p}\n", activity, window); + try_with_waitable_activity_ref(activity, |waitable_activity| { + waitable_activity.notify_window_redraw_needed(window) + }); +} + unsafe extern "C" fn on_native_window_destroyed( activity: *mut ndk_sys::ANativeActivity, window: *mut ndk_sys::ANativeWindow, @@ -731,6 +778,16 @@ unsafe extern "C" fn on_input_queue_destroyed( }); } +unsafe extern "C" fn on_content_rect_changed( + activity: *mut ndk_sys::ANativeActivity, + rect: *const ndk_sys::ARect, +) { + log::debug!("ContentRectChanged: {:p} -- {:p}\n", activity, rect); + try_with_waitable_activity_ref(activity, |waitable_activity| { + waitable_activity.set_content_rect(rect) + }); +} + /// This is the native entrypoint for our cdylib library that `ANativeActivity` will look for via `dlsym` #[no_mangle] extern "C" fn ANativeActivity_onCreate( @@ -779,7 +836,7 @@ extern "C" fn ANativeActivity_onCreate( // Note: we drop the thread handle which will detach the thread std::thread::spawn(move || { - let activity: *mut ANativeActivity = activity_ptr as *mut _; + let activity: *mut ndk_sys::ANativeActivity = activity_ptr as *mut _; let jvm = unsafe { let na = activity; diff --git a/android-activity/src/native_activity/mod.rs b/android-activity/src/native_activity/mod.rs index a67f56a..e0bb330 100644 --- a/android-activity/src/native_activity/mod.rs +++ b/android-activity/src/native_activity/mod.rs @@ -7,12 +7,7 @@ use std::time::Duration; use libc::c_void; use log::{error, trace}; - -use ndk_sys::ALooper_wake; -use ndk_sys::{ALooper, ALooper_pollAll}; - -use ndk::asset::AssetManager; -use ndk::native_window::NativeWindow; +use ndk::{asset::AssetManager, native_window::NativeWindow}; use crate::{ util, AndroidApp, ConfigurationRef, InputStatus, MainEvent, PollEvent, Rect, WindowManagerFlags, @@ -63,7 +58,7 @@ pub struct AndroidAppWaker { // The looper pointer is owned by the android_app and effectively // has a 'static lifetime, and the ALooper_wake C API is thread // safe, so this can be cloned safely and is send + sync safe - looper: NonNull, + looper: NonNull, } unsafe impl Send for AndroidAppWaker {} unsafe impl Sync for AndroidAppWaker {} @@ -77,7 +72,7 @@ impl AndroidAppWaker { /// [wake_event]: crate::PollEvent::Wake pub fn wake(&self) { unsafe { - ALooper_wake(self.looper.as_ptr()); + ndk_sys::ALooper_wake(self.looper.as_ptr()); } } } @@ -119,7 +114,7 @@ impl AndroidApp { #[derive(Debug)] struct Looper { - pub ptr: *mut ALooper, + pub ptr: *mut ndk_sys::ALooper, } unsafe impl Send for Looper {} unsafe impl Sync for Looper {} @@ -174,7 +169,7 @@ impl AndroidAppInner { !ndk_sys::ALooper_forThread().is_null(), "Application tried to poll events from non-main thread" ); - let id = ALooper_pollAll( + let id = ndk_sys::ALooper_pollAll( timeout_milliseconds, &mut fd, &mut events,