mirror of
https://github.com/rust-mobile/android-activity.git
synced 2026-07-10 00:37:28 +00:00
Make AndroidApp Send + Sync
In Winit then we ideally want to be able to associate a cloned reference to the AndroidApp with the `Window` and `MonitorHandle` types (since that's the most practical way to access the native_window and config state for the application) but for that `AndroidApp` needs to be Send + Sync
This commit is contained in:
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Emit an `InputAvailable` event for new input with `NativeActivity` and `GameActivity`
|
||||
enabling gui apps that don't render continuously
|
||||
- Oboe and Cpal audio examples added
|
||||
- `AndroidApp` is now `Send` + `Sync`
|
||||
### Changed
|
||||
- *Breaking*: updates to `ndk 0.7` and `ndk-sys 0.4`
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::ffi::{CStr, CString};
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
use std::os::raw;
|
||||
use std::os::unix::prelude::*;
|
||||
use std::ptr::NonNull;
|
||||
@@ -45,7 +46,7 @@ impl<'a> StateSaver<'a> {
|
||||
// via libc::malloc since it will automatically handle freeing the data once it
|
||||
// has been handed over to the Java Activity / main thread.
|
||||
unsafe {
|
||||
let app_ptr = self.app.ptr.as_ptr();
|
||||
let app_ptr = self.app.native_app.as_ptr();
|
||||
|
||||
// In case the application calls store() multiple times for some reason we
|
||||
// make sure to free any pre-existing state...
|
||||
@@ -82,7 +83,7 @@ pub struct StateLoader<'a> {
|
||||
impl<'a> StateLoader<'a> {
|
||||
pub fn load(&self) -> Option<Vec<u8>> {
|
||||
unsafe {
|
||||
let app_ptr = self.app.ptr.as_ptr();
|
||||
let app_ptr = self.app.native_app.as_ptr();
|
||||
if (*app_ptr).savedState != ptr::null_mut() && (*app_ptr).savedStateSize > 0 {
|
||||
let buf: &mut [u8] = std::slice::from_raw_parts_mut(
|
||||
(*app_ptr).savedState.cast(),
|
||||
@@ -126,18 +127,32 @@ impl AndroidApp {
|
||||
let config = Configuration::clone_from_ptr(NonNull::new_unchecked((*ptr.as_ptr()).config));
|
||||
|
||||
Self {
|
||||
inner: Arc::new(AndroidAppInner {
|
||||
ptr,
|
||||
inner: Arc::new(RwLock::new(AndroidAppInner {
|
||||
native_app: NativeAppGlue { ptr },
|
||||
config: RwLock::new(config),
|
||||
native_window: Default::default(),
|
||||
}),
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AndroidAppInner {
|
||||
struct NativeAppGlue {
|
||||
ptr: NonNull<ffi::android_app>,
|
||||
}
|
||||
impl Deref for NativeAppGlue {
|
||||
type Target = NonNull<ffi::android_app>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.ptr
|
||||
}
|
||||
}
|
||||
unsafe impl Send for NativeAppGlue {}
|
||||
unsafe impl Sync for NativeAppGlue {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AndroidAppInner {
|
||||
native_app: NativeAppGlue,
|
||||
config: RwLock<Configuration>,
|
||||
native_window: RwLock<Option<NativeWindow>>,
|
||||
}
|
||||
@@ -159,7 +174,7 @@ impl AndroidAppInner {
|
||||
trace!("poll_events");
|
||||
|
||||
unsafe {
|
||||
let app_ptr = self.ptr;
|
||||
let native_app = &self.native_app;
|
||||
|
||||
let mut fd: i32 = 0;
|
||||
let mut events: i32 = 0;
|
||||
@@ -181,7 +196,7 @@ impl AndroidAppInner {
|
||||
ffi::ALOOPER_POLL_WAKE => {
|
||||
trace!("ALooper_pollAll returned POLL_WAKE");
|
||||
|
||||
if ffi::android_app_input_available_wake_up(app_ptr.as_ptr()) {
|
||||
if ffi::android_app_input_available_wake_up(native_app.as_ptr()) {
|
||||
log::debug!("Notifying Input Available");
|
||||
callback(PollEvent::Main(MainEvent::InputAvailable));
|
||||
}
|
||||
@@ -213,7 +228,7 @@ impl AndroidAppInner {
|
||||
trace!("ALooper_pollAll returned ID_MAIN");
|
||||
let source: *mut ffi::android_poll_source = source.cast();
|
||||
if source != ptr::null_mut() {
|
||||
let cmd_i = ffi::android_app_read_cmd(app_ptr.as_ptr());
|
||||
let cmd_i = ffi::android_app_read_cmd(native_app.as_ptr());
|
||||
|
||||
let cmd = match cmd_i as u32 {
|
||||
//NativeAppGlueAppCmd_UNUSED_APP_CMD_INPUT_CHANGED => AndroidAppMainEvent::InputChanged,
|
||||
@@ -265,16 +280,16 @@ impl AndroidAppInner {
|
||||
trace!("Read ID_MAIN command {cmd_i} = {cmd:?}");
|
||||
|
||||
trace!("Calling android_app_pre_exec_cmd({cmd_i})");
|
||||
ffi::android_app_pre_exec_cmd(app_ptr.as_ptr(), cmd_i);
|
||||
ffi::android_app_pre_exec_cmd(native_app.as_ptr(), cmd_i);
|
||||
match cmd {
|
||||
MainEvent::ConfigChanged => {
|
||||
*self.config.write().unwrap() =
|
||||
Configuration::clone_from_ptr(NonNull::new_unchecked(
|
||||
(*app_ptr.as_ptr()).config,
|
||||
(*native_app.as_ptr()).config,
|
||||
));
|
||||
}
|
||||
MainEvent::InitWindow { .. } => {
|
||||
let win_ptr = (*app_ptr.as_ptr()).window;
|
||||
let win_ptr = (*native_app.as_ptr()).window;
|
||||
// It's important that we use ::clone_from_ptr() here
|
||||
// because NativeWindow has a Drop implementation that
|
||||
// will unconditionally _release() the native window
|
||||
@@ -293,7 +308,7 @@ impl AndroidAppInner {
|
||||
callback(PollEvent::Main(cmd));
|
||||
|
||||
trace!("Calling android_app_post_exec_cmd({cmd_i})");
|
||||
ffi::android_app_post_exec_cmd(app_ptr.as_ptr(), 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!");
|
||||
}
|
||||
@@ -320,11 +335,11 @@ impl AndroidAppInner {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_motion_axis(&self, axis: Axis) {
|
||||
pub fn enable_motion_axis(&mut self, axis: Axis) {
|
||||
unsafe { ffi::GameActivityPointerAxes_enableAxis(axis as i32) }
|
||||
}
|
||||
|
||||
pub fn disable_motion_axis(&self, axis: Axis) {
|
||||
pub fn disable_motion_axis(&mut self, axis: Axis) {
|
||||
unsafe { ffi::GameActivityPointerAxes_disableAxis(axis as i32) }
|
||||
}
|
||||
|
||||
@@ -332,7 +347,7 @@ impl AndroidAppInner {
|
||||
unsafe {
|
||||
// From the application's pov we assume the app_ptr and looper pointer
|
||||
// have static lifetimes and we can safely assume they are never NULL.
|
||||
let app_ptr = self.ptr.as_ptr();
|
||||
let app_ptr = self.native_app.as_ptr();
|
||||
AndroidAppWaker {
|
||||
looper: NonNull::new_unchecked((*app_ptr).looper),
|
||||
}
|
||||
@@ -345,7 +360,7 @@ impl AndroidAppInner {
|
||||
|
||||
pub fn content_rect(&self) -> Rect {
|
||||
unsafe {
|
||||
let app_ptr = self.ptr.as_ptr();
|
||||
let app_ptr = self.native_app.as_ptr();
|
||||
Rect {
|
||||
left: (*app_ptr).contentRect.left,
|
||||
right: (*app_ptr).contentRect.right,
|
||||
@@ -357,7 +372,7 @@ impl AndroidAppInner {
|
||||
|
||||
pub fn asset_manager(&self) -> AssetManager {
|
||||
unsafe {
|
||||
let app_ptr = self.ptr.as_ptr();
|
||||
let app_ptr = self.native_app.as_ptr();
|
||||
let am_ptr = NonNull::new_unchecked((*(*app_ptr).activity).assetManager);
|
||||
AssetManager::from_ptr(am_ptr)
|
||||
}
|
||||
@@ -368,7 +383,7 @@ impl AndroidAppInner {
|
||||
F: FnMut(&InputEvent),
|
||||
{
|
||||
let buf = unsafe {
|
||||
let app_ptr = self.ptr.as_ptr();
|
||||
let app_ptr = self.native_app.as_ptr();
|
||||
let input_buffer = ffi::android_app_swap_input_buffers(app_ptr);
|
||||
if input_buffer == ptr::null_mut() {
|
||||
return;
|
||||
@@ -386,21 +401,21 @@ impl AndroidAppInner {
|
||||
|
||||
pub fn internal_data_path(&self) -> Option<std::path::PathBuf> {
|
||||
unsafe {
|
||||
let app_ptr = self.ptr.as_ptr();
|
||||
let app_ptr = self.native_app.as_ptr();
|
||||
util::try_get_path_from_ptr((*(*app_ptr).activity).internalDataPath)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn external_data_path(&self) -> Option<std::path::PathBuf> {
|
||||
unsafe {
|
||||
let app_ptr = self.ptr.as_ptr();
|
||||
let app_ptr = self.native_app.as_ptr();
|
||||
util::try_get_path_from_ptr((*(*app_ptr).activity).externalDataPath)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn obb_path(&self) -> Option<std::path::PathBuf> {
|
||||
unsafe {
|
||||
let app_ptr = self.ptr.as_ptr();
|
||||
let app_ptr = self.native_app.as_ptr();
|
||||
util::try_get_path_from_ptr((*(*app_ptr).activity).obbPath)
|
||||
}
|
||||
}
|
||||
|
||||
+21
-26
@@ -1,5 +1,6 @@
|
||||
use std::hash::Hash;
|
||||
use std::ops::Deref;
|
||||
use std::sync::RwLock;
|
||||
use std::time::Duration;
|
||||
use std::{os::unix::prelude::RawFd, sync::Arc};
|
||||
|
||||
@@ -207,7 +208,7 @@ pub use activity_impl::AndroidAppWaker;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AndroidApp {
|
||||
pub(crate) inner: Arc<AndroidAppInner>,
|
||||
pub(crate) inner: Arc<RwLock<AndroidAppInner>>,
|
||||
}
|
||||
|
||||
impl PartialEq for AndroidApp {
|
||||
@@ -227,7 +228,7 @@ impl AndroidApp {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "native-activity")))]
|
||||
#[cfg(feature = "native-activity")]
|
||||
pub(crate) fn native_activity(&self) -> *const ndk_sys::ANativeActivity {
|
||||
self.inner.native_activity()
|
||||
self.inner.read().unwrap().native_activity()
|
||||
}
|
||||
|
||||
/// Queries the current [`NativeWindow`] for the application.
|
||||
@@ -236,7 +237,7 @@ impl AndroidApp {
|
||||
/// [`AndroidAppMainEvent::InitWindow`] and [`AndroidAppMainEvent::TerminateWindow`]
|
||||
/// events.
|
||||
pub fn native_window<'a>(&self) -> Option<NativeWindowRef> {
|
||||
self.inner.native_window()
|
||||
self.inner.read().unwrap().native_window()
|
||||
}
|
||||
|
||||
/// Calls [`ALooper_pollAll`] on the looper associated with this AndroidApp as well
|
||||
@@ -250,14 +251,11 @@ impl AndroidApp {
|
||||
/// 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.
|
||||
///
|
||||
/// # Safety
|
||||
/// This API must only be called from the application's main thread
|
||||
pub fn poll_events<F>(&self, timeout: Option<Duration>, callback: F)
|
||||
where
|
||||
F: FnMut(PollEvent),
|
||||
{
|
||||
self.inner.poll_events(timeout, callback);
|
||||
self.inner.read().unwrap().poll_events(timeout, callback);
|
||||
}
|
||||
|
||||
/// Creates a means to wake up the main loop while it is blocked waiting for
|
||||
@@ -265,50 +263,41 @@ impl AndroidApp {
|
||||
///
|
||||
/// Internally this uses [`ALooper_wake`] on the looper associated with this
|
||||
/// [AndroidApp].
|
||||
///
|
||||
/// # Safety
|
||||
/// This API can be used from any thread
|
||||
pub fn create_waker(&self) -> activity_impl::AndroidAppWaker {
|
||||
self.inner.create_waker()
|
||||
self.inner.read().unwrap().create_waker()
|
||||
}
|
||||
|
||||
/// Returns a deep copy of this application's [`Configuration`]
|
||||
pub fn config(&self) -> Configuration {
|
||||
self.inner.config()
|
||||
self.inner.read().unwrap().config()
|
||||
}
|
||||
|
||||
/// Queries the current content rectangle of the window; this is the area where the
|
||||
/// window's content should be placed to be seen by the user.
|
||||
///
|
||||
/// # Safety
|
||||
/// This API must only be called from the applications main thread
|
||||
pub fn content_rect(&self) -> Rect {
|
||||
self.inner.content_rect()
|
||||
self.inner.read().unwrap().content_rect()
|
||||
}
|
||||
|
||||
/// Queries the Asset Manager instance for the application.
|
||||
///
|
||||
/// Use this to access binary assets bundled inside your application's .apk file.
|
||||
///
|
||||
/// # Safety
|
||||
/// This API must only be called from the applications main thread
|
||||
pub fn asset_manager(&self) -> AssetManager {
|
||||
self.inner.asset_manager()
|
||||
self.inner.read().unwrap().asset_manager()
|
||||
}
|
||||
|
||||
pub fn enable_motion_axis(&self, axis: input::Axis) {
|
||||
self.inner.enable_motion_axis(axis);
|
||||
self.inner.write().unwrap().enable_motion_axis(axis);
|
||||
}
|
||||
|
||||
pub fn disable_motion_axis(&self, axis: input::Axis) {
|
||||
self.inner.disable_motion_axis(axis);
|
||||
self.inner.write().unwrap().disable_motion_axis(axis);
|
||||
}
|
||||
|
||||
pub fn input_events<'b, F>(&self, callback: F)
|
||||
where
|
||||
F: FnMut(&input::InputEvent),
|
||||
{
|
||||
self.inner.input_events(callback);
|
||||
self.inner.read().unwrap().input_events(callback);
|
||||
}
|
||||
|
||||
/// The user-visible SDK version of the framework
|
||||
@@ -325,16 +314,22 @@ impl AndroidApp {
|
||||
|
||||
/// Path to this application's internal data directory
|
||||
pub fn internal_data_path(&self) -> Option<std::path::PathBuf> {
|
||||
self.inner.internal_data_path()
|
||||
self.inner.read().unwrap().internal_data_path()
|
||||
}
|
||||
|
||||
/// Path to this application's external data directory
|
||||
pub fn external_data_path(&self) -> Option<std::path::PathBuf> {
|
||||
self.inner.external_data_path()
|
||||
self.inner.read().unwrap().external_data_path()
|
||||
}
|
||||
|
||||
/// Path to the directory containing the application's OBB files (if any).
|
||||
pub fn obb_path(&self) -> Option<std::path::PathBuf> {
|
||||
self.inner.obb_path()
|
||||
self.inner.read().unwrap().obb_path()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_app_is_send_sync() {
|
||||
fn needs_send_sync<T: Send + Sync>() {}
|
||||
needs_send_sync::<AndroidApp>();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::ops::Deref;
|
||||
use std::os::raw;
|
||||
use std::os::unix::prelude::*;
|
||||
use std::ptr::NonNull;
|
||||
@@ -47,7 +48,7 @@ impl<'a> StateSaver<'a> {
|
||||
// via libc::malloc since it will automatically handle freeing the data once it
|
||||
// has been handed over to the Java Activity / main thread.
|
||||
unsafe {
|
||||
let app_ptr = self.app.ptr.as_ptr();
|
||||
let app_ptr = self.app.native_app.as_ptr();
|
||||
|
||||
// In case the application calls store() multiple times for some reason we
|
||||
// make sure to free any pre-existing state...
|
||||
@@ -84,7 +85,7 @@ pub struct StateLoader<'a> {
|
||||
impl<'a> StateLoader<'a> {
|
||||
pub fn load(&self) -> Option<Vec<u8>> {
|
||||
unsafe {
|
||||
let app_ptr = self.app.ptr.as_ptr();
|
||||
let app_ptr = self.app.native_app.as_ptr();
|
||||
if (*app_ptr).savedState != ptr::null_mut() && (*app_ptr).savedStateSize > 0 {
|
||||
let buf: &mut [u8] = std::slice::from_raw_parts_mut(
|
||||
(*app_ptr).savedState.cast(),
|
||||
@@ -128,18 +129,32 @@ impl AndroidApp {
|
||||
let config = Configuration::clone_from_ptr(NonNull::new_unchecked((*ptr.as_ptr()).config));
|
||||
|
||||
AndroidApp {
|
||||
inner: Arc::new(AndroidAppInner {
|
||||
ptr,
|
||||
inner: Arc::new(RwLock::new(AndroidAppInner {
|
||||
native_app: NativeAppGlue { ptr },
|
||||
config: RwLock::new(config),
|
||||
native_window: Default::default(),
|
||||
}),
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AndroidAppInner {
|
||||
struct NativeAppGlue {
|
||||
ptr: NonNull<ffi::android_app>,
|
||||
}
|
||||
impl Deref for NativeAppGlue {
|
||||
type Target = NonNull<ffi::android_app>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.ptr
|
||||
}
|
||||
}
|
||||
unsafe impl Send for NativeAppGlue {}
|
||||
unsafe impl Sync for NativeAppGlue {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AndroidAppInner {
|
||||
native_app: NativeAppGlue,
|
||||
config: RwLock<Configuration>,
|
||||
native_window: RwLock<Option<NativeWindow>>,
|
||||
}
|
||||
@@ -147,7 +162,7 @@ pub(crate) struct AndroidAppInner {
|
||||
impl AndroidAppInner {
|
||||
pub(crate) fn native_activity(&self) -> *const ndk_sys::ANativeActivity {
|
||||
unsafe {
|
||||
let app_ptr = self.ptr.as_ptr();
|
||||
let app_ptr = self.native_app.as_ptr();
|
||||
(*app_ptr).activity.cast()
|
||||
}
|
||||
}
|
||||
@@ -168,7 +183,7 @@ impl AndroidAppInner {
|
||||
trace!("poll_events");
|
||||
|
||||
unsafe {
|
||||
let app_ptr = self.ptr;
|
||||
let native_app = &self.native_app;
|
||||
|
||||
let mut fd: i32 = 0;
|
||||
let mut events: i32 = 0;
|
||||
@@ -217,7 +232,7 @@ impl AndroidAppInner {
|
||||
trace!("ALooper_pollAll returned ID_MAIN");
|
||||
let source: *mut ffi::android_poll_source = source.cast();
|
||||
if source != ptr::null_mut() {
|
||||
let cmd_i = ffi::android_app_read_cmd(app_ptr.as_ptr());
|
||||
let cmd_i = ffi::android_app_read_cmd(native_app.as_ptr());
|
||||
|
||||
let cmd = match cmd_i as u32 {
|
||||
// We don't forward info about the AInputQueue to apps since it's
|
||||
@@ -256,7 +271,7 @@ impl AndroidAppInner {
|
||||
};
|
||||
|
||||
trace!("Calling android_app_pre_exec_cmd({cmd_i})");
|
||||
ffi::android_app_pre_exec_cmd(app_ptr.as_ptr(), cmd_i);
|
||||
ffi::android_app_pre_exec_cmd(native_app.as_ptr(), cmd_i);
|
||||
|
||||
if let Some(cmd) = cmd {
|
||||
trace!("Read ID_MAIN command {cmd_i} = {cmd:?}");
|
||||
@@ -265,12 +280,12 @@ impl AndroidAppInner {
|
||||
*self.config.write().unwrap() =
|
||||
Configuration::clone_from_ptr(
|
||||
NonNull::new_unchecked(
|
||||
(*app_ptr.as_ptr()).config,
|
||||
(*native_app.as_ptr()).config,
|
||||
),
|
||||
);
|
||||
}
|
||||
MainEvent::InitWindow { .. } => {
|
||||
let win_ptr = (*app_ptr.as_ptr()).window;
|
||||
let win_ptr = (*native_app.as_ptr()).window;
|
||||
// It's important that we use ::clone_from_ptr() here
|
||||
// because NativeWindow has a Drop implementation that
|
||||
// will unconditionally _release() the native window
|
||||
@@ -290,7 +305,7 @@ impl AndroidAppInner {
|
||||
}
|
||||
|
||||
trace!("Calling android_app_post_exec_cmd({cmd_i})");
|
||||
ffi::android_app_post_exec_cmd(app_ptr.as_ptr(), 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!");
|
||||
}
|
||||
@@ -302,7 +317,7 @@ impl AndroidAppInner {
|
||||
// input events then we only send one `InputAvailable` per iteration of input
|
||||
// handling. We re-attach the looper when the application calls
|
||||
// `AndroidApp::input_events()`
|
||||
ffi::android_app_detach_input_queue_looper(app_ptr.as_ptr());
|
||||
ffi::android_app_detach_input_queue_looper(native_app.as_ptr());
|
||||
callback(PollEvent::Main(MainEvent::InputAvailable))
|
||||
}
|
||||
_ => {
|
||||
@@ -331,7 +346,7 @@ impl AndroidAppInner {
|
||||
unsafe {
|
||||
// From the application's pov we assume the app_ptr and looper pointer
|
||||
// have static lifetimes and we can safely assume they are never NULL.
|
||||
let app_ptr = self.ptr.as_ptr();
|
||||
let app_ptr = self.native_app.as_ptr();
|
||||
AndroidAppWaker {
|
||||
looper: NonNull::new_unchecked((*app_ptr).looper),
|
||||
}
|
||||
@@ -344,7 +359,7 @@ impl AndroidAppInner {
|
||||
|
||||
pub fn content_rect(&self) -> Rect {
|
||||
unsafe {
|
||||
let app_ptr = self.ptr.as_ptr();
|
||||
let app_ptr = self.native_app.as_ptr();
|
||||
Rect {
|
||||
left: (*app_ptr).contentRect.left,
|
||||
right: (*app_ptr).contentRect.right,
|
||||
@@ -356,7 +371,7 @@ impl AndroidAppInner {
|
||||
|
||||
pub fn asset_manager(&self) -> AssetManager {
|
||||
unsafe {
|
||||
let app_ptr = self.ptr.as_ptr();
|
||||
let app_ptr = self.native_app.as_ptr();
|
||||
let am_ptr = NonNull::new_unchecked((*(*app_ptr).activity).assetManager);
|
||||
AssetManager::from_ptr(am_ptr)
|
||||
}
|
||||
@@ -375,7 +390,7 @@ impl AndroidAppInner {
|
||||
F: FnMut(&input::InputEvent),
|
||||
{
|
||||
let queue = unsafe {
|
||||
let app_ptr = self.ptr.as_ptr();
|
||||
let app_ptr = self.native_app.as_ptr();
|
||||
if (*app_ptr).inputQueue == ptr::null_mut() {
|
||||
return;
|
||||
}
|
||||
|
||||
-1
@@ -13,7 +13,6 @@
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
Reference in New Issue
Block a user