Runs cargo fmt across everything

This commit is contained in:
Robert Bragg
2022-08-11 23:13:41 +01:00
parent 5220d35373
commit 0d0c10ebb2
15 changed files with 685 additions and 552 deletions
+3 -3
View File
@@ -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();
}
}
+2 -3
View File
@@ -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");
include!("ffi_x86_64.rs");
+8 -13
View File
@@ -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<i32> for Class {
@@ -95,7 +95,7 @@ impl From<i32> 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)
}
}
}
+138 -74
View File
@@ -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<ALooper>
looper: NonNull<ALooper>,
}
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<ffi::android_app>) -> 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<NativeWindowRef> {
let guard = self.native_window.read().unwrap();
if let Some(ref window) = *guard {
@@ -152,7 +153,8 @@ impl AndroidAppInner {
}
pub fn poll_events<F>(&self, timeout: Option<Duration>, 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<std::path::PathBuf> {
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<ffi::android_input_buffer>,
_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<ffi::android_input_buffer>) -> 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();
}
}
+34 -19
View File
@@ -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<AndroidAppInner>
pub(crate) inner: Arc<AndroidAppInner>,
}
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<F>(&self, timeout: Option<Duration>, 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<std::path::PathBuf> {
self.inner.obb_path()
}
}
}
+2 -3
View File
@@ -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");
include!("ffi_x86_64.rs");
+77 -38
View File
@@ -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<ALooper>
looper: NonNull<ALooper>,
}
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<ffi::android_app>) -> 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<F>(&self, timeout: Option<Duration>, 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<std::path::PathBuf> {
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();
}
}
+75 -73
View File
@@ -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<std::sync::Mutex<winit::event_loop::EventLoopProxy<Event>>>);
fn create_window<T>(event_loop: &EventLoopWindowTarget<T>, state: &mut State, painter: &mut Painter) -> winit::window::Window {
fn create_window<T>(
event_loop: &EventLoopWindowTarget<T>,
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<T>(event_loop: &EventLoopWindowTarget<T>, 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<T>(event_loop: &EventLoopWindowTarget<T>, state: &mut State, pa
fn _main(event_loop: EventLoop<Event>) {
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<Event>) {
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<winit::window::Window> = 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);
}
}
+60 -54
View File
@@ -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...");
}
}
},
);
}
}
}
+1 -1
View File
@@ -217,4 +217,4 @@ pub fn audio_probe() {
println!(" Formats: {:?}", device.formats);
println!("}}");
}
}
}
+66 -60
View File
@@ -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...");
}
}
},
);
}
}
}
+42 -46
View File
@@ -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<RwLock<AppInner>>
inner: Arc<RwLock<AppInner>>,
}
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<T>(app: &App, event_loop: &EventLoopWindowTarget<T>) -> Surfac
let guard = app.inner.read().unwrap();
let surface = unsafe { guard.instance.create_surface(&window) };
SurfaceState {
window,
surface
}
SurfaceState { window, surface }
}
fn resume<T>(app: &App, event_loop: &EventLoopWindowTarget<T>) {
@@ -209,9 +207,7 @@ fn resume<T>(app: &App, event_loop: &EventLoopWindowTarget<T>) {
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);
}
}
+59 -53
View File
@@ -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...");
}
}
},
);
}
}
+71 -64
View File
@@ -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!");
}
}
+47 -48
View File
@@ -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<RwLock<AppInner>>
inner: Arc<RwLock<AppInner>>,
}
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<T>(app: &App, event_loop: &EventLoopWindowTarget<T>) -> Surfac
let guard = app.inner.read().unwrap();
let surface = unsafe { guard.instance.create_surface(&window) };
SurfaceState {
window,
surface
}
SurfaceState { window, surface }
}
fn resume<T>(app: &App, event_loop: &EventLoopWindowTarget<T>) {
@@ -209,9 +210,7 @@ fn resume<T>(app: &App, event_loop: &EventLoopWindowTarget<T>) {
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);
}
}