From 0565ae01ec37377b6e384961da49141fb73e2cb5 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sat, 2 Jul 2022 19:24:52 +0100 Subject: [PATCH] Expose native_window() via AndroidApp instead of static To be able to support multiple activities per-process we can't have any static/global state. --- game-activity/src/lib.rs | 70 +++++++++++++++++++++++++------------- native-activity/src/lib.rs | 66 +++++++++++++++++++++++------------ 2 files changed, 91 insertions(+), 45 deletions(-) diff --git a/game-activity/src/lib.rs b/game-activity/src/lib.rs index 110b968..7e74bf1 100755 --- a/game-activity/src/lib.rs +++ b/game-activity/src/lib.rs @@ -5,7 +5,7 @@ use ndk::asset::AssetManager; use ndk::configuration::Configuration; use ndk::looper::{FdEvent}; use ndk::native_window::NativeWindow; -use ndk_sys::ALooper_wake; +use ndk_sys::{ALooper_wake}; use ndk_sys::{ALooper, ALooper_pollAll}; use std::ffi::{CStr, CString}; use std::fs::File; @@ -16,11 +16,9 @@ use std::os::raw; use std::ptr::NonNull; use std::sync::Arc; use std::sync::RwLock; -use std::sync::RwLockReadGuard; use std::time::Duration; use std::{thread, ptr}; use std::os::unix::prelude::*; -use lazy_static::lazy_static; use crate::input::{MotionEvent, KeyEvent}; @@ -51,16 +49,6 @@ pub mod input; // getter also helps keep simple winit usage portable. static mut ANDROID_APP: Option = None; -// This is mainly just for convenience for implementing a winit backend -// although ideally it shouldn't be necessary to have a static global. -// -// Removing this would just require moving the `native_window()` getter -// to be an AndroidApp method and require winit to pass around the -// app wherever it needs to query the window. -lazy_static! { - static ref NATIVE_WINDOW: RwLock> = Default::default(); -} - // Note: unlike in ndk-glue this has signed components (consistent // with Android's ARect) which generally allows for representing // rectangles with a negative/off-screen origin. Even though this @@ -76,6 +64,33 @@ pub struct Rect { pub bottom: i32, } +// XXX: NativeWindow is a ref-counted object but the NativeWindow rust API +// doesn't currently implement Clone() in terms of acquiring a reference +// and Drop() in terms of releasing a reference. + +/// A reference to a `NativeWindow`, used for rendering +pub struct NativeWindowRef { + inner: NativeWindow +} +impl NativeWindowRef { + pub fn new(native_window: &NativeWindow) -> Self { + unsafe { ndk_sys::ANativeWindow_acquire(native_window.ptr().as_ptr()); } + Self { inner: native_window.clone() } + } +} +impl Drop for NativeWindowRef { + fn drop(&mut self) { + unsafe { ndk_sys::ANativeWindow_release(self.inner.ptr().as_ptr()) } + } +} +impl Deref for NativeWindowRef { + type Target = NativeWindow; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + // The only time it's safe to update the android_app->savedState pointer is // while handling a SaveState event, so this API is only exposed for those // events... @@ -272,6 +287,7 @@ impl Deref for AndroidApp { pub struct AndroidAppInner { ptr: NonNull, config: RwLock, + native_window: RwLock>, } impl AndroidApp { @@ -290,10 +306,25 @@ impl AndroidApp { inner: Arc::new(AndroidAppInner { ptr, config: RwLock::new(config), + native_window: Default::default(), }) } } + /// Queries the current [`NativeWindow`] for the application. + /// + /// This will only return `Some(window)` between + /// [`AndroidAppMainEvent::InitWindow`] and [`AndroidAppMainEvent::TerminateWindow`] + /// events. + pub fn native_window<'a>(&self) -> Option { + let guard = self.native_window.read().unwrap(); + if let Some(ref window) = *guard { + Some(NativeWindowRef::new(window)) + } else { + None + } + } + /// Calls [`ALooper_pollAll`] on the looper associated with this AndroidApp as well /// as processing any events (such as lifecycle events) via the given `callback`. /// @@ -387,11 +418,11 @@ impl AndroidApp { } MainEvent::InitWindow { .. } => { let win_ptr = (*app_ptr.as_ptr()).window; - *NATIVE_WINDOW.write().unwrap() = + *self.native_window.write().unwrap() = Some(NativeWindow::from_ptr(NonNull::new(win_ptr).unwrap())); } MainEvent::TerminateWindow { .. } => { - *NATIVE_WINDOW.write().unwrap() = None; + *self.native_window.write().unwrap() = None; } _ => {} } @@ -681,15 +712,6 @@ pub fn android_app() -> AndroidApp { } } -/// Queries the current [`NativeWindow`] for the application. -/// -/// This will only return `Some(window)` between -/// [`AndroidAppMainEvent::InitWindow`] and [`AndroidAppMainEvent::TerminateWindow`] -/// events. -pub fn native_window() -> RwLockReadGuard<'static, Option> { - NATIVE_WINDOW.read().unwrap() -} - // Rust doesn't give us a clean way to directly export symbols from C/C++ // so we rename the C/C++ symbols and re-export these JNI entrypoints from // Rust... diff --git a/native-activity/src/lib.rs b/native-activity/src/lib.rs index d5264b4..ba239a5 100644 --- a/native-activity/src/lib.rs +++ b/native-activity/src/lib.rs @@ -55,16 +55,6 @@ pub mod input { // getter also helps keep simple winit usage portable. static mut ANDROID_APP: Option = None; -// This is mainly just for convenience for implementing a winit backend -// although ideally it shouldn't be necessary to have a static global. -// -// Removing this would just require moving the `native_window()` getter -// to be an AndroidApp method and require winit to pass around the -// app wherever it needs to query the window. -lazy_static! { - static ref NATIVE_WINDOW: RwLock> = Default::default(); -} - pub type InputEvent = ndk::event::InputEvent; // Note: unlike in ndk-glue this has signed components (consistent @@ -82,6 +72,33 @@ pub struct Rect { pub bottom: i32, } +// XXX: NativeWindow is a ref-counted object but the NativeWindow rust API +// doesn't currently implement Clone() in terms of acquiring a reference +// and Drop() in terms of releasing a reference. + +/// A reference to a `NativeWindow`, used for rendering +pub struct NativeWindowRef { + inner: NativeWindow +} +impl NativeWindowRef { + pub fn new(native_window: &NativeWindow) -> Self { + unsafe { ndk_sys::ANativeWindow_acquire(native_window.ptr().as_ptr()); } + Self { inner: native_window.clone() } + } +} +impl Drop for NativeWindowRef { + fn drop(&mut self) { + unsafe { ndk_sys::ANativeWindow_release(self.inner.ptr().as_ptr()) } + } +} +impl Deref for NativeWindowRef { + type Target = NativeWindow; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + // The only time it's safe to update the android_app->savedState pointer is // while handling a SaveState event, so this API is only exposed for those // events... @@ -279,6 +296,7 @@ impl Deref for AndroidApp { pub struct AndroidAppInner { ptr: NonNull, config: RwLock, + native_window: RwLock>, } impl AndroidApp { @@ -297,6 +315,7 @@ impl AndroidApp { inner: Arc::new(AndroidAppInner { ptr, config: RwLock::new(config), + native_window: Default::default() }) } } @@ -308,6 +327,20 @@ impl AndroidApp { } } + /// Queries the current [`NativeWindow`] for the application. + /// + /// This will only return `Some(window)` between + /// [`AndroidAppMainEvent::InitWindow`] and [`AndroidAppMainEvent::TerminateWindow`] + /// events. + pub fn native_window<'a>(&self) -> Option { + let guard = self.native_window.read().unwrap(); + if let Some(ref window) = *guard { + Some(NativeWindowRef::new(window)) + } else { + None + } + } + /// Calls [`ALooper_pollAll`] on the looper associated with this AndroidApp as well /// as processing any events (such as lifecycle events) via the given `callback`. /// @@ -408,11 +441,11 @@ impl AndroidApp { } MainEvent::InitWindow { .. } => { let win_ptr = (*app_ptr.as_ptr()).window; - *NATIVE_WINDOW.write().unwrap() = + *self.native_window.write().unwrap() = Some(NativeWindow::from_ptr(NonNull::new(win_ptr).unwrap())); } MainEvent::TerminateWindow { .. } => { - *NATIVE_WINDOW.write().unwrap() = None; + *self.native_window.write().unwrap() = None; } _ => {} } @@ -593,15 +626,6 @@ pub fn android_app() -> AndroidApp { } } -/// Queries the current [`NativeWindow`] for the application. -/// -/// This will only return `Some(window)` between -/// [`AndroidAppMainEvent::InitWindow`] and [`AndroidAppMainEvent::TerminateWindow`] -/// events. -pub fn native_window() -> RwLockReadGuard<'static, Option> { - NATIVE_WINDOW.read().unwrap() -} - // Rust doesn't give us a clean way to directly export symbols from C/C++ // so we rename the C/C++ symbols and re-export this entrypoint from // Rust...