From 0062cfc7a08720155d5d5f6a368e38ec99886533 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Tue, 10 Mar 2026 20:40:06 +0000 Subject: [PATCH] import android-games-sdk patches for mainLooper + onCreate hook This imports the SDK from commit 30b8bfcc9a12942d1268820e8a83d7643e99ee92, from: https://github.com/rust-mobile/android-games-sdk/commits/android-activity-4.0.0 this includes these patches: # PATCH: Add mainLooper to android_app Track the Looper for the Java main/UI thread in the android_app. This makes it possible to add file descriptors and callbacks to the Java UI Looper from the android_main thread. This needs to be initialized by the android_native_app_glue before spawning the android_main thread because the looper needs to be discovered via `ALooper_forThread` while still running on the Java main thread (in the onCreate callback). # PATCH: Enable Rust glue to hook into onCreate This declares an extern `_rust_glue_on_create_hook` that is called from `GameActivity` `onCreate` native method callback, before the `android_main` thread is spawned. This gives Rust code an opportunity to run code and initialize state while still running on the Java main/UI thread. For example, this could be used to initialize JNI bindings while we can assume that the current thread has an associated ClassLoader that will be able to find application classes. It may also be a convenient place to make some initial JNI calls into Android SDK APIs that can only be used from the Java main thread. # Updated import-games-sdk.sh to remove symlinks While updating the SDK the import script has been updated to remove any symlinks which make it difficult to build android-activity from Git on Windows. Note: the symlinks were redundant based on how the include paths were already configured in `build.rs` --- .../modules/game-activity/include/common | 1 - .../native_app_glue/android_native_app_glue.h | 13 ++++ .../include/game-text-input/gametextinput.h | 1 - .../game-activity/src/common/system_utils.cpp | 1 - .../native_app_glue/android_native_app_glue.c | 15 +++++ .../src/game-text-input/gametextinput.cpp | 1 - .../src/game-text-input/gametextinput.h | 1 - .../modules/game-text-input/include/common | 1 - .../android-games-sdk/import-games-sdk.sh | 12 +++- .../src/game_activity/ffi_aarch64.rs | 62 +++++++++++-------- android-activity/src/game_activity/ffi_arm.rs | 22 ++++--- .../src/game_activity/ffi_i686.rs | 62 +++++++++++-------- .../src/game_activity/ffi_x86_64.rs | 62 +++++++++++-------- android-activity/src/game_activity/mod.rs | 9 ++- 14 files changed, 167 insertions(+), 96 deletions(-) delete mode 120000 android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/common delete mode 120000 android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.h delete mode 120000 android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/common/system_utils.cpp delete mode 120000 android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-text-input/gametextinput.cpp delete mode 120000 android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-text-input/gametextinput.h delete mode 120000 android-activity/android-games-sdk/game-text-input/prefab-src/modules/game-text-input/include/common diff --git a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/common b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/common deleted file mode 120000 index 9388f34..0000000 --- a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/common +++ /dev/null @@ -1 +0,0 @@ -../../../../../include/common/ \ No newline at end of file diff --git a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h index b0769c5..3cabe73 100644 --- a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h +++ b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h @@ -201,6 +201,9 @@ struct android_app { /** The ALooper associated with the app's thread. */ ALooper* looper; + /** The ALooper associated with the app's Java main/UI thread. */ + ALooper* mainLooper; + /** When non-NULL, this is the window surface that the app can draw in. */ ANativeWindow* window; @@ -511,6 +514,16 @@ void android_app_clear_motion_events(struct android_input_buffer* inputBuffer); */ void android_app_clear_key_events(struct android_input_buffer* inputBuffer); +/** + * A hook that is called within Activity.onCreate, before the android_main + * thread has been spawned. + * + * This gives the Rust glue code a chance to perform any necessary + * initialization that needs to run from the Java main/UI thread, before the + * android_main thread is started. + */ +extern void _rust_glue_on_create_hook(struct android_app* app); + /** * This is a springboard into the Rust glue layer that wraps calling the * main entry for the app itself. diff --git a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.h b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.h deleted file mode 120000 index 84e9337..0000000 --- a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-text-input/gametextinput.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h \ No newline at end of file diff --git a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/common/system_utils.cpp b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/common/system_utils.cpp deleted file mode 120000 index 713dcbb..0000000 --- a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/common/system_utils.cpp +++ /dev/null @@ -1 +0,0 @@ -../../../../../../src/common/system_utils.cpp \ No newline at end of file diff --git a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c index 4422ec6..7fd9d39 100644 --- a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c +++ b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c @@ -224,6 +224,8 @@ static void* android_app_entry(void* param) { android_app->cmdPollSource.app = android_app; android_app->cmdPollSource.process = process_cmd; + _rust_glue_on_create_hook(android_app); + ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL, &android_app->cmdPollSource); @@ -290,6 +292,13 @@ static struct android_app* android_app_create(GameActivity* activity, memcpy(android_app->savedState, savedState, savedStateSize); } + android_app->mainLooper = ALooper_forThread(); + if (android_app->mainLooper == NULL) { + LOGE("Failed to get main looper"); + return NULL; + } + ALooper_acquire(android_app->mainLooper); + int msgpipe[2]; if (pipe(msgpipe)) { LOGE("could not create pipe: %s", strerror(errno)); @@ -396,8 +405,14 @@ static void android_app_free(struct android_app* android_app) { close(android_app->msgread); close(android_app->msgwrite); + pthread_cond_destroy(&android_app->cond); pthread_mutex_destroy(&android_app->mutex); + + if (android_app->mainLooper != NULL) { + ALooper_release(android_app->mainLooper); + } + free(android_app); } diff --git a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-text-input/gametextinput.cpp b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-text-input/gametextinput.cpp deleted file mode 120000 index 3deaa67..0000000 --- a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-text-input/gametextinput.cpp +++ /dev/null @@ -1 +0,0 @@ -../../../../../../game-text-input/prefab-src/modules/game-text-input/src/game-text-input/gametextinput.cpp \ No newline at end of file diff --git a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-text-input/gametextinput.h b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-text-input/gametextinput.h deleted file mode 120000 index 84e9337..0000000 --- a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-text-input/gametextinput.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h \ No newline at end of file diff --git a/android-activity/android-games-sdk/game-text-input/prefab-src/modules/game-text-input/include/common b/android-activity/android-games-sdk/game-text-input/prefab-src/modules/game-text-input/include/common deleted file mode 120000 index 9388f34..0000000 --- a/android-activity/android-games-sdk/game-text-input/prefab-src/modules/game-text-input/include/common +++ /dev/null @@ -1 +0,0 @@ -../../../../../include/common/ \ No newline at end of file diff --git a/android-activity/android-games-sdk/import-games-sdk.sh b/android-activity/android-games-sdk/import-games-sdk.sh index 83bdf9f..21351d3 100755 --- a/android-activity/android-games-sdk/import-games-sdk.sh +++ b/android-activity/android-games-sdk/import-games-sdk.sh @@ -42,4 +42,14 @@ cp -av "$SOURCE_DIR/game-activity/prefab-src" "$DEST_DIR/game-activity" cp -av "$SOURCE_DIR/game-text-input/prefab-src" "$DEST_DIR/game-text-input" cp -av "$SOURCE_DIR/include/common/gamesdk_common.h" "$DEST_DIR/include/common" cp -av "$SOURCE_DIR/src/common/system_utils.h" "$DEST_DIR/src/common" -cp -av "$SOURCE_DIR/src/common/system_utils.cpp" "$DEST_DIR/src/common" \ No newline at end of file +cp -av "$SOURCE_DIR/src/common/system_utils.cpp" "$DEST_DIR/src/common" + +# Remove symlinks so the android-activity crate is easily buildable +# from Git on Windows. + +rm "$DEST_DIR/game-activity/prefab-src/modules/game-activity/include/common" +rm -fr "$DEST_DIR/game-activity/prefab-src/modules/game-activity/include/game-text-input" +rm -fr "$DEST_DIR/game-activity/prefab-src/modules/game-activity/src/common" +rm -fr "$DEST_DIR/game-activity/prefab-src/modules/game-activity/src/game-text-input" + +rm "$DEST_DIR/game-text-input/prefab-src/modules/game-text-input/include/common" diff --git a/android-activity/src/game_activity/ffi_aarch64.rs b/android-activity/src/game_activity/ffi_aarch64.rs index 17bf8ca..a755087 100644 --- a/android-activity/src/game_activity/ffi_aarch64.rs +++ b/android-activity/src/game_activity/ffi_aarch64.rs @@ -4675,6 +4675,8 @@ pub struct android_app { pub savedStateSize: usize, #[doc = " The ALooper associated with the app's thread."] pub looper: *mut ALooper, + #[doc = " The ALooper associated with the app's Java main/UI thread."] + pub mainLooper: *mut ALooper, #[doc = " When non-NULL, this is the window surface that the app can draw in."] pub window: *mut ANativeWindow, #[doc = " Current content rectangle of the window; this is the area where the\n window's content should be placed to be seen by the user."] @@ -4714,7 +4716,7 @@ pub struct android_app { } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { - ["Size of android_app"][::std::mem::size_of::() - 400usize]; + ["Size of android_app"][::std::mem::size_of::() - 408usize]; ["Alignment of android_app"][::std::mem::align_of::() - 8usize]; ["Offset of field: android_app::userData"] [::std::mem::offset_of!(android_app, userData) - 0usize]; @@ -4728,55 +4730,57 @@ const _: () = { ["Offset of field: android_app::savedStateSize"] [::std::mem::offset_of!(android_app, savedStateSize) - 40usize]; ["Offset of field: android_app::looper"][::std::mem::offset_of!(android_app, looper) - 48usize]; - ["Offset of field: android_app::window"][::std::mem::offset_of!(android_app, window) - 56usize]; + ["Offset of field: android_app::mainLooper"] + [::std::mem::offset_of!(android_app, mainLooper) - 56usize]; + ["Offset of field: android_app::window"][::std::mem::offset_of!(android_app, window) - 64usize]; ["Offset of field: android_app::contentRect"] - [::std::mem::offset_of!(android_app, contentRect) - 64usize]; + [::std::mem::offset_of!(android_app, contentRect) - 72usize]; ["Offset of field: android_app::softwareKeyboardVisible"] - [::std::mem::offset_of!(android_app, softwareKeyboardVisible) - 80usize]; + [::std::mem::offset_of!(android_app, softwareKeyboardVisible) - 88usize]; ["Offset of field: android_app::editorAction"] - [::std::mem::offset_of!(android_app, editorAction) - 84usize]; + [::std::mem::offset_of!(android_app, editorAction) - 92usize]; ["Offset of field: android_app::pendingEditorAction"] - [::std::mem::offset_of!(android_app, pendingEditorAction) - 88usize]; + [::std::mem::offset_of!(android_app, pendingEditorAction) - 96usize]; ["Offset of field: android_app::activityState"] - [::std::mem::offset_of!(android_app, activityState) - 92usize]; + [::std::mem::offset_of!(android_app, activityState) - 100usize]; ["Offset of field: android_app::destroyRequested"] - [::std::mem::offset_of!(android_app, destroyRequested) - 96usize]; + [::std::mem::offset_of!(android_app, destroyRequested) - 104usize]; ["Offset of field: android_app::inputBuffers"] - [::std::mem::offset_of!(android_app, inputBuffers) - 104usize]; + [::std::mem::offset_of!(android_app, inputBuffers) - 112usize]; ["Offset of field: android_app::currentInputBuffer"] - [::std::mem::offset_of!(android_app, currentInputBuffer) - 200usize]; + [::std::mem::offset_of!(android_app, currentInputBuffer) - 208usize]; ["Offset of field: android_app::textInputState"] - [::std::mem::offset_of!(android_app, textInputState) - 204usize]; - ["Offset of field: android_app::mutex"][::std::mem::offset_of!(android_app, mutex) - 208usize]; - ["Offset of field: android_app::cond"][::std::mem::offset_of!(android_app, cond) - 248usize]; + [::std::mem::offset_of!(android_app, textInputState) - 212usize]; + ["Offset of field: android_app::mutex"][::std::mem::offset_of!(android_app, mutex) - 216usize]; + ["Offset of field: android_app::cond"][::std::mem::offset_of!(android_app, cond) - 256usize]; ["Offset of field: android_app::msgread"] - [::std::mem::offset_of!(android_app, msgread) - 296usize]; + [::std::mem::offset_of!(android_app, msgread) - 304usize]; ["Offset of field: android_app::msgwrite"] - [::std::mem::offset_of!(android_app, msgwrite) - 300usize]; + [::std::mem::offset_of!(android_app, msgwrite) - 308usize]; ["Offset of field: android_app::thread"] - [::std::mem::offset_of!(android_app, thread) - 304usize]; + [::std::mem::offset_of!(android_app, thread) - 312usize]; ["Offset of field: android_app::cmdPollSource"] - [::std::mem::offset_of!(android_app, cmdPollSource) - 312usize]; + [::std::mem::offset_of!(android_app, cmdPollSource) - 320usize]; ["Offset of field: android_app::running"] - [::std::mem::offset_of!(android_app, running) - 336usize]; + [::std::mem::offset_of!(android_app, running) - 344usize]; ["Offset of field: android_app::stateSaved"] - [::std::mem::offset_of!(android_app, stateSaved) - 340usize]; + [::std::mem::offset_of!(android_app, stateSaved) - 348usize]; ["Offset of field: android_app::destroyed"] - [::std::mem::offset_of!(android_app, destroyed) - 344usize]; + [::std::mem::offset_of!(android_app, destroyed) - 352usize]; ["Offset of field: android_app::redrawNeeded"] - [::std::mem::offset_of!(android_app, redrawNeeded) - 348usize]; + [::std::mem::offset_of!(android_app, redrawNeeded) - 356usize]; ["Offset of field: android_app::pendingWindow"] - [::std::mem::offset_of!(android_app, pendingWindow) - 352usize]; + [::std::mem::offset_of!(android_app, pendingWindow) - 360usize]; ["Offset of field: android_app::pendingContentRect"] - [::std::mem::offset_of!(android_app, pendingContentRect) - 360usize]; + [::std::mem::offset_of!(android_app, pendingContentRect) - 368usize]; ["Offset of field: android_app::keyEventFilter"] - [::std::mem::offset_of!(android_app, keyEventFilter) - 376usize]; + [::std::mem::offset_of!(android_app, keyEventFilter) - 384usize]; ["Offset of field: android_app::motionEventFilter"] - [::std::mem::offset_of!(android_app, motionEventFilter) - 384usize]; + [::std::mem::offset_of!(android_app, motionEventFilter) - 392usize]; ["Offset of field: android_app::inputAvailableWakeUp"] - [::std::mem::offset_of!(android_app, inputAvailableWakeUp) - 392usize]; + [::std::mem::offset_of!(android_app, inputAvailableWakeUp) - 400usize]; ["Offset of field: android_app::inputSwapPending"] - [::std::mem::offset_of!(android_app, inputSwapPending) - 393usize]; + [::std::mem::offset_of!(android_app, inputSwapPending) - 401usize]; }; #[doc = " Looper data ID of commands coming from the app's main thread, which\n is returned as an identifier from ALooper_pollOnce(). The data for this\n identifier is a pointer to an android_poll_source structure.\n These can be retrieved and processed with android_app_read_cmd()\n and android_app_exec_cmd()."] pub const NativeAppGlueLooperId_LOOPER_ID_MAIN: NativeAppGlueLooperId = 1; @@ -4850,6 +4854,10 @@ unsafe extern "C" { #[doc = " Clear the array of key events that were waiting to be handled, and release\n each of them.\n\n This method should be called after you have processed the key up events in\n your game loop. You should handle events at each iteration of your game loop."] pub fn android_app_clear_key_events(inputBuffer: *mut android_input_buffer); } +unsafe extern "C" { + #[doc = " A hook that is called within Activity.onCreate, before the android_main\n thread has been spawned.\n\n This gives the Rust glue code a chance to perform any necessary\n initialization that needs to run from the Java main/UI thread, before the\n android_main thread is started."] + pub fn _rust_glue_on_create_hook(app: *mut android_app); +} unsafe extern "C" { #[doc = " This is a springboard into the Rust glue layer that wraps calling the\n main entry for the app itself."] pub fn _rust_glue_entry(app: *mut android_app); diff --git a/android-activity/src/game_activity/ffi_arm.rs b/android-activity/src/game_activity/ffi_arm.rs index 5a6cccc..f3b28a9 100644 --- a/android-activity/src/game_activity/ffi_arm.rs +++ b/android-activity/src/game_activity/ffi_arm.rs @@ -5087,6 +5087,8 @@ pub struct android_app { pub savedStateSize: usize, #[doc = " The ALooper associated with the app's thread."] pub looper: *mut ALooper, + #[doc = " The ALooper associated with the app's Java main/UI thread."] + pub mainLooper: *mut ALooper, #[doc = " When non-NULL, this is the window surface that the app can draw in."] pub window: *mut ANativeWindow, #[doc = " Current content rectangle of the window; this is the area where the\n window's content should be placed to be seen by the user."] @@ -5140,19 +5142,21 @@ const _: () = { ["Offset of field: android_app::savedStateSize"] [::std::mem::offset_of!(android_app, savedStateSize) - 20usize]; ["Offset of field: android_app::looper"][::std::mem::offset_of!(android_app, looper) - 24usize]; - ["Offset of field: android_app::window"][::std::mem::offset_of!(android_app, window) - 28usize]; + ["Offset of field: android_app::mainLooper"] + [::std::mem::offset_of!(android_app, mainLooper) - 28usize]; + ["Offset of field: android_app::window"][::std::mem::offset_of!(android_app, window) - 32usize]; ["Offset of field: android_app::contentRect"] - [::std::mem::offset_of!(android_app, contentRect) - 32usize]; + [::std::mem::offset_of!(android_app, contentRect) - 36usize]; ["Offset of field: android_app::softwareKeyboardVisible"] - [::std::mem::offset_of!(android_app, softwareKeyboardVisible) - 48usize]; + [::std::mem::offset_of!(android_app, softwareKeyboardVisible) - 52usize]; ["Offset of field: android_app::editorAction"] - [::std::mem::offset_of!(android_app, editorAction) - 52usize]; + [::std::mem::offset_of!(android_app, editorAction) - 56usize]; ["Offset of field: android_app::pendingEditorAction"] - [::std::mem::offset_of!(android_app, pendingEditorAction) - 56usize]; + [::std::mem::offset_of!(android_app, pendingEditorAction) - 60usize]; ["Offset of field: android_app::activityState"] - [::std::mem::offset_of!(android_app, activityState) - 60usize]; + [::std::mem::offset_of!(android_app, activityState) - 64usize]; ["Offset of field: android_app::destroyRequested"] - [::std::mem::offset_of!(android_app, destroyRequested) - 64usize]; + [::std::mem::offset_of!(android_app, destroyRequested) - 68usize]; ["Offset of field: android_app::inputBuffers"] [::std::mem::offset_of!(android_app, inputBuffers) - 72usize]; ["Offset of field: android_app::currentInputBuffer"] @@ -5262,6 +5266,10 @@ unsafe extern "C" { #[doc = " Clear the array of key events that were waiting to be handled, and release\n each of them.\n\n This method should be called after you have processed the key up events in\n your game loop. You should handle events at each iteration of your game loop."] pub fn android_app_clear_key_events(inputBuffer: *mut android_input_buffer); } +unsafe extern "C" { + #[doc = " A hook that is called within Activity.onCreate, before the android_main\n thread has been spawned.\n\n This gives the Rust glue code a chance to perform any necessary\n initialization that needs to run from the Java main/UI thread, before the\n android_main thread is started."] + pub fn _rust_glue_on_create_hook(app: *mut android_app); +} unsafe extern "C" { #[doc = " This is a springboard into the Rust glue layer that wraps calling the\n main entry for the app itself."] pub fn _rust_glue_entry(app: *mut android_app); diff --git a/android-activity/src/game_activity/ffi_i686.rs b/android-activity/src/game_activity/ffi_i686.rs index b71a13e..7ead1e9 100644 --- a/android-activity/src/game_activity/ffi_i686.rs +++ b/android-activity/src/game_activity/ffi_i686.rs @@ -5127,6 +5127,8 @@ pub struct android_app { pub savedStateSize: usize, #[doc = " The ALooper associated with the app's thread."] pub looper: *mut ALooper, + #[doc = " The ALooper associated with the app's Java main/UI thread."] + pub mainLooper: *mut ALooper, #[doc = " When non-NULL, this is the window surface that the app can draw in."] pub window: *mut ANativeWindow, #[doc = " Current content rectangle of the window; this is the area where the\n window's content should be placed to be seen by the user."] @@ -5166,7 +5168,7 @@ pub struct android_app { } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { - ["Size of android_app"][::std::mem::size_of::() - 236usize]; + ["Size of android_app"][::std::mem::size_of::() - 240usize]; ["Alignment of android_app"][::std::mem::align_of::() - 4usize]; ["Offset of field: android_app::userData"] [::std::mem::offset_of!(android_app, userData) - 0usize]; @@ -5180,55 +5182,57 @@ const _: () = { ["Offset of field: android_app::savedStateSize"] [::std::mem::offset_of!(android_app, savedStateSize) - 20usize]; ["Offset of field: android_app::looper"][::std::mem::offset_of!(android_app, looper) - 24usize]; - ["Offset of field: android_app::window"][::std::mem::offset_of!(android_app, window) - 28usize]; + ["Offset of field: android_app::mainLooper"] + [::std::mem::offset_of!(android_app, mainLooper) - 28usize]; + ["Offset of field: android_app::window"][::std::mem::offset_of!(android_app, window) - 32usize]; ["Offset of field: android_app::contentRect"] - [::std::mem::offset_of!(android_app, contentRect) - 32usize]; + [::std::mem::offset_of!(android_app, contentRect) - 36usize]; ["Offset of field: android_app::softwareKeyboardVisible"] - [::std::mem::offset_of!(android_app, softwareKeyboardVisible) - 48usize]; + [::std::mem::offset_of!(android_app, softwareKeyboardVisible) - 52usize]; ["Offset of field: android_app::editorAction"] - [::std::mem::offset_of!(android_app, editorAction) - 52usize]; + [::std::mem::offset_of!(android_app, editorAction) - 56usize]; ["Offset of field: android_app::pendingEditorAction"] - [::std::mem::offset_of!(android_app, pendingEditorAction) - 56usize]; + [::std::mem::offset_of!(android_app, pendingEditorAction) - 60usize]; ["Offset of field: android_app::activityState"] - [::std::mem::offset_of!(android_app, activityState) - 60usize]; + [::std::mem::offset_of!(android_app, activityState) - 64usize]; ["Offset of field: android_app::destroyRequested"] - [::std::mem::offset_of!(android_app, destroyRequested) - 64usize]; + [::std::mem::offset_of!(android_app, destroyRequested) - 68usize]; ["Offset of field: android_app::inputBuffers"] - [::std::mem::offset_of!(android_app, inputBuffers) - 68usize]; + [::std::mem::offset_of!(android_app, inputBuffers) - 72usize]; ["Offset of field: android_app::currentInputBuffer"] - [::std::mem::offset_of!(android_app, currentInputBuffer) - 148usize]; + [::std::mem::offset_of!(android_app, currentInputBuffer) - 152usize]; ["Offset of field: android_app::textInputState"] - [::std::mem::offset_of!(android_app, textInputState) - 152usize]; - ["Offset of field: android_app::mutex"][::std::mem::offset_of!(android_app, mutex) - 156usize]; - ["Offset of field: android_app::cond"][::std::mem::offset_of!(android_app, cond) - 160usize]; + [::std::mem::offset_of!(android_app, textInputState) - 156usize]; + ["Offset of field: android_app::mutex"][::std::mem::offset_of!(android_app, mutex) - 160usize]; + ["Offset of field: android_app::cond"][::std::mem::offset_of!(android_app, cond) - 164usize]; ["Offset of field: android_app::msgread"] - [::std::mem::offset_of!(android_app, msgread) - 164usize]; + [::std::mem::offset_of!(android_app, msgread) - 168usize]; ["Offset of field: android_app::msgwrite"] - [::std::mem::offset_of!(android_app, msgwrite) - 168usize]; + [::std::mem::offset_of!(android_app, msgwrite) - 172usize]; ["Offset of field: android_app::thread"] - [::std::mem::offset_of!(android_app, thread) - 172usize]; + [::std::mem::offset_of!(android_app, thread) - 176usize]; ["Offset of field: android_app::cmdPollSource"] - [::std::mem::offset_of!(android_app, cmdPollSource) - 176usize]; + [::std::mem::offset_of!(android_app, cmdPollSource) - 180usize]; ["Offset of field: android_app::running"] - [::std::mem::offset_of!(android_app, running) - 188usize]; + [::std::mem::offset_of!(android_app, running) - 192usize]; ["Offset of field: android_app::stateSaved"] - [::std::mem::offset_of!(android_app, stateSaved) - 192usize]; + [::std::mem::offset_of!(android_app, stateSaved) - 196usize]; ["Offset of field: android_app::destroyed"] - [::std::mem::offset_of!(android_app, destroyed) - 196usize]; + [::std::mem::offset_of!(android_app, destroyed) - 200usize]; ["Offset of field: android_app::redrawNeeded"] - [::std::mem::offset_of!(android_app, redrawNeeded) - 200usize]; + [::std::mem::offset_of!(android_app, redrawNeeded) - 204usize]; ["Offset of field: android_app::pendingWindow"] - [::std::mem::offset_of!(android_app, pendingWindow) - 204usize]; + [::std::mem::offset_of!(android_app, pendingWindow) - 208usize]; ["Offset of field: android_app::pendingContentRect"] - [::std::mem::offset_of!(android_app, pendingContentRect) - 208usize]; + [::std::mem::offset_of!(android_app, pendingContentRect) - 212usize]; ["Offset of field: android_app::keyEventFilter"] - [::std::mem::offset_of!(android_app, keyEventFilter) - 224usize]; + [::std::mem::offset_of!(android_app, keyEventFilter) - 228usize]; ["Offset of field: android_app::motionEventFilter"] - [::std::mem::offset_of!(android_app, motionEventFilter) - 228usize]; + [::std::mem::offset_of!(android_app, motionEventFilter) - 232usize]; ["Offset of field: android_app::inputAvailableWakeUp"] - [::std::mem::offset_of!(android_app, inputAvailableWakeUp) - 232usize]; + [::std::mem::offset_of!(android_app, inputAvailableWakeUp) - 236usize]; ["Offset of field: android_app::inputSwapPending"] - [::std::mem::offset_of!(android_app, inputSwapPending) - 233usize]; + [::std::mem::offset_of!(android_app, inputSwapPending) - 237usize]; }; #[doc = " Looper data ID of commands coming from the app's main thread, which\n is returned as an identifier from ALooper_pollOnce(). The data for this\n identifier is a pointer to an android_poll_source structure.\n These can be retrieved and processed with android_app_read_cmd()\n and android_app_exec_cmd()."] pub const NativeAppGlueLooperId_LOOPER_ID_MAIN: NativeAppGlueLooperId = 1; @@ -5302,6 +5306,10 @@ unsafe extern "C" { #[doc = " Clear the array of key events that were waiting to be handled, and release\n each of them.\n\n This method should be called after you have processed the key up events in\n your game loop. You should handle events at each iteration of your game loop."] pub fn android_app_clear_key_events(inputBuffer: *mut android_input_buffer); } +unsafe extern "C" { + #[doc = " A hook that is called within Activity.onCreate, before the android_main\n thread has been spawned.\n\n This gives the Rust glue code a chance to perform any necessary\n initialization that needs to run from the Java main/UI thread, before the\n android_main thread is started."] + pub fn _rust_glue_on_create_hook(app: *mut android_app); +} unsafe extern "C" { #[doc = " This is a springboard into the Rust glue layer that wraps calling the\n main entry for the app itself."] pub fn _rust_glue_entry(app: *mut android_app); diff --git a/android-activity/src/game_activity/ffi_x86_64.rs b/android-activity/src/game_activity/ffi_x86_64.rs index 5504712..7224c26 100644 --- a/android-activity/src/game_activity/ffi_x86_64.rs +++ b/android-activity/src/game_activity/ffi_x86_64.rs @@ -5156,6 +5156,8 @@ pub struct android_app { pub savedStateSize: usize, #[doc = " The ALooper associated with the app's thread."] pub looper: *mut ALooper, + #[doc = " The ALooper associated with the app's Java main/UI thread."] + pub mainLooper: *mut ALooper, #[doc = " When non-NULL, this is the window surface that the app can draw in."] pub window: *mut ANativeWindow, #[doc = " Current content rectangle of the window; this is the area where the\n window's content should be placed to be seen by the user."] @@ -5195,7 +5197,7 @@ pub struct android_app { } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { - ["Size of android_app"][::std::mem::size_of::() - 400usize]; + ["Size of android_app"][::std::mem::size_of::() - 408usize]; ["Alignment of android_app"][::std::mem::align_of::() - 8usize]; ["Offset of field: android_app::userData"] [::std::mem::offset_of!(android_app, userData) - 0usize]; @@ -5209,55 +5211,57 @@ const _: () = { ["Offset of field: android_app::savedStateSize"] [::std::mem::offset_of!(android_app, savedStateSize) - 40usize]; ["Offset of field: android_app::looper"][::std::mem::offset_of!(android_app, looper) - 48usize]; - ["Offset of field: android_app::window"][::std::mem::offset_of!(android_app, window) - 56usize]; + ["Offset of field: android_app::mainLooper"] + [::std::mem::offset_of!(android_app, mainLooper) - 56usize]; + ["Offset of field: android_app::window"][::std::mem::offset_of!(android_app, window) - 64usize]; ["Offset of field: android_app::contentRect"] - [::std::mem::offset_of!(android_app, contentRect) - 64usize]; + [::std::mem::offset_of!(android_app, contentRect) - 72usize]; ["Offset of field: android_app::softwareKeyboardVisible"] - [::std::mem::offset_of!(android_app, softwareKeyboardVisible) - 80usize]; + [::std::mem::offset_of!(android_app, softwareKeyboardVisible) - 88usize]; ["Offset of field: android_app::editorAction"] - [::std::mem::offset_of!(android_app, editorAction) - 84usize]; + [::std::mem::offset_of!(android_app, editorAction) - 92usize]; ["Offset of field: android_app::pendingEditorAction"] - [::std::mem::offset_of!(android_app, pendingEditorAction) - 88usize]; + [::std::mem::offset_of!(android_app, pendingEditorAction) - 96usize]; ["Offset of field: android_app::activityState"] - [::std::mem::offset_of!(android_app, activityState) - 92usize]; + [::std::mem::offset_of!(android_app, activityState) - 100usize]; ["Offset of field: android_app::destroyRequested"] - [::std::mem::offset_of!(android_app, destroyRequested) - 96usize]; + [::std::mem::offset_of!(android_app, destroyRequested) - 104usize]; ["Offset of field: android_app::inputBuffers"] - [::std::mem::offset_of!(android_app, inputBuffers) - 104usize]; + [::std::mem::offset_of!(android_app, inputBuffers) - 112usize]; ["Offset of field: android_app::currentInputBuffer"] - [::std::mem::offset_of!(android_app, currentInputBuffer) - 200usize]; + [::std::mem::offset_of!(android_app, currentInputBuffer) - 208usize]; ["Offset of field: android_app::textInputState"] - [::std::mem::offset_of!(android_app, textInputState) - 204usize]; - ["Offset of field: android_app::mutex"][::std::mem::offset_of!(android_app, mutex) - 208usize]; - ["Offset of field: android_app::cond"][::std::mem::offset_of!(android_app, cond) - 248usize]; + [::std::mem::offset_of!(android_app, textInputState) - 212usize]; + ["Offset of field: android_app::mutex"][::std::mem::offset_of!(android_app, mutex) - 216usize]; + ["Offset of field: android_app::cond"][::std::mem::offset_of!(android_app, cond) - 256usize]; ["Offset of field: android_app::msgread"] - [::std::mem::offset_of!(android_app, msgread) - 296usize]; + [::std::mem::offset_of!(android_app, msgread) - 304usize]; ["Offset of field: android_app::msgwrite"] - [::std::mem::offset_of!(android_app, msgwrite) - 300usize]; + [::std::mem::offset_of!(android_app, msgwrite) - 308usize]; ["Offset of field: android_app::thread"] - [::std::mem::offset_of!(android_app, thread) - 304usize]; + [::std::mem::offset_of!(android_app, thread) - 312usize]; ["Offset of field: android_app::cmdPollSource"] - [::std::mem::offset_of!(android_app, cmdPollSource) - 312usize]; + [::std::mem::offset_of!(android_app, cmdPollSource) - 320usize]; ["Offset of field: android_app::running"] - [::std::mem::offset_of!(android_app, running) - 336usize]; + [::std::mem::offset_of!(android_app, running) - 344usize]; ["Offset of field: android_app::stateSaved"] - [::std::mem::offset_of!(android_app, stateSaved) - 340usize]; + [::std::mem::offset_of!(android_app, stateSaved) - 348usize]; ["Offset of field: android_app::destroyed"] - [::std::mem::offset_of!(android_app, destroyed) - 344usize]; + [::std::mem::offset_of!(android_app, destroyed) - 352usize]; ["Offset of field: android_app::redrawNeeded"] - [::std::mem::offset_of!(android_app, redrawNeeded) - 348usize]; + [::std::mem::offset_of!(android_app, redrawNeeded) - 356usize]; ["Offset of field: android_app::pendingWindow"] - [::std::mem::offset_of!(android_app, pendingWindow) - 352usize]; + [::std::mem::offset_of!(android_app, pendingWindow) - 360usize]; ["Offset of field: android_app::pendingContentRect"] - [::std::mem::offset_of!(android_app, pendingContentRect) - 360usize]; + [::std::mem::offset_of!(android_app, pendingContentRect) - 368usize]; ["Offset of field: android_app::keyEventFilter"] - [::std::mem::offset_of!(android_app, keyEventFilter) - 376usize]; + [::std::mem::offset_of!(android_app, keyEventFilter) - 384usize]; ["Offset of field: android_app::motionEventFilter"] - [::std::mem::offset_of!(android_app, motionEventFilter) - 384usize]; + [::std::mem::offset_of!(android_app, motionEventFilter) - 392usize]; ["Offset of field: android_app::inputAvailableWakeUp"] - [::std::mem::offset_of!(android_app, inputAvailableWakeUp) - 392usize]; + [::std::mem::offset_of!(android_app, inputAvailableWakeUp) - 400usize]; ["Offset of field: android_app::inputSwapPending"] - [::std::mem::offset_of!(android_app, inputSwapPending) - 393usize]; + [::std::mem::offset_of!(android_app, inputSwapPending) - 401usize]; }; #[doc = " Looper data ID of commands coming from the app's main thread, which\n is returned as an identifier from ALooper_pollOnce(). The data for this\n identifier is a pointer to an android_poll_source structure.\n These can be retrieved and processed with android_app_read_cmd()\n and android_app_exec_cmd()."] pub const NativeAppGlueLooperId_LOOPER_ID_MAIN: NativeAppGlueLooperId = 1; @@ -5331,6 +5335,10 @@ unsafe extern "C" { #[doc = " Clear the array of key events that were waiting to be handled, and release\n each of them.\n\n This method should be called after you have processed the key up events in\n your game loop. You should handle events at each iteration of your game loop."] pub fn android_app_clear_key_events(inputBuffer: *mut android_input_buffer); } +unsafe extern "C" { + #[doc = " A hook that is called within Activity.onCreate, before the android_main\n thread has been spawned.\n\n This gives the Rust glue code a chance to perform any necessary\n initialization that needs to run from the Java main/UI thread, before the\n android_main thread is started."] + pub fn _rust_glue_on_create_hook(app: *mut android_app); +} unsafe extern "C" { #[doc = " This is a springboard into the Rust glue layer that wraps calling the\n main entry for the app itself."] pub fn _rust_glue_entry(app: *mut android_app); diff --git a/android-activity/src/game_activity/mod.rs b/android-activity/src/game_activity/mod.rs index 6a43771..1fc4cd0 100644 --- a/android-activity/src/game_activity/mod.rs +++ b/android-activity/src/game_activity/mod.rs @@ -959,8 +959,15 @@ extern "Rust" { pub fn android_main(app: AndroidApp); } +// This is called via `GameActivity.onCreate`, from the Java main/UI thread, +// before spawning an `android_main` thread. +#[no_mangle] +pub unsafe extern "C" fn _rust_glue_on_create_hook(_native_app: *mut ffi::android_app) { + // Noop currently +} + // This is a spring board between android_native_app_glue and the user's -// `app_main` function. This is run on a dedicated thread spawned +// `android_main` function. This is run on a dedicated thread spawned // by android_native_app_glue. #[no_mangle] pub unsafe extern "C" fn _rust_glue_entry(native_app: *mut ffi::android_app) {