Merge pull request #70 from MarijnS95/na-resize

native-activity: Propagate `NativeWindow` redraw/resize and `ContentRectChanged` callbacks to main loop
This commit is contained in:
Robert Bragg
2023-04-19 13:44:52 +01:00
committed by GitHub
3 changed files with 72 additions and 20 deletions
+2 -2
View File
@@ -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" ]
rustdoc-args = ["--cfg", "docsrs"]
+65 -8
View File
@@ -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<NativeWindow>,
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;
+5 -10
View File
@@ -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<ALooper>,
looper: NonNull<ndk_sys::ALooper>,
}
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,