diff --git a/android-activity/build.rs b/android-activity/build.rs index 8ccf0ee..45ce9f2 100644 --- a/android-activity/build.rs +++ b/android-activity/build.rs @@ -32,8 +32,8 @@ fn build_glue_for_game_activity() { } fn main() { - #[cfg(feature="game-activity")] + #[cfg(feature = "game-activity")] build_glue_for_game_activity(); - #[cfg(feature="native-activity")] + #[cfg(feature = "native-activity")] build_glue_for_native_activity(); -} \ No newline at end of file +} diff --git a/android-activity/src/game_activity/ffi.rs b/android-activity/src/game_activity/ffi.rs index d7be0f7..56f98e9 100644 --- a/android-activity/src/game_activity/ffi.rs +++ b/android-activity/src/game_activity/ffi.rs @@ -15,8 +15,7 @@ use jni_sys::*; use ndk_sys::AAssetManager; use ndk_sys::ANativeWindow; -use ndk_sys::{ALooper, ALooper_callbackFunc, AConfiguration}; - +use ndk_sys::{AConfiguration, ALooper, ALooper_callbackFunc}; #[cfg(all( any(target_os = "android", feature = "test"), @@ -31,4 +30,4 @@ include!("ffi_aarch64.rs"); include!("ffi_i686.rs"); #[cfg(all(any(target_os = "android", feature = "test"), target_arch = "x86_64"))] -include!("ffi_x86_64.rs"); \ No newline at end of file +include!("ffi_x86_64.rs"); diff --git a/android-activity/src/game_activity/input.rs b/android-activity/src/game_activity/input.rs index 1285434..d75bc96 100644 --- a/android-activity/src/game_activity/input.rs +++ b/android-activity/src/game_activity/input.rs @@ -13,9 +13,9 @@ // The `Class` was also bound differently to `android-ndk-rs` considering how the class is defined // by masking bits from the `Source`. +use crate::game_activity::ffi::{GameActivityKeyEvent, GameActivityMotionEvent}; use num_enum::{IntoPrimitive, TryFromPrimitive}; use std::{convert::TryInto, ops::Deref}; -use crate::game_activity::ffi::{GameActivityMotionEvent, GameActivityKeyEvent}; use bitflags::bitflags; @@ -25,7 +25,7 @@ use bitflags::bitflags; #[non_exhaustive] pub enum InputEvent { MotionEvent(MotionEvent), - KeyEvent(KeyEvent) + KeyEvent(KeyEvent), } /// An enum representing the source of an [`MotionEvent`] or [`KeyEvent`] @@ -83,7 +83,7 @@ pub enum Class { Pointer, Trackball, Position, - Joystick + Joystick, } impl From for Class { @@ -95,7 +95,7 @@ impl From for Class { SourceFlags::TRACKBALL => Class::Trackball, SourceFlags::POSITION => Class::Position, SourceFlags::JOYSTICK => Class::Joystick, - _ => Class::None + _ => Class::None, } } } @@ -183,7 +183,7 @@ impl MetaState { /// javadoc](https://developer.android.com/reference/android/view/MotionEvent). #[derive(Clone, Debug)] pub struct MotionEvent { - ga_event: GameActivityMotionEvent + ga_event: GameActivityMotionEvent, } impl Deref for MotionEvent { @@ -433,10 +433,7 @@ impl MotionEvent { if index >= self.pointer_count() { panic!("Pointer index {} is out of bounds", index); } - Pointer { - event: self, - index, - } + Pointer { event: self, index } } /* @@ -987,7 +984,7 @@ impl ExactSizeIterator for HistoricalPointersIter<'_> { /// javadoc](https://developer.android.com/reference/android/view/KeyEvent). #[derive(Debug, Clone)] pub struct KeyEvent { - ga_event: GameActivityKeyEvent + ga_event: GameActivityKeyEvent, } impl Deref for KeyEvent { @@ -1307,7 +1304,6 @@ pub enum Keycode { } impl KeyEvent { - pub(crate) fn new(ga_event: GameActivityKeyEvent) -> Self { Self { ga_event } } @@ -1445,7 +1441,6 @@ impl KeyEventFlags { } impl KeyEvent { - /// Flags associated with this [`KeyEvent`]. /// /// See [the NDK docs](https://developer.android.com/ndk/reference/group/input#akeyevent_getflags) @@ -1462,4 +1457,4 @@ impl KeyEvent { pub fn meta_state(&self) -> MetaState { MetaState(self.metaState as u32) } -} \ No newline at end of file +} diff --git a/android-activity/src/game_activity/mod.rs b/android-activity/src/game_activity/mod.rs index d217939..264e500 100644 --- a/android-activity/src/game_activity/mod.rs +++ b/android-activity/src/game_activity/mod.rs @@ -1,36 +1,35 @@ -#![cfg(feature="game-activity")] +#![cfg(feature = "game-activity")] use std::ffi::{CStr, CString}; use std::fs::File; use std::io::{BufRead, BufReader}; use std::marker::PhantomData; use std::os::raw::{self, c_char}; +use std::os::unix::prelude::*; use std::ptr::NonNull; use std::sync::Arc; use std::sync::RwLock; use std::time::Duration; -use std::{thread, ptr}; -use std::os::unix::prelude::*; +use std::{ptr, thread}; -use log::{Level, error, trace}; +use log::{error, trace, Level}; use jni_sys::*; -use ndk_sys::{ALooper_wake}; +use ndk_sys::ALooper_wake; use ndk_sys::{ALooper, ALooper_pollAll}; use ndk::asset::AssetManager; use ndk::configuration::Configuration; -use ndk::looper::{FdEvent}; +use ndk::looper::FdEvent; use ndk::native_window::NativeWindow; -use crate::{MainEvent, Rect, PollEvent, AndroidApp, NativeWindowRef}; +use crate::{AndroidApp, MainEvent, NativeWindowRef, PollEvent, Rect}; mod ffi; pub mod input; -use input::{MotionEvent, KeyEvent, Axis, InputEvent}; - +use input::{Axis, InputEvent, KeyEvent, MotionEvent}; // 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 @@ -42,7 +41,6 @@ pub struct StateSaver<'a> { impl<'a> StateSaver<'a> { pub fn store(&self, state: &'a [u8]) { - // android_native_app_glue specifically expects savedState to have been allocated // via libc::malloc since it will automatically handle freeing the data once it // has been handed over to the Java Activity / main thread. @@ -86,7 +84,10 @@ impl<'a> StateLoader<'a> { unsafe { let app_ptr = self.app.ptr.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(), (*app_ptr).savedStateSize as usize); + let buf: &mut [u8] = std::slice::from_raw_parts_mut( + (*app_ptr).savedState.cast(), + (*app_ptr).savedStateSize as usize, + ); let state = buf.to_vec(); Some(state) } else { @@ -101,20 +102,21 @@ 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 {} impl AndroidAppWaker { pub fn wake(&self) { - unsafe { ALooper_wake(self.looper.as_ptr()); } + unsafe { + ALooper_wake(self.looper.as_ptr()); + } } } impl AndroidApp { pub(crate) unsafe fn from_ptr(ptr: NonNull) -> Self { - // Note: we don't use from_ptr since we don't own the android_app.config // and need to keep in mind that the Drop handler is going to call // AConfiguration_delete() @@ -128,7 +130,7 @@ impl AndroidApp { ptr, config: RwLock::new(config), native_window: Default::default(), - }) + }), } } } @@ -141,7 +143,6 @@ pub struct AndroidAppInner { } impl AndroidAppInner { - pub fn native_window<'a>(&self) -> Option { let guard = self.native_window.read().unwrap(); if let Some(ref window) = *guard { @@ -152,7 +153,8 @@ impl AndroidAppInner { } pub fn poll_events(&self, timeout: Option, mut callback: F) - where F: FnMut(PollEvent) + where + F: FnMut(PollEvent), { trace!("poll_events"); @@ -163,9 +165,18 @@ impl AndroidAppInner { let mut events: i32 = 0; let mut source: *mut core::ffi::c_void = ptr::null_mut(); - let timeout_milliseconds = if let Some(timeout) = timeout { timeout.as_millis() as i32 } else { -1 }; + let timeout_milliseconds = if let Some(timeout) = timeout { + timeout.as_millis() as i32 + } else { + -1 + }; trace!("Calling ALooper_pollAll, timeout = {timeout_milliseconds}"); - let id = ALooper_pollAll(timeout_milliseconds, &mut fd, &mut events, &mut source as *mut *mut core::ffi::c_void); + let id = ALooper_pollAll( + 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"); @@ -200,23 +211,49 @@ impl AndroidAppInner { let cmd = match cmd_i as u32 { //NativeAppGlueAppCmd_UNUSED_APP_CMD_INPUT_CHANGED => AndroidAppMainEvent::InputChanged, - ffi::NativeAppGlueAppCmd_APP_CMD_INIT_WINDOW => MainEvent::InitWindow {}, - ffi::NativeAppGlueAppCmd_APP_CMD_TERM_WINDOW => MainEvent::TerminateWindow {}, - ffi::NativeAppGlueAppCmd_APP_CMD_WINDOW_RESIZED => MainEvent::WindowResized {}, - ffi::NativeAppGlueAppCmd_APP_CMD_WINDOW_REDRAW_NEEDED => MainEvent::RedrawNeeded {}, - ffi::NativeAppGlueAppCmd_APP_CMD_CONTENT_RECT_CHANGED => MainEvent::ContentRectChanged, - ffi::NativeAppGlueAppCmd_APP_CMD_GAINED_FOCUS => MainEvent::GainedFocus, - ffi::NativeAppGlueAppCmd_APP_CMD_LOST_FOCUS => MainEvent::LostFocus, - ffi::NativeAppGlueAppCmd_APP_CMD_CONFIG_CHANGED => MainEvent::ConfigChanged, - ffi::NativeAppGlueAppCmd_APP_CMD_LOW_MEMORY => MainEvent::LowMemory, + ffi::NativeAppGlueAppCmd_APP_CMD_INIT_WINDOW => { + MainEvent::InitWindow {} + } + ffi::NativeAppGlueAppCmd_APP_CMD_TERM_WINDOW => { + MainEvent::TerminateWindow {} + } + ffi::NativeAppGlueAppCmd_APP_CMD_WINDOW_RESIZED => { + MainEvent::WindowResized {} + } + ffi::NativeAppGlueAppCmd_APP_CMD_WINDOW_REDRAW_NEEDED => { + MainEvent::RedrawNeeded {} + } + ffi::NativeAppGlueAppCmd_APP_CMD_CONTENT_RECT_CHANGED => { + MainEvent::ContentRectChanged + } + ffi::NativeAppGlueAppCmd_APP_CMD_GAINED_FOCUS => { + MainEvent::GainedFocus + } + ffi::NativeAppGlueAppCmd_APP_CMD_LOST_FOCUS => { + MainEvent::LostFocus + } + ffi::NativeAppGlueAppCmd_APP_CMD_CONFIG_CHANGED => { + MainEvent::ConfigChanged + } + ffi::NativeAppGlueAppCmd_APP_CMD_LOW_MEMORY => { + MainEvent::LowMemory + } ffi::NativeAppGlueAppCmd_APP_CMD_START => MainEvent::Start, - ffi::NativeAppGlueAppCmd_APP_CMD_RESUME => MainEvent::Resume { loader: StateLoader { app: &self } }, - ffi::NativeAppGlueAppCmd_APP_CMD_SAVE_STATE => MainEvent::SaveState { saver: StateSaver { app: &self } }, + ffi::NativeAppGlueAppCmd_APP_CMD_RESUME => MainEvent::Resume { + loader: StateLoader { app: &self }, + }, + ffi::NativeAppGlueAppCmd_APP_CMD_SAVE_STATE => { + MainEvent::SaveState { + saver: StateSaver { app: &self }, + } + } ffi::NativeAppGlueAppCmd_APP_CMD_PAUSE => MainEvent::Pause, ffi::NativeAppGlueAppCmd_APP_CMD_STOP => MainEvent::Stop, ffi::NativeAppGlueAppCmd_APP_CMD_DESTROY => MainEvent::Destroy, - ffi::NativeAppGlueAppCmd_APP_CMD_WINDOW_INSETS_CHANGED => MainEvent::InsetsChanged {}, - _ => unreachable!() + ffi::NativeAppGlueAppCmd_APP_CMD_WINDOW_INSETS_CHANGED => { + MainEvent::InsetsChanged {} + } + _ => unreachable!(), }; trace!("Read ID_MAIN command {cmd_i} = {cmd:?}"); @@ -226,12 +263,15 @@ impl AndroidAppInner { match cmd { MainEvent::ConfigChanged => { *self.config.write().unwrap() = - Configuration::clone_from_ptr(NonNull::new_unchecked((*app_ptr.as_ptr()).config)); + Configuration::clone_from_ptr(NonNull::new_unchecked( + (*app_ptr.as_ptr()).config, + )); } MainEvent::InitWindow { .. } => { let win_ptr = (*app_ptr.as_ptr()).window; - *self.native_window.write().unwrap() = - Some(NativeWindow::from_ptr(NonNull::new(win_ptr).unwrap())); + *self.native_window.write().unwrap() = Some( + NativeWindow::from_ptr(NonNull::new(win_ptr).unwrap()), + ); } MainEvent::TerminateWindow { .. } => { *self.native_window.write().unwrap() = None; @@ -249,10 +289,17 @@ impl AndroidAppInner { } } _ => { - let events = FdEvent::from_bits(events as u32) - .expect(&format!("Spurious ALooper_pollAll event flags {:#04x}", events as u32)); + let events = FdEvent::from_bits(events as u32).expect(&format!( + "Spurious ALooper_pollAll event flags {:#04x}", + events as u32 + )); trace!("Custom ALooper event source: id = {id}, fd = {fd}, events = {events:?}, data = {source:?}"); - callback(PollEvent::FdEvent{ ident: id, fd: fd as RawFd, events, data: source }); + callback(PollEvent::FdEvent { + ident: id, + fd: fd as RawFd, + events, + data: source, + }); } } } @@ -264,15 +311,11 @@ impl AndroidAppInner { } pub fn enable_motion_axis(&self, axis: Axis) { - unsafe { - ffi::GameActivityPointerAxes_enableAxis(axis as i32) - } + unsafe { ffi::GameActivityPointerAxes_enableAxis(axis as i32) } } pub fn disable_motion_axis(&self, axis: Axis) { - unsafe { - ffi::GameActivityPointerAxes_disableAxis(axis as i32) - } + unsafe { ffi::GameActivityPointerAxes_disableAxis(axis as i32) } } pub fn create_waker(&self) -> AndroidAppWaker { @@ -280,7 +323,9 @@ impl AndroidAppInner { // 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(); - AndroidAppWaker { looper: NonNull::new_unchecked((*app_ptr).looper) } + AndroidAppWaker { + looper: NonNull::new_unchecked((*app_ptr).looper), + } } } @@ -309,7 +354,8 @@ impl AndroidAppInner { } pub fn input_events<'b, F>(&self, mut callback: F) - where F: FnMut(&InputEvent) + where + F: FnMut(&InputEvent), { let buf = unsafe { let app_ptr = self.ptr.as_ptr(); @@ -329,12 +375,16 @@ impl AndroidAppInner { } fn try_get_path_from_ptr(path: *const c_char) -> Option { - if path == ptr::null() { return None; } + if path == ptr::null() { + return None; + } let cstr = unsafe { let cstr_slice = CStr::from_ptr(path); cstr_slice.to_str().ok()? }; - if cstr.len() == 0 { return None; } + if cstr.len() == 0 { + return None; + } Some(std::path::PathBuf::from(cstr)) } @@ -363,7 +413,7 @@ impl AndroidAppInner { struct MotionEventsIterator<'a> { pos: usize, count: usize, - buffer: &'a InputBuffer<'a> + buffer: &'a InputBuffer<'a>, } impl<'a> Iterator for MotionEventsIterator<'a> { @@ -386,7 +436,7 @@ impl<'a> Iterator for MotionEventsIterator<'a> { struct KeyEventsIterator<'a> { pos: usize, count: usize, - buffer: &'a InputBuffer<'a> + buffer: &'a InputBuffer<'a>, } impl<'a> Iterator for KeyEventsIterator<'a> { @@ -408,14 +458,14 @@ impl<'a> Iterator for KeyEventsIterator<'a> { struct InputBuffer<'a> { ptr: NonNull, - _lifetime: PhantomData<&'a ffi::android_input_buffer> + _lifetime: PhantomData<&'a ffi::android_input_buffer>, } impl<'a> InputBuffer<'a> { pub(crate) fn from_ptr(ptr: NonNull) -> InputBuffer<'a> { Self { ptr, - _lifetime: PhantomData::default() + _lifetime: PhantomData::default(), } } @@ -425,14 +475,22 @@ impl<'a> InputBuffer<'a> { pub fn motion_events_iter<'b>(&'b self) -> MotionEventsIterator<'b> { unsafe { let count = (*self.ptr.as_ptr()).motionEventsCount as usize; - MotionEventsIterator { pos: 0, count, buffer: self } + MotionEventsIterator { + pos: 0, + count, + buffer: self, + } } } pub fn key_events_iter<'b>(&'b self) -> KeyEventsIterator<'b> { unsafe { let count = (*self.ptr.as_ptr()).keyEventsCount as usize; - KeyEventsIterator { pos: 0, count, buffer: self } + KeyEventsIterator { + pos: 0, + count, + buffer: self, + } } } } @@ -473,28 +531,35 @@ extern "C" { #[no_mangle] pub unsafe extern "C" fn Java_com_google_androidgamesdk_GameActivity_loadNativeCode( - env: *mut JNIEnv, - java_game_activity: jobject, - path: jstring, - func_name: jstring, - internal_data_dir: jstring, - obb_dir: jstring, - external_data_dir: jstring, - jasset_mgr: jobject, - saved_state: jbyteArray, - ) -> jni_sys::jlong -{ - Java_com_google_androidgamesdk_GameActivity_loadNativeCode_C(env, java_game_activity, path, func_name, - internal_data_dir, obb_dir, external_data_dir, jasset_mgr, saved_state) + env: *mut JNIEnv, + java_game_activity: jobject, + path: jstring, + func_name: jstring, + internal_data_dir: jstring, + obb_dir: jstring, + external_data_dir: jstring, + jasset_mgr: jobject, + saved_state: jbyteArray, +) -> jni_sys::jlong { + Java_com_google_androidgamesdk_GameActivity_loadNativeCode_C( + env, + java_game_activity, + path, + func_name, + internal_data_dir, + obb_dir, + external_data_dir, + jasset_mgr, + saved_state, + ) } #[no_mangle] pub unsafe extern "C" fn GameActivity_onCreate( - activity: *mut ffi::GameActivity, - saved_state: *mut ::std::os::raw::c_void, - saved_state_size: ffi::size_t, - ) -{ + activity: *mut ffi::GameActivity, + saved_state: *mut ::std::os::raw::c_void, + saved_state_size: ffi::size_t, +) { GameActivity_onCreate_C(activity, saved_state, saved_state_size); } @@ -520,7 +585,6 @@ extern "Rust" { // by android_native_app_glue. #[no_mangle] pub unsafe extern "C" fn _rust_glue_entry(app: *mut ffi::android_app) { - // Maybe make this stdout/stderr redirection an optional / opt-in feature?... let mut logpipe: [RawFd; 2] = Default::default(); libc::pipe(logpipe.as_mut_ptr()); @@ -575,4 +639,4 @@ pub unsafe extern "C" fn _rust_glue_entry(app: *mut ffi::android_app) { } ndk_context::release_android_context(); -} \ No newline at end of file +} diff --git a/android-activity/src/lib.rs b/android-activity/src/lib.rs index ebd1b7d..f6467b5 100644 --- a/android-activity/src/lib.rs +++ b/android-activity/src/lib.rs @@ -1,7 +1,7 @@ -use std::time::Duration; -use std::{os::unix::prelude::RawFd, sync::Arc}; use std::hash::Hash; use std::ops::Deref; +use std::time::Duration; +use std::{os::unix::prelude::RawFd, sync::Arc}; use ndk::asset::AssetManager; use ndk::configuration::Configuration; @@ -14,19 +14,23 @@ use ndk::native_window::NativeWindow; compile_error!("android-activity only supports compiling for Android"); #[cfg(all(feature = "game-activity", feature = "native-activity"))] -compile_error!("The \"game-activity\" and \"native-activity\" features cannot be enabled at the same time"); -#[cfg(all(not(any(feature = "game-activity", feature = "native-activity")), not(doc)))] +compile_error!( + "The \"game-activity\" and \"native-activity\" features cannot be enabled at the same time" +); +#[cfg(all( + not(any(feature = "game-activity", feature = "native-activity")), + not(doc) +))] compile_error!("Either \"game-activity\" or \"native-activity\" must be enabled as features"); - -#[cfg(any(feature="native-activity", doc))] +#[cfg(any(feature = "native-activity", doc))] mod native_activity; -#[cfg(any(feature="native-activity", doc))] +#[cfg(any(feature = "native-activity", doc))] use native_activity as activity_impl; -#[cfg(feature="game-activity")] +#[cfg(feature = "game-activity")] mod game_activity; -#[cfg(feature="game-activity")] +#[cfg(feature = "game-activity")] use game_activity as activity_impl; pub use activity_impl::input; @@ -54,12 +58,16 @@ pub struct Rect { /// A reference to a `NativeWindow`, used for rendering pub struct NativeWindowRef { - inner: NativeWindow + 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() } + unsafe { + ndk_sys::ANativeWindow_acquire(native_window.ptr().as_ptr()); + } + Self { + inner: native_window.clone(), + } } } impl Drop for NativeWindowRef { @@ -100,7 +108,7 @@ pub enum MainEvent<'a> { /// Command from main thread: a new [`NativeWindow`] is ready for use. Upon /// receiving this command, [`native_window()`] will return the new window #[non_exhaustive] - InitWindow { }, + InitWindow {}, /// Command from main thread: the existing [`NativeWindow`] needs to be /// terminated. Upon receiving this command, [`native_window()`] still @@ -181,9 +189,14 @@ pub enum PollEvent<'a> { Main(MainEvent<'a>), #[non_exhaustive] - FdEvent { ident: i32, fd: RawFd, events: FdEvent, data: *mut std::ffi::c_void }, + FdEvent { + ident: i32, + fd: RawFd, + events: FdEvent, + data: *mut std::ffi::c_void, + }, - Error + Error, } use activity_impl::AndroidAppInner; @@ -192,7 +205,7 @@ pub use activity_impl::AndroidAppWaker; #[derive(Debug, Clone)] pub struct AndroidApp { - pub(crate) inner: Arc + pub(crate) inner: Arc, } impl PartialEq for AndroidApp { @@ -239,7 +252,8 @@ impl AndroidApp { /// # Safety /// This API must only be called from the application's main thread pub fn poll_events(&self, timeout: Option, callback: F) - where F: FnMut(PollEvent) + where + F: FnMut(PollEvent), { self.inner.poll_events(timeout, callback); } @@ -289,7 +303,8 @@ impl AndroidApp { } pub fn input_events<'b, F>(&self, callback: F) - where F: FnMut(&input::InputEvent) + where + F: FnMut(&input::InputEvent), { self.inner.input_events(callback); } @@ -320,4 +335,4 @@ impl AndroidApp { pub fn obb_path(&self) -> Option { self.inner.obb_path() } -} \ No newline at end of file +} diff --git a/android-activity/src/native_activity/ffi.rs b/android-activity/src/native_activity/ffi.rs index c8ea1fd..be3e68e 100644 --- a/android-activity/src/native_activity/ffi.rs +++ b/android-activity/src/native_activity/ffi.rs @@ -15,8 +15,7 @@ use jni_sys::*; use ndk_sys::AAssetManager; use ndk_sys::ANativeWindow; -use ndk_sys::{ALooper, AConfiguration, AInputQueue}; - +use ndk_sys::{AConfiguration, AInputQueue, ALooper}; #[cfg(all( any(target_os = "android", feature = "test"), @@ -31,4 +30,4 @@ include!("ffi_aarch64.rs"); include!("ffi_i686.rs"); #[cfg(all(any(target_os = "android", feature = "test"), target_arch = "x86_64"))] -include!("ffi_x86_64.rs"); \ No newline at end of file +include!("ffi_x86_64.rs"); diff --git a/android-activity/src/native_activity/mod.rs b/android-activity/src/native_activity/mod.rs index e00e25a..1bc42a8 100644 --- a/android-activity/src/native_activity/mod.rs +++ b/android-activity/src/native_activity/mod.rs @@ -1,17 +1,17 @@ -#![cfg(any(feature="native-activity", doc))] +#![cfg(any(feature = "native-activity", doc))] use std::ffi::{CStr, CString}; use std::fs::File; use std::io::{BufRead, BufReader}; use std::os::raw; +use std::os::unix::prelude::*; use std::ptr::NonNull; use std::sync::Arc; use std::sync::RwLock; use std::time::Duration; -use std::{thread, ptr}; -use std::os::unix::prelude::*; +use std::{ptr, thread}; -use log::{Level, error, info, trace}; +use log::{error, info, trace, Level}; use ndk_sys::ALooper_wake; use ndk_sys::{ALooper, ALooper_pollAll}; @@ -19,19 +19,17 @@ use ndk_sys::{ALooper, ALooper_pollAll}; use ndk::asset::AssetManager; use ndk::configuration::Configuration; use ndk::input_queue::InputQueue; -use ndk::looper::{FdEvent}; +use ndk::looper::FdEvent; use ndk::native_window::NativeWindow; -use crate::{MainEvent, Rect, PollEvent, AndroidApp, NativeWindowRef}; - +use crate::{AndroidApp, MainEvent, NativeWindowRef, PollEvent, Rect}; mod ffi; pub mod input { pub use ndk::event::{ - InputEvent, Source, MetaState, - MotionEvent, Pointer, MotionAction, Axis, ButtonState, EdgeFlags, MotionEventFlags, - KeyEvent, KeyAction, Keycode, KeyEventFlags, + Axis, ButtonState, EdgeFlags, InputEvent, KeyAction, KeyEvent, KeyEventFlags, Keycode, + MetaState, MotionAction, MotionEvent, MotionEventFlags, Pointer, Source, }; } @@ -45,7 +43,6 @@ pub struct StateSaver<'a> { impl<'a> StateSaver<'a> { pub fn store(&self, state: &'a [u8]) { - // android_native_app_glue specifically expects savedState to have been allocated // via libc::malloc since it will automatically handle freeing the data once it // has been handed over to the Java Activity / main thread. @@ -89,7 +86,10 @@ impl<'a> StateLoader<'a> { unsafe { let app_ptr = self.app.ptr.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(), (*app_ptr).savedStateSize as usize); + let buf: &mut [u8] = std::slice::from_raw_parts_mut( + (*app_ptr).savedState.cast(), + (*app_ptr).savedStateSize as usize, + ); let state = buf.to_vec(); Some(state) } else { @@ -99,26 +99,26 @@ impl<'a> StateLoader<'a> { } } - #[derive(Clone)] 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 {} impl AndroidAppWaker { pub fn wake(&self) { - unsafe { ALooper_wake(self.looper.as_ptr()); } + unsafe { + ALooper_wake(self.looper.as_ptr()); + } } } impl AndroidApp { pub(crate) unsafe fn from_ptr(ptr: NonNull) -> AndroidApp { - // Note: we don't use from_ptr since we don't own the android_app.config // and need to keep in mind that the Drop handler is going to call // AConfiguration_delete() @@ -131,8 +131,8 @@ impl AndroidApp { inner: Arc::new(AndroidAppInner { ptr, config: RwLock::new(config), - native_window: Default::default() - }) + native_window: Default::default(), + }), } } } @@ -162,7 +162,8 @@ impl AndroidAppInner { } pub fn poll_events(&self, timeout: Option, mut callback: F) - where F: FnMut(PollEvent) + where + F: FnMut(PollEvent), { trace!("poll_events"); @@ -173,9 +174,18 @@ impl AndroidAppInner { let mut events: i32 = 0; let mut source: *mut core::ffi::c_void = ptr::null_mut(); - let timeout_milliseconds = if let Some(timeout) = timeout { timeout.as_millis() as i32 } else { -1 }; + let timeout_milliseconds = if let Some(timeout) = timeout { + timeout.as_millis() as i32 + } else { + -1 + }; info!("Calling ALooper_pollAll, timeout = {timeout_milliseconds}"); - let id = ALooper_pollAll(timeout_milliseconds, &mut fd, &mut events, &mut source as *mut *mut core::ffi::c_void); + let id = ALooper_pollAll( + timeout_milliseconds, + &mut fd, + &mut events, + &mut source as *mut *mut core::ffi::c_void, + ); info!("pollAll id = {id}"); match id { ffi::ALOOPER_POLL_WAKE => { @@ -217,22 +227,32 @@ impl AndroidAppInner { ffi::APP_CMD_INIT_WINDOW => Some(MainEvent::InitWindow {}), ffi::APP_CMD_TERM_WINDOW => Some(MainEvent::TerminateWindow {}), - ffi::APP_CMD_WINDOW_RESIZED => Some(MainEvent::WindowResized {}), - ffi::APP_CMD_WINDOW_REDRAW_NEEDED => Some(MainEvent::RedrawNeeded {}), - ffi::APP_CMD_CONTENT_RECT_CHANGED => Some(MainEvent::ContentRectChanged), + ffi::APP_CMD_WINDOW_RESIZED => { + Some(MainEvent::WindowResized {}) + } + ffi::APP_CMD_WINDOW_REDRAW_NEEDED => { + Some(MainEvent::RedrawNeeded {}) + } + ffi::APP_CMD_CONTENT_RECT_CHANGED => { + Some(MainEvent::ContentRectChanged) + } ffi::APP_CMD_GAINED_FOCUS => Some(MainEvent::GainedFocus), ffi::APP_CMD_LOST_FOCUS => Some(MainEvent::LostFocus), ffi::APP_CMD_CONFIG_CHANGED => Some(MainEvent::ConfigChanged), ffi::APP_CMD_LOW_MEMORY => Some(MainEvent::LowMemory), ffi::APP_CMD_START => Some(MainEvent::Start), - ffi::APP_CMD_RESUME => Some(MainEvent::Resume { loader: StateLoader { app: &self } }), - ffi::APP_CMD_SAVE_STATE => Some(MainEvent::SaveState { saver: StateSaver { app: &self } }), + ffi::APP_CMD_RESUME => Some(MainEvent::Resume { + loader: StateLoader { app: &self }, + }), + ffi::APP_CMD_SAVE_STATE => Some(MainEvent::SaveState { + saver: StateSaver { app: &self }, + }), ffi::APP_CMD_PAUSE => Some(MainEvent::Pause), ffi::APP_CMD_STOP => Some(MainEvent::Stop), ffi::APP_CMD_DESTROY => Some(MainEvent::Destroy), //ffi::NativeAppGlueAppCmd_APP_CMD_WINDOW_INSETS_CHANGED => MainEvent::InsetsChanged {}, - _ => unreachable!() + _ => unreachable!(), }; trace!("Calling android_app_pre_exec_cmd({cmd_i})"); @@ -243,12 +263,18 @@ impl AndroidAppInner { match cmd { MainEvent::ConfigChanged => { *self.config.write().unwrap() = - Configuration::clone_from_ptr(NonNull::new_unchecked((*app_ptr.as_ptr()).config)); + Configuration::clone_from_ptr( + NonNull::new_unchecked( + (*app_ptr.as_ptr()).config, + ), + ); } MainEvent::InitWindow { .. } => { let win_ptr = (*app_ptr.as_ptr()).window; *self.native_window.write().unwrap() = - Some(NativeWindow::from_ptr(NonNull::new(win_ptr).unwrap())); + Some(NativeWindow::from_ptr( + NonNull::new(win_ptr).unwrap(), + )); } MainEvent::TerminateWindow { .. } => { *self.native_window.write().unwrap() = None; @@ -277,10 +303,17 @@ impl AndroidAppInner { callback(PollEvent::InputAvailable) } _ => { - let events = FdEvent::from_bits(events as u32) - .expect(&format!("Spurious ALooper_pollAll event flags {:#04x}", events as u32)); + let events = FdEvent::from_bits(events as u32).expect(&format!( + "Spurious ALooper_pollAll event flags {:#04x}", + events as u32 + )); trace!("Custom ALooper event source: id = {id}, fd = {fd}, events = {events:?}, data = {source:?}"); - callback(PollEvent::FdEvent{ ident: id, fd: fd as RawFd, events, data: source }); + callback(PollEvent::FdEvent { + ident: id, + fd: fd as RawFd, + events, + data: source, + }); } } } @@ -296,7 +329,9 @@ impl AndroidAppInner { // 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(); - AndroidAppWaker { looper: NonNull::new_unchecked((*app_ptr).looper) } + AndroidAppWaker { + looper: NonNull::new_unchecked((*app_ptr).looper), + } } } @@ -333,7 +368,8 @@ impl AndroidAppInner { } pub fn input_events<'b, F>(&self, mut callback: F) - where F: FnMut(&input::InputEvent) + where + F: FnMut(&input::InputEvent), { // Reattach the input queue to the looper so future input will again deliver an // `InputAvailable` event. @@ -369,12 +405,16 @@ impl AndroidAppInner { } fn try_get_path_from_ptr(path: *const u8) -> Option { - if path == ptr::null() { return None; } + if path == ptr::null() { + return None; + } let cstr = unsafe { let cstr_slice = CStr::from_ptr(path); cstr_slice.to_str().ok()? }; - if cstr.len() == 0 { return None; } + if cstr.len() == 0 { + return None; + } Some(std::path::PathBuf::from(cstr)) } @@ -438,7 +478,6 @@ extern "Rust" { // by android_native_app_glue. #[no_mangle] pub unsafe extern "C" fn _rust_glue_entry(app: *mut ffi::android_app) { - // Maybe make this stdout/stderr redirection an optional / opt-in feature?... let mut logpipe: [RawFd; 2] = Default::default(); libc::pipe(logpipe.as_mut_ptr()); @@ -494,4 +533,4 @@ pub unsafe extern "C" fn _rust_glue_entry(app: *mut ffi::android_app) { } ndk_context::release_android_context(); -} \ No newline at end of file +} diff --git a/examples/agdk-egui/src/lib.rs b/examples/agdk-egui/src/lib.rs index f5eb43c..dc21248 100644 --- a/examples/agdk-egui/src/lib.rs +++ b/examples/agdk-egui/src/lib.rs @@ -1,14 +1,12 @@ use log::Level; -use winit::event_loop::{EventLoopWindowTarget, EventLoopBuilder, EventLoop}; +use winit::event_loop::{EventLoop, EventLoopBuilder, EventLoopWindowTarget}; -#[cfg(target_os="android")] +#[cfg(target_os = "android")] use android_activity::AndroidApp; -#[cfg(target_os="android")] +#[cfg(target_os = "android")] use winit::platform::android::EventLoopBuilderExtAndroid; -use winit::{ - event_loop::{ControlFlow}, -}; +use winit::event_loop::ControlFlow; use egui_wgpu::winit::Painter; use egui_winit::State; @@ -26,8 +24,11 @@ enum Event { #[derive(Clone)] struct RepaintSignal(std::sync::Arc>>); - -fn create_window(event_loop: &EventLoopWindowTarget, state: &mut State, painter: &mut Painter) -> winit::window::Window { +fn create_window( + event_loop: &EventLoopWindowTarget, + state: &mut State, + painter: &mut Painter, +) -> winit::window::Window { let window = winit::window::WindowBuilder::new() .with_decorations(true) .with_resizable(true) @@ -37,8 +38,8 @@ fn create_window(event_loop: &EventLoopWindowTarget, state: &mut State, pa width: INITIAL_WIDTH, height: INITIAL_HEIGHT, }) - .build(&event_loop) - .unwrap(); + .build(&event_loop) + .unwrap(); unsafe { painter.set_window(Some(&window)) }; @@ -60,10 +61,15 @@ fn create_window(event_loop: &EventLoopWindowTarget, state: &mut State, pa fn _main(event_loop: EventLoop) { let ctx = egui::Context::default(); let repaint_signal = RepaintSignal(std::sync::Arc::new(std::sync::Mutex::new( - event_loop.create_proxy() + event_loop.create_proxy(), ))); ctx.set_request_repaint_callback(move || { - repaint_signal.0.lock().unwrap().send_event(Event::RequestRedraw).ok(); + repaint_signal + .0 + .lock() + .unwrap() + .send_event(Event::RequestRedraw) + .ok(); }); let mut state = State::new(&event_loop); @@ -73,90 +79,85 @@ fn _main(event_loop: EventLoop) { wgpu::DeviceDescriptor { label: None, features: wgpu::Features::default(), - limits: wgpu::Limits::default() + limits: wgpu::Limits::default(), }, wgpu::PresentMode::Fifo, - 1); + 1, + ); let mut window: Option = None; let mut egui_demo_windows = egui_demo_lib::DemoWindows::default(); // On most platforms we can immediately create a winit window. On Android we manage // window + surface state according to Resumed/Paused events. - #[cfg(not(target_os="android"))] + #[cfg(not(target_os = "android"))] { window = Some(create_window(&event_loop, &mut state, &mut painter)); } - event_loop.run(move |event, event_loop, control_flow| { - - match event { - #[cfg(target_os="android")] - Resumed => { - match window { - None => { - window = Some(create_window(event_loop, &mut state, &mut painter)); - } - Some(ref window) => { - unsafe { painter.set_window(Some(window)) }; - window.request_redraw(); - } - } + event_loop.run(move |event, event_loop, control_flow| match event { + #[cfg(target_os = "android")] + Resumed => match window { + None => { + window = Some(create_window(event_loop, &mut state, &mut painter)); } - - #[cfg(target_os="android")] - Suspended => { - window = None; + Some(ref window) => { + unsafe { painter.set_window(Some(window)) }; + window.request_redraw(); } + }, - RedrawRequested(..) => { - if let Some(window) = window.as_ref() { - let raw_input = state.take_egui_input(window); + #[cfg(target_os = "android")] + Suspended => { + window = None; + } - let full_output = ctx.run(raw_input, |ctx| { - egui_demo_windows.ui(ctx); - }); - state.handle_platform_output(window, &ctx, full_output.platform_output); + RedrawRequested(..) => { + if let Some(window) = window.as_ref() { + let raw_input = state.take_egui_input(window); - painter.paint_and_update_textures(state.pixels_per_point(), - egui::Rgba::default(), - &ctx.tessellate(full_output.shapes), - &full_output.textures_delta); + let full_output = ctx.run(raw_input, |ctx| { + egui_demo_windows.ui(ctx); + }); + state.handle_platform_output(window, &ctx, full_output.platform_output); - if full_output.repaint_after.is_zero() { - window.request_redraw(); - } - } - } - MainEventsCleared | UserEvent(Event::RequestRedraw) => { - if let Some(window) = window.as_ref() { + painter.paint_and_update_textures( + state.pixels_per_point(), + egui::Rgba::default(), + &ctx.tessellate(full_output.shapes), + &full_output.textures_delta, + ); + + if full_output.repaint_after.is_zero() { window.request_redraw(); } } - WindowEvent { event, .. } => { - if state.on_event(&ctx, &event) == false { - match event { - winit::event::WindowEvent::Resized(size) => { - painter.on_window_resized(size.width, size.height); - } - winit::event::WindowEvent::CloseRequested => { - *control_flow = ControlFlow::Exit; - } - _ => {} - } - } - }, - _ => (), } + MainEventsCleared | UserEvent(Event::RequestRedraw) => { + if let Some(window) = window.as_ref() { + window.request_redraw(); + } + } + WindowEvent { event, .. } => { + if state.on_event(&ctx, &event) == false { + match event { + winit::event::WindowEvent::Resized(size) => { + painter.on_window_resized(size.width, size.height); + } + winit::event::WindowEvent::CloseRequested => { + *control_flow = ControlFlow::Exit; + } + _ => {} + } + } + } + _ => (), }); } - -#[cfg(target_os="android")] +#[cfg(target_os = "android")] #[no_mangle] fn android_main(app: AndroidApp) { - android_logger::init_once( - android_logger::Config::default().with_min_level(Level::Trace) - ); + android_logger::init_once(android_logger::Config::default().with_min_level(Level::Trace)); let event_loop = EventLoopBuilder::with_user_event() .with_android_app(app) @@ -164,12 +165,13 @@ fn android_main(app: AndroidApp) { _main(event_loop); } -#[cfg(not(target_os="android"))] +#[cfg(not(target_os = "android"))] fn main() { - env_logger::builder().filter_level(log::LevelFilter::Warn) // Default Log Level + env_logger::builder() + .filter_level(log::LevelFilter::Warn) // Default Log Level .parse_default_env() .init(); let event_loop = EventLoopBuilder::with_user_event().build(); _main(event_loop); -} \ No newline at end of file +} diff --git a/examples/agdk-mainloop/src/lib.rs b/examples/agdk-mainloop/src/lib.rs index 145f96a..cb98354 100644 --- a/examples/agdk-mainloop/src/lib.rs +++ b/examples/agdk-mainloop/src/lib.rs @@ -1,70 +1,76 @@ -use android_activity::{AndroidApp, PollEvent, MainEvent}; +use android_activity::{AndroidApp, MainEvent, PollEvent}; use log::info; #[no_mangle] fn android_main(app: AndroidApp) { - android_logger::init_once( - android_logger::Config::default().with_min_level(log::Level::Info) - ); + android_logger::init_once(android_logger::Config::default().with_min_level(log::Level::Info)); let mut quit = false; let mut redraw_pending = true; let mut render_state: Option<()> = Default::default(); while !quit { - app.poll_events(Some(std::time::Duration::from_millis(500)) /* timeout */, |event| { - match event { - PollEvent::Wake => { info!("Early wake up"); }, - PollEvent::Timeout => { - info!("Timed out"); - // Real app would probably rely on vblank sync via graphics API... - redraw_pending = true; - }, - PollEvent::Main(main_event) => { - info!("Main event: {:?}", main_event); - match main_event { - MainEvent::SaveState { saver, .. } => { - saver.store("foo://bar".as_bytes()); - }, - MainEvent::Pause => {}, - MainEvent::Resume { loader, .. } => { - if let Some(state) = loader.load() { - if let Ok(uri) = String::from_utf8(state) { - info!("Resumed with saved state = {uri:#?}"); + app.poll_events( + Some(std::time::Duration::from_millis(500)), /* timeout */ + |event| { + match event { + PollEvent::Wake => { + info!("Early wake up"); + } + PollEvent::Timeout => { + info!("Timed out"); + // Real app would probably rely on vblank sync via graphics API... + redraw_pending = true; + } + PollEvent::Main(main_event) => { + info!("Main event: {:?}", main_event); + match main_event { + MainEvent::SaveState { saver, .. } => { + saver.store("foo://bar".as_bytes()); + } + MainEvent::Pause => {} + MainEvent::Resume { loader, .. } => { + if let Some(state) = loader.load() { + if let Ok(uri) = String::from_utf8(state) { + info!("Resumed with saved state = {uri:#?}"); + } } } - }, - MainEvent::InitWindow { .. } => { - render_state = Some(()); - redraw_pending = true; - }, - MainEvent::TerminateWindow { .. } => { - render_state = None; + MainEvent::InitWindow { .. } => { + render_state = Some(()); + redraw_pending = true; + } + MainEvent::TerminateWindow { .. } => { + render_state = None; + } + MainEvent::WindowResized { .. } => { + redraw_pending = true; + } + MainEvent::RedrawNeeded { .. } => { + redraw_pending = true; + } + MainEvent::LowMemory => {} + + MainEvent::Destroy => quit = true, + _ => { /* ... */ } } - MainEvent::WindowResized { .. } => { redraw_pending = true; }, - MainEvent::RedrawNeeded { ..} => { redraw_pending = true; }, - MainEvent::LowMemory => {}, - - MainEvent::Destroy => { quit = true }, - _ => { /* ... */} } - }, - _ => {} - } - - if redraw_pending { - if let Some(_rs) = render_state { - redraw_pending = false; - - // Handle input - app.input_events(|event| { - info!("Input Event: {event:?}"); - - }); - - info!("Render..."); + _ => {} } - } - }); + + if redraw_pending { + if let Some(_rs) = render_state { + redraw_pending = false; + + // Handle input + app.input_events(|event| { + info!("Input Event: {event:?}"); + }); + + info!("Render..."); + } + } + }, + ); } -} \ No newline at end of file +} diff --git a/examples/agdk-oboe/src/audio.rs b/examples/agdk-oboe/src/audio.rs index ae45a35..9cc7f34 100644 --- a/examples/agdk-oboe/src/audio.rs +++ b/examples/agdk-oboe/src/audio.rs @@ -217,4 +217,4 @@ pub fn audio_probe() { println!(" Formats: {:?}", device.formats); println!("}}"); } -} \ No newline at end of file +} diff --git a/examples/agdk-oboe/src/lib.rs b/examples/agdk-oboe/src/lib.rs index 979daa8..d03d044 100644 --- a/examples/agdk-oboe/src/lib.rs +++ b/examples/agdk-oboe/src/lib.rs @@ -1,4 +1,4 @@ -use android_activity::{AndroidApp, PollEvent, MainEvent}; +use android_activity::{AndroidApp, MainEvent, PollEvent}; use log::info; mod audio; @@ -6,9 +6,7 @@ use audio::*; #[no_mangle] fn android_main(app: AndroidApp) { - android_logger::init_once( - android_logger::Config::default().with_min_level(log::Level::Info) - ); + android_logger::init_once(android_logger::Config::default().with_min_level(log::Level::Info)); let mut quit = false; let mut redraw_pending = true; @@ -17,64 +15,72 @@ fn android_main(app: AndroidApp) { let mut sine = SineGen::default(); while !quit { - app.poll_events(Some(std::time::Duration::from_millis(500)) /* timeout */, |event| { - match event { - PollEvent::Wake => { info!("Early wake up"); }, - PollEvent::Timeout => { - info!("Timed out"); - // Real app would probably rely on vblank sync via graphics API... - redraw_pending = true; - }, - PollEvent::Main(main_event) => { - info!("Main event: {:?}", main_event); - match main_event { - MainEvent::SaveState { saver, .. } => { - saver.store("foo://bar".as_bytes()); - }, - MainEvent::Pause => { - sine.try_stop(); - }, - MainEvent::Resume { loader, .. } => { - if let Some(state) = loader.load() { - if let Ok(uri) = String::from_utf8(state) { - info!("Resumed with saved state = {uri:#?}"); - } - } - - audio_probe(); - sine.try_start(); - }, - MainEvent::InitWindow { .. } => { - render_state = Some(()); - redraw_pending = true; - }, - MainEvent::TerminateWindow { .. } => { - render_state = None; - } - MainEvent::WindowResized { .. } => { redraw_pending = true; }, - MainEvent::RedrawNeeded { ..} => { redraw_pending = true; }, - MainEvent::LowMemory => {}, - - MainEvent::Destroy => { quit = true }, - _ => { /* ... */} + app.poll_events( + Some(std::time::Duration::from_millis(500)), /* timeout */ + |event| { + match event { + PollEvent::Wake => { + info!("Early wake up"); } - }, - _ => {} - } + PollEvent::Timeout => { + info!("Timed out"); + // Real app would probably rely on vblank sync via graphics API... + redraw_pending = true; + } + PollEvent::Main(main_event) => { + info!("Main event: {:?}", main_event); + match main_event { + MainEvent::SaveState { saver, .. } => { + saver.store("foo://bar".as_bytes()); + } + MainEvent::Pause => { + sine.try_stop(); + } + MainEvent::Resume { loader, .. } => { + if let Some(state) = loader.load() { + if let Ok(uri) = String::from_utf8(state) { + info!("Resumed with saved state = {uri:#?}"); + } + } - if redraw_pending { - if let Some(_rs) = render_state { - redraw_pending = false; + audio_probe(); + sine.try_start(); + } + MainEvent::InitWindow { .. } => { + render_state = Some(()); + redraw_pending = true; + } + MainEvent::TerminateWindow { .. } => { + render_state = None; + } + MainEvent::WindowResized { .. } => { + redraw_pending = true; + } + MainEvent::RedrawNeeded { .. } => { + redraw_pending = true; + } + MainEvent::LowMemory => {} - // Handle input - app.input_events(|event| { - info!("Input Event: {event:?}"); - - }); - - info!("Render..."); + MainEvent::Destroy => quit = true, + _ => { /* ... */ } + } + } + _ => {} } - } - }); + + if redraw_pending { + if let Some(_rs) = render_state { + redraw_pending = false; + + // Handle input + app.input_events(|event| { + info!("Input Event: {event:?}"); + }); + + info!("Render..."); + } + } + }, + ); } -} \ No newline at end of file +} diff --git a/examples/agdk-winit-wgpu/src/lib.rs b/examples/agdk-winit-wgpu/src/lib.rs index dbbc4af..e882e21 100644 --- a/examples/agdk-winit-wgpu/src/lib.rs +++ b/examples/agdk-winit-wgpu/src/lib.rs @@ -1,22 +1,21 @@ - -use std::ops::Deref; use std::borrow::Cow; +use std::ops::Deref; use std::sync::{Arc, RwLock}; -use log::Level; use log::trace; +use log::Level; use wgpu::TextureFormat; -use wgpu::{Instance, Adapter, Device, ShaderModule, PipelineLayout, RenderPipeline, Queue}; +use wgpu::{Adapter, Device, Instance, PipelineLayout, Queue, RenderPipeline, ShaderModule}; use winit::{ event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop, EventLoopBuilder, EventLoopWindowTarget}, }; -#[cfg(target_os="android")] +#[cfg(target_os = "android")] use android_activity::AndroidApp; -#[cfg(target_os="android")] +#[cfg(target_os = "android")] use winit::platform::android::EventLoopBuilderExtAndroid; struct RenderState { @@ -30,7 +29,7 @@ struct RenderState { struct SurfaceState { window: winit::window::Window, - surface: wgpu::Surface + surface: wgpu::Surface, } struct AppInner { @@ -41,7 +40,7 @@ struct AppInner { } struct App { - inner: Arc> + inner: Arc>, } impl App { @@ -52,7 +51,7 @@ impl App { adapter: None, surface_state: None, render_state: None, - })) + })), } } } @@ -63,7 +62,6 @@ impl Deref for App { } } - async fn init_render_state(adapter: &Adapter, target_format: TextureFormat) -> RenderState { trace!("Initializing render state"); @@ -141,7 +139,9 @@ fn configure_surface_swapchain(render_state: &RenderState, surface_state: &Surfa }; trace!("WGPU: Configuring surface swapchain: format = {swapchain_format:?}, size = {size:?}"); - surface_state.surface.configure(&render_state.device, &config); + surface_state + .surface + .configure(&render_state.device, &config); } // We want to defer the initialization of our render state until @@ -155,7 +155,8 @@ async fn ensure_render_state_for_surface(app: &App, new_surface_state: &SurfaceS if app_guard.adapter.is_none() { trace!("WGPU: requesting a suitable adapter (compatible with our surface)"); - let adapter = app_guard.instance + let adapter = app_guard + .instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::default(), force_fallback_adapter: false, @@ -185,10 +186,7 @@ fn create_surface(app: &App, event_loop: &EventLoopWindowTarget) -> Surfac let guard = app.inner.read().unwrap(); let surface = unsafe { guard.instance.create_surface(&window) }; - SurfaceState { - window, - surface - } + SurfaceState { window, surface } } fn resume(app: &App, event_loop: &EventLoopWindowTarget) { @@ -209,9 +207,7 @@ fn resume(app: &App, event_loop: &EventLoopWindowTarget) { surface_state.window.request_redraw(); } - fn run(event_loop: EventLoop<()>, app: App) { - //let mut running = false; trace!("Running mainloop..."); @@ -225,7 +221,7 @@ fn run(event_loop: EventLoop<()>, app: App) { // Note: that because Winit doesn't currently support lifecycle events consistently // across platforms then we effectively issue a fake 'resume' on non-android // platforms... - #[cfg(not(target_os="android"))] + #[cfg(not(target_os = "android"))] resume(&app, event_loop) } Event::Resumed => { @@ -236,7 +232,7 @@ fn run(event_loop: EventLoop<()>, app: App) { let mut guard = app.write().unwrap(); //guard.running = false; guard.render_state = None; - }, + } Event::WindowEvent { event: WindowEvent::Resized(_size), .. @@ -258,27 +254,32 @@ fn run(event_loop: EventLoop<()>, app: App) { let guard = app.read().unwrap(); if let Some(ref surface_state) = guard.surface_state { if let Some(ref rs) = guard.render_state { - let frame = surface_state.surface + let frame = surface_state + .surface .get_current_texture() .expect("Failed to acquire next swap chain texture"); let view = frame .texture .create_view(&wgpu::TextureViewDescriptor::default()); let mut encoder = - rs.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + rs.device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: None, + }); { - let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), - store: true, - }, - })], - depth_stencil_attachment: None, - }); + let mut rpass = + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), + store: true, + }, + })], + depth_stencil_attachment: None, + }); rpass.set_pipeline(&rs.render_pipeline); rpass.draw(0..3, 0..1); } @@ -298,7 +299,6 @@ fn run(event_loop: EventLoop<()>, app: App) { }); } - fn _main(event_loop: EventLoop<()>) { // We can decide on our graphics API / backend up-front and that // doesn't need to be re-considered later @@ -311,26 +311,22 @@ fn _main(event_loop: EventLoop<()>) { run(event_loop, app); } - -#[cfg(target_os="android")] +#[cfg(target_os = "android")] #[no_mangle] fn android_main(app: AndroidApp) { - android_logger::init_once( - android_logger::Config::default().with_min_level(Level::Trace) - ); + android_logger::init_once(android_logger::Config::default().with_min_level(Level::Trace)); - let event_loop = EventLoopBuilder::new() - .with_android_app(app) - .build(); + let event_loop = EventLoopBuilder::new().with_android_app(app).build(); _main(event_loop); } -#[cfg(not(target_os="android"))] +#[cfg(not(target_os = "android"))] fn main() { - env_logger::builder().filter_level(log::LevelFilter::Warn) // Default Log Level + env_logger::builder() + .filter_level(log::LevelFilter::Warn) // Default Log Level .parse_default_env() .init(); let event_loop = EventLoopBuilder::new().build(); _main(event_loop); -} \ No newline at end of file +} diff --git a/examples/na-mainloop/src/lib.rs b/examples/na-mainloop/src/lib.rs index 7cc5e33..cb98354 100644 --- a/examples/na-mainloop/src/lib.rs +++ b/examples/na-mainloop/src/lib.rs @@ -1,70 +1,76 @@ -use android_activity::{PollEvent, MainEvent, AndroidApp}; +use android_activity::{AndroidApp, MainEvent, PollEvent}; use log::info; #[no_mangle] fn android_main(app: AndroidApp) { - android_logger::init_once( - android_logger::Config::default().with_min_level(log::Level::Info) - ); + android_logger::init_once(android_logger::Config::default().with_min_level(log::Level::Info)); let mut quit = false; let mut redraw_pending = true; let mut render_state: Option<()> = Default::default(); while !quit { - app.poll_events(Some(std::time::Duration::from_millis(500)) /* timeout */, |event| { - match event { - PollEvent::Wake => { info!("Early wake up"); }, - PollEvent::Timeout => { - info!("Timed out"); - // Real app would probably rely on vblank sync via graphics API... - redraw_pending = true; - }, - PollEvent::Main(main_event) => { - info!("Main event: {:?}", main_event); - match main_event { - MainEvent::SaveState { saver, .. } => { - saver.store("foo://bar".as_bytes()); - }, - MainEvent::Pause => {}, - MainEvent::Resume { loader, .. } => { - if let Some(state) = loader.load() { - if let Ok(uri) = String::from_utf8(state) { - info!("Resumed with saved state = {uri:#?}"); + app.poll_events( + Some(std::time::Duration::from_millis(500)), /* timeout */ + |event| { + match event { + PollEvent::Wake => { + info!("Early wake up"); + } + PollEvent::Timeout => { + info!("Timed out"); + // Real app would probably rely on vblank sync via graphics API... + redraw_pending = true; + } + PollEvent::Main(main_event) => { + info!("Main event: {:?}", main_event); + match main_event { + MainEvent::SaveState { saver, .. } => { + saver.store("foo://bar".as_bytes()); + } + MainEvent::Pause => {} + MainEvent::Resume { loader, .. } => { + if let Some(state) = loader.load() { + if let Ok(uri) = String::from_utf8(state) { + info!("Resumed with saved state = {uri:#?}"); + } } } - }, - MainEvent::InitWindow { .. } => { - render_state = Some(()); - redraw_pending = true; - }, - MainEvent::TerminateWindow { .. } => { - render_state = None; + MainEvent::InitWindow { .. } => { + render_state = Some(()); + redraw_pending = true; + } + MainEvent::TerminateWindow { .. } => { + render_state = None; + } + MainEvent::WindowResized { .. } => { + redraw_pending = true; + } + MainEvent::RedrawNeeded { .. } => { + redraw_pending = true; + } + MainEvent::LowMemory => {} + + MainEvent::Destroy => quit = true, + _ => { /* ... */ } } - MainEvent::WindowResized { .. } => { redraw_pending = true; }, - MainEvent::RedrawNeeded { ..} => { redraw_pending = true; }, - MainEvent::LowMemory => {}, - - MainEvent::Destroy => { quit = true }, - _ => { /* ... */} } - }, - _ => {} - } - - if redraw_pending { - if let Some(_rs) = render_state { - redraw_pending = false; - - // Handle input - app.input_events(|event| { - info!("Input Event: {event:?}"); - - }); - - info!("Render..."); + _ => {} } - } - }); + + if redraw_pending { + if let Some(_rs) = render_state { + redraw_pending = false; + + // Handle input + app.input_events(|event| { + info!("Input Event: {event:?}"); + }); + + info!("Render..."); + } + } + }, + ); } } diff --git a/examples/na-subclass-jni/src/lib.rs b/examples/na-subclass-jni/src/lib.rs index 6c3a82f..95fcca3 100644 --- a/examples/na-subclass-jni/src/lib.rs +++ b/examples/na-subclass-jni/src/lib.rs @@ -1,9 +1,8 @@ - -use android_activity::{PollEvent, MainEvent, AndroidApp}; +use android_activity::{AndroidApp, MainEvent, PollEvent}; use log::Level; -use log::{trace, info}; +use log::{info, trace}; +use serde::{Deserialize, Serialize}; use std::time::Duration; -use serde::{Serialize, Deserialize}; #[derive(Debug, Serialize, Deserialize)] struct AppState { @@ -12,77 +11,85 @@ struct AppState { #[no_mangle] fn android_main(app: AndroidApp) { - android_logger::init_once( - android_logger::Config::default().with_min_level(Level::Info) - ); + android_logger::init_once(android_logger::Config::default().with_min_level(Level::Info)); let mut quit = false; let mut redraw_pending = true; let mut render_state: Option<()> = Default::default(); while !quit { - app.poll_events(Some(Duration::from_millis(500)) /* timeout */, |event| { - match event { - PollEvent::Wake => { trace!("Early wake up"); }, - PollEvent::Timeout => { - trace!("Timed out"); - // Real app would probably rely on vblank sync via graphics API... - redraw_pending = true; - }, - PollEvent::Main(main_event) => { - info!("Main event: {:?}", main_event); - match main_event { - MainEvent::SaveState { saver, .. } => { - let state = serde_json::to_vec(&AppState { uri: format!("foo://bar") }).unwrap(); - saver.store(&state); - }, - MainEvent::Pause => {}, - MainEvent::Resume { loader, .. } => { - if let Some(state) = loader.load() { - let state: AppState = serde_json::from_slice(&state).unwrap(); - info!("Resumed with saved state = {state:#?}"); - } - }, - MainEvent::InitWindow { .. } => { - render_state = Some(()); - redraw_pending = true; - }, - MainEvent::TerminateWindow { .. } => { - render_state = None; - } - MainEvent::WindowResized { .. } => { redraw_pending = true; }, - MainEvent::RedrawNeeded { ..} => { redraw_pending = true; }, - MainEvent::LowMemory => {}, - - MainEvent::Destroy => { quit = true }, - _ => { /* ... */} + app.poll_events( + Some(Duration::from_millis(500)), /* timeout */ + |event| { + match event { + PollEvent::Wake => { + trace!("Early wake up"); } - }, - _ => {} - } + PollEvent::Timeout => { + trace!("Timed out"); + // Real app would probably rely on vblank sync via graphics API... + redraw_pending = true; + } + PollEvent::Main(main_event) => { + info!("Main event: {:?}", main_event); + match main_event { + MainEvent::SaveState { saver, .. } => { + let state = serde_json::to_vec(&AppState { + uri: format!("foo://bar"), + }) + .unwrap(); + saver.store(&state); + } + MainEvent::Pause => {} + MainEvent::Resume { loader, .. } => { + if let Some(state) = loader.load() { + let state: AppState = serde_json::from_slice(&state).unwrap(); + info!("Resumed with saved state = {state:#?}"); + } + } + MainEvent::InitWindow { .. } => { + render_state = Some(()); + redraw_pending = true; + } + MainEvent::TerminateWindow { .. } => { + render_state = None; + } + MainEvent::WindowResized { .. } => { + redraw_pending = true; + } + MainEvent::RedrawNeeded { .. } => { + redraw_pending = true; + } + MainEvent::LowMemory => {} - if redraw_pending { - info!("Checking input: START"); - if let Some(_rs) = render_state { - redraw_pending = false; - - // Handle input - app.input_events(|event| { - info!("Input Event: {event:?}"); - - }); - - // Render... + MainEvent::Destroy => quit = true, + _ => { /* ... */ } + } + } + _ => {} } - info!("Checking input: DONE"); - } else { - info!("No redraw pending"); - } - }); + + if redraw_pending { + info!("Checking input: START"); + if let Some(_rs) = render_state { + redraw_pending = false; + + // Handle input + app.input_events(|event| { + info!("Input Event: {event:?}"); + }); + + // Render... + } + info!("Checking input: DONE"); + } else { + info!("No redraw pending"); + } + }, + ); } } - #[allow(non_snake_case)] #[no_mangle] pub extern "C" fn Java_co_realfit_nasubclassjni_MainActivity_notifyOnNewIntent( @@ -91,4 +98,4 @@ pub extern "C" fn Java_co_realfit_nasubclassjni_MainActivity_notifyOnNewIntent( _activity: jni::objects::JObject, ) { info!("onNewIntent was called!"); -} \ No newline at end of file +} diff --git a/examples/na-winit-wgpu/src/lib.rs b/examples/na-winit-wgpu/src/lib.rs index cf65658..b762a73 100644 --- a/examples/na-winit-wgpu/src/lib.rs +++ b/examples/na-winit-wgpu/src/lib.rs @@ -1,22 +1,21 @@ - -use std::ops::Deref; use std::borrow::Cow; +use std::ops::Deref; use std::sync::{Arc, RwLock}; -use log::Level; use log::trace; +use log::Level; use wgpu::TextureFormat; -use wgpu::{Instance, Adapter, Device, ShaderModule, PipelineLayout, RenderPipeline, Queue}; +use wgpu::{Adapter, Device, Instance, PipelineLayout, Queue, RenderPipeline, ShaderModule}; use winit::{ event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget, EventLoopBuilder}, + event_loop::{ControlFlow, EventLoop, EventLoopBuilder, EventLoopWindowTarget}, }; -#[cfg(target_os="android")] +#[cfg(target_os = "android")] use android_activity::AndroidApp; -#[cfg(target_os="android")] +#[cfg(target_os = "android")] use winit::platform::android::EventLoopBuilderExtAndroid; struct RenderState { @@ -30,7 +29,7 @@ struct RenderState { struct SurfaceState { window: winit::window::Window, - surface: wgpu::Surface + surface: wgpu::Surface, } struct AppInner { @@ -41,7 +40,7 @@ struct AppInner { } struct App { - inner: Arc> + inner: Arc>, } impl App { @@ -52,7 +51,7 @@ impl App { adapter: None, surface_state: None, render_state: None, - })) + })), } } } @@ -63,7 +62,6 @@ impl Deref for App { } } - async fn init_render_state(adapter: &Adapter, target_format: TextureFormat) -> RenderState { trace!("Initializing render state"); @@ -141,7 +139,9 @@ fn configure_surface_swapchain(render_state: &RenderState, surface_state: &Surfa }; trace!("WGPU: Configuring surface swapchain: format = {swapchain_format:?}, size = {size:?}"); - surface_state.surface.configure(&render_state.device, &config); + surface_state + .surface + .configure(&render_state.device, &config); } // We want to defer the initialization of our render state until @@ -155,7 +155,8 @@ async fn ensure_render_state_for_surface(app: &App, new_surface_state: &SurfaceS if app_guard.adapter.is_none() { trace!("WGPU: requesting a suitable adapter (compatible with our surface)"); - let adapter = app_guard.instance + let adapter = app_guard + .instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::default(), force_fallback_adapter: false, @@ -171,7 +172,10 @@ async fn ensure_render_state_for_surface(app: &App, new_surface_state: &SurfaceS if app_guard.render_state.is_none() { trace!("WGPU: finding preferred swapchain format"); - let swapchain_format = new_surface_state.surface.get_preferred_format(&adapter).unwrap(); + let swapchain_format = new_surface_state + .surface + .get_preferred_format(&adapter) + .unwrap(); let rs = init_render_state(adapter, swapchain_format).await; app_guard.render_state = Some(rs); @@ -185,10 +189,7 @@ fn create_surface(app: &App, event_loop: &EventLoopWindowTarget) -> Surfac let guard = app.inner.read().unwrap(); let surface = unsafe { guard.instance.create_surface(&window) }; - SurfaceState { - window, - surface - } + SurfaceState { window, surface } } fn resume(app: &App, event_loop: &EventLoopWindowTarget) { @@ -209,9 +210,7 @@ fn resume(app: &App, event_loop: &EventLoopWindowTarget) { surface_state.window.request_redraw(); } - fn run(event_loop: EventLoop<()>, app: App) { - //let mut running = false; trace!("Running mainloop..."); @@ -225,7 +224,7 @@ fn run(event_loop: EventLoop<()>, app: App) { // Note: that because Winit doesn't currently support lifecycle events consistently // across platforms then we effectively issue a fake 'resume' on non-android // platforms... - #[cfg(not(target_os="android"))] + #[cfg(not(target_os = "android"))] resume(&app, event_loop) } Event::Resumed => { @@ -236,7 +235,7 @@ fn run(event_loop: EventLoop<()>, app: App) { let mut guard = app.write().unwrap(); //guard.running = false; guard.render_state = None; - }, + } Event::WindowEvent { event: WindowEvent::Resized(_size), .. @@ -258,27 +257,32 @@ fn run(event_loop: EventLoop<()>, app: App) { let guard = app.read().unwrap(); if let Some(ref surface_state) = guard.surface_state { if let Some(ref rs) = guard.render_state { - let frame = surface_state.surface + let frame = surface_state + .surface .get_current_texture() .expect("Failed to acquire next swap chain texture"); let view = frame .texture .create_view(&wgpu::TextureViewDescriptor::default()); let mut encoder = - rs.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + rs.device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: None, + }); { - let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), - store: true, - }, - }], - depth_stencil_attachment: None, - }); + let mut rpass = + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), + store: true, + }, + }], + depth_stencil_attachment: None, + }); rpass.set_pipeline(&rs.render_pipeline); rpass.draw(0..3, 0..1); } @@ -298,7 +302,6 @@ fn run(event_loop: EventLoop<()>, app: App) { }); } - fn _main(event_loop: EventLoop<()>) { // We can decide on our graphics API / backend up-front and that // doesn't need to be re-considered later @@ -311,26 +314,22 @@ fn _main(event_loop: EventLoop<()>) { run(event_loop, app); } - -#[cfg(target_os="android")] +#[cfg(target_os = "android")] #[no_mangle] fn android_main(app: AndroidApp) { - android_logger::init_once( - android_logger::Config::default().with_min_level(Level::Trace) - ); + android_logger::init_once(android_logger::Config::default().with_min_level(Level::Trace)); - let event_loop = EventLoopBuilder::new() - .with_android_app(app) - .build(); + let event_loop = EventLoopBuilder::new().with_android_app(app).build(); _main(event_loop); } -#[cfg(not(target_os="android"))] +#[cfg(not(target_os = "android"))] fn main() { - env_logger::builder().filter_level(log::LevelFilter::Warn) // Default Log Level + env_logger::builder() + .filter_level(log::LevelFilter::Warn) // Default Log Level .parse_default_env() .init(); let event_loop = EventLoopBuilder::new().build(); _main(event_loop); -} \ No newline at end of file +}