mirror of
https://github.com/rust-mobile/android-activity.git
synced 2026-07-04 05:47:26 +00:00
Runs cargo fmt across everything
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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...");
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,4 +217,4 @@ pub fn audio_probe() {
|
||||
println!(" Formats: {:?}", device.formats);
|
||||
println!("}}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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...");
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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...");
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user