Last active
March 13, 2025 22:24
-
-
Save jb55/e162d48309ef874cd34397fa820945d8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
commit 3552189390f2693fea6ed6acbcb08b1b17db0bd5 | |
Author: Robert Bragg <[email protected]> | |
Date: Wed May 25 23:57:45 2022 +0100 | |
glue: remove unused variable | |
diff --git a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c | |
index ce6c7efdb8aa..626e7b82c10d 100644 | |
--- a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c | |
+++ b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c | |
@@ -402,7 +402,6 @@ static void onSaveInstanceState(GameActivity* activity, | |
LOGV("SaveInstanceState: %p", activity); | |
struct android_app* android_app = ToApp(activity); | |
- void* savedState = NULL; | |
pthread_mutex_lock(&android_app->mutex); | |
android_app->stateSaved = 0; | |
android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); | |
commit d68f8aff9ff655a68dfc2dabf2d9bfae2cc16c93 | |
Author: Robert Bragg <[email protected]> | |
Date: Fri Jul 28 22:18:24 2023 +0100 | |
glue: fix deadlocks in java callbacks after app destroyed | |
This ensures that any java Activity callbacks take into account the | |
possibility that the `android_app` may have already been marked | |
destroyed if `android_main` has returned - and so they mustn't block | |
and wait for a thread that is no longer running. | |
diff --git a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c | |
index 626e7b82c10d..f1c135f40d01 100644 | |
--- a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c | |
+++ b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c | |
@@ -327,6 +327,15 @@ static void android_app_set_window(struct android_app* android_app, | |
ANativeWindow* window) { | |
LOGV("android_app_set_window called"); | |
pthread_mutex_lock(&android_app->mutex); | |
+ | |
+ // NB: we have to consider that the native thread could have already | |
+ // (gracefully) exit (setting android_app->destroyed) and so we need | |
+ // to be careful to avoid a deadlock waiting for a thread that's | |
+ // already exit. | |
+ if (android_app->destroyed) { | |
+ pthread_mutex_unlock(&android_app->mutex); | |
+ return; | |
+ } | |
if (android_app->pendingWindow != NULL) { | |
android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW); | |
} | |
@@ -343,9 +352,16 @@ static void android_app_set_window(struct android_app* android_app, | |
static void android_app_set_activity_state(struct android_app* android_app, | |
int8_t cmd) { | |
pthread_mutex_lock(&android_app->mutex); | |
- android_app_write_cmd(android_app, cmd); | |
- while (android_app->activityState != cmd) { | |
- pthread_cond_wait(&android_app->cond, &android_app->mutex); | |
+ | |
+ // NB: we have to consider that the native thread could have already | |
+ // (gracefully) exit (setting android_app->destroyed) and so we need | |
+ // to be careful to avoid a deadlock waiting for a thread that's | |
+ // already exit. | |
+ if (!android_app->destroyed) { | |
+ android_app_write_cmd(android_app, cmd); | |
+ while (android_app->activityState != cmd) { | |
+ pthread_cond_wait(&android_app->cond, &android_app->mutex); | |
+ } | |
} | |
pthread_mutex_unlock(&android_app->mutex); | |
} | |
@@ -354,6 +370,14 @@ static void android_app_free(struct android_app* android_app) { | |
int input_buf_idx = 0; | |
pthread_mutex_lock(&android_app->mutex); | |
+ | |
+ // It's possible that onDestroy is called after we have already 'destroyed' | |
+ // the app (via `android_app_destroy` due to `android_main` returning. | |
+ // | |
+ // In this case `->destroyed` will already be set (so we won't deadlock in | |
+ // the loop below) but we still need to close the messaging fds and finish | |
+ // freeing the android_app | |
+ | |
android_app_write_cmd(android_app, APP_CMD_DESTROY); | |
while (!android_app->destroyed) { | |
pthread_cond_wait(&android_app->cond, &android_app->mutex); | |
@@ -403,6 +427,16 @@ static void onSaveInstanceState(GameActivity* activity, | |
struct android_app* android_app = ToApp(activity); | |
pthread_mutex_lock(&android_app->mutex); | |
+ | |
+ // NB: we have to consider that the native thread could have already | |
+ // (gracefully) exit (setting android_app->destroyed) and so we need | |
+ // to be careful to avoid a deadlock waiting for a thread that's | |
+ // already exit. | |
+ if (android_app->destroyed) { | |
+ pthread_mutex_unlock(&android_app->mutex); | |
+ return; | |
+ } | |
+ | |
android_app->stateSaved = 0; | |
android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); | |
while (!android_app->stateSaved) { | |
@@ -485,6 +519,15 @@ static bool onTouchEvent(GameActivity* activity, | |
struct android_app* android_app = ToApp(activity); | |
pthread_mutex_lock(&android_app->mutex); | |
+ // NB: we have to consider that the native thread could have already | |
+ // (gracefully) exit (setting android_app->destroyed) and so we need | |
+ // to be careful to avoid a deadlock waiting for a thread that's | |
+ // already exit. | |
+ if (android_app->destroyed) { | |
+ pthread_mutex_unlock(&android_app->mutex); | |
+ return false; | |
+ } | |
+ | |
if (android_app->motionEventFilter != NULL && | |
!android_app->motionEventFilter(event)) { | |
pthread_mutex_unlock(&android_app->mutex); | |
@@ -559,6 +602,15 @@ static bool onKey(GameActivity* activity, const GameActivityKeyEvent* event) { | |
struct android_app* android_app = ToApp(activity); | |
pthread_mutex_lock(&android_app->mutex); | |
+ // NB: we have to consider that the native thread could have already | |
+ // (gracefully) exit (setting android_app->destroyed) and so we need | |
+ // to be careful to avoid a deadlock waiting for a thread that's | |
+ // already exit. | |
+ if (android_app->destroyed) { | |
+ pthread_mutex_unlock(&android_app->mutex); | |
+ return false; | |
+ } | |
+ | |
if (android_app->keyEventFilter != NULL && | |
!android_app->keyEventFilter(event)) { | |
pthread_mutex_unlock(&android_app->mutex); | |
@@ -598,8 +650,9 @@ static void onTextInputEvent(GameActivity* activity, | |
const GameTextInputState* state) { | |
struct android_app* android_app = ToApp(activity); | |
pthread_mutex_lock(&android_app->mutex); | |
- | |
- android_app->textInputState = 1; | |
+ if (!android_app->destroyed) { | |
+ android_app->textInputState = 1; | |
+ } | |
pthread_mutex_unlock(&android_app->mutex); | |
} | |
commit 77ef23b4541278922f4b2d1fa376b6a56e6eafb6 | |
Author: Robert Bragg <[email protected]> | |
Date: Sat Aug 13 05:42:37 2022 +0100 | |
glue: support InputAvailable events | |
This makes a small change to the C glue code for GameActivity to send | |
looper wake ups when new input is received (only sending a single wake | |
up, until the application next handles input). | |
This makes it possible to recognise that new input is available and send | |
an `InputAvailable` event to the application - consistent with how | |
NativeActivity can deliver `InputAvailable` events. | |
This addresses a significant feature disparity between GameActivity and | |
NativeActivity that meant GameActivity was not practically usable for | |
GUI applications that wouldn't want to render continuously like a game. | |
diff --git a/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h b/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h | |
index d5d3eb02d235..f04e434e56ab 100644 | |
--- a/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h | |
+++ b/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h | |
@@ -273,6 +273,26 @@ struct android_app { | |
android_key_event_filter keyEventFilter; | |
android_motion_event_filter motionEventFilter; | |
+ // When new input is received we set both of these flags and use the looper to | |
+ // wake up the application mainloop. | |
+ // | |
+ // To avoid spamming the mainloop with wake ups from lots of input though we | |
+ // don't sent a wake up if the inputSwapPending flag is already set. (i.e. | |
+ // we already expect input to be processed in a finite amount of time due to | |
+ // our previous wake up) | |
+ // | |
+ // When a wake up is received then we will check this flag (clearing it | |
+ // at the same time). If it was set then an InputAvailable event is sent to | |
+ // the application - which should lead to all input being processed within | |
+ // a finite amount of time. | |
+ // | |
+ // The next time android_app_swap_input_buffers is called, both flags will be | |
+ // cleared. | |
+ // | |
+ // NB: both of these should only be read with the app mutex held | |
+ bool inputAvailableWakeUp; | |
+ bool inputSwapPending; | |
+ | |
/** @endcond */ | |
}; | |
@@ -518,6 +538,11 @@ void android_app_set_motion_event_filter(struct android_app* app, | |
*/ | |
void android_app_write_cmd(struct android_app* android_app, int8_t cmd); | |
+/** | |
+ * Determines if a looper wake up was due to new input becoming available | |
+ */ | |
+bool android_app_input_available_wake_up(struct android_app* app); | |
+ | |
#ifdef __cplusplus | |
} | |
#endif | |
diff --git a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c | |
index f1c135f40d01..745dc03d6de8 100644 | |
--- a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c | |
+++ b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c | |
@@ -514,6 +514,29 @@ void android_app_set_motion_event_filter(struct android_app* app, | |
pthread_mutex_unlock(&app->mutex); | |
} | |
+bool android_app_input_available_wake_up(struct android_app* app) { | |
+ pthread_mutex_lock(&app->mutex); | |
+ bool available = app->inputAvailableWakeUp; | |
+ app->inputAvailableWakeUp = false; | |
+ pthread_mutex_unlock(&app->mutex); | |
+ return available; | |
+} | |
+ | |
+// NB: should be called with the android_app->mutex held already | |
+static void notifyInput(struct android_app* android_app) { | |
+ // Don't spam the mainloop with wake ups if we've already sent one | |
+ if (android_app->inputSwapPending) { | |
+ return; | |
+ } | |
+ | |
+ if (android_app->looper != NULL) { | |
+ // for the app thread to know why it received the wake() up | |
+ android_app->inputAvailableWakeUp = true; | |
+ android_app->inputSwapPending = true; | |
+ ALooper_wake(android_app->looper); | |
+ } | |
+} | |
+ | |
static bool onTouchEvent(GameActivity* activity, | |
const GameActivityMotionEvent* event) { | |
struct android_app* android_app = ToApp(activity); | |
@@ -554,6 +577,7 @@ static bool onTouchEvent(GameActivity* activity, | |
memcpy(&inputBuffer->motionEvents[new_ix], event, | |
sizeof(GameActivityMotionEvent)); | |
++inputBuffer->motionEventsCount; | |
+ notifyInput(android_app); | |
android_app_write_cmd(android_app, APP_CMD_TOUCH_EVENT); | |
pthread_mutex_unlock(&android_app->mutex); | |
@@ -574,6 +598,9 @@ struct android_input_buffer* android_app_swap_input_buffers( | |
NATIVE_APP_GLUE_MAX_INPUT_BUFFERS; | |
} | |
+ android_app->inputSwapPending = false; | |
+ android_app->inputAvailableWakeUp = false; | |
+ | |
pthread_mutex_unlock(&android_app->mutex); | |
return inputBuffer; | |
@@ -636,6 +663,7 @@ static bool onKey(GameActivity* activity, const GameActivityKeyEvent* event) { | |
int new_ix = inputBuffer->keyEventsCount; | |
memcpy(&inputBuffer->keyEvents[new_ix], event, sizeof(GameActivityKeyEvent)); | |
++inputBuffer->keyEventsCount; | |
+ notifyInput(android_app); | |
android_app_write_cmd(android_app, APP_CMD_KEY_EVENT); | |
pthread_mutex_unlock(&android_app->mutex); | |
@@ -652,6 +680,7 @@ static void onTextInputEvent(GameActivity* activity, | |
pthread_mutex_lock(&android_app->mutex); | |
if (!android_app->destroyed) { | |
android_app->textInputState = 1; | |
+ notifyInput(android_app); | |
} | |
pthread_mutex_unlock(&android_app->mutex); | |
} | |
commit 14243455504385fd2186e0db5913f7f4e3aee17e | |
Author: Robert Bragg <[email protected]> | |
Date: Sat Jul 22 18:12:01 2023 +0100 | |
android-activity: rename C symbols that need export | |
Give C symbols that need to be exported a `_C` suffix so that they can | |
be linked into a Rust symbol with the correct name (Since we can't | |
directly export from C/C++ with Rust+Cargo) | |
See: https://github.com/rust-lang/rfcs/issues/2771 | |
We also rename android_main _rust_glue_entry | |
The real `android_main` is going to be written in Rust and | |
android-activity needs to handle its own initialization before calling | |
the application's `android_main` and so the C/C++ code | |
calls an intermediate `_rust_glue_entry` function. | |
diff --git a/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h b/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h | |
index 42f450a1cb5b..b6ff8b44afc5 100644 | |
--- a/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h | |
+++ b/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h | |
@@ -322,7 +322,7 @@ typedef void GameActivity_createFunc(GameActivity* activity, void* savedState, | |
* "android.app.func_name" string meta-data in your manifest to use a different | |
* function. | |
*/ | |
-extern GameActivity_createFunc GameActivity_onCreate; | |
+extern GameActivity_createFunc GameActivity_onCreate_C; | |
/** | |
* Finish the given activity. Its finish() method will be called, causing it | |
diff --git a/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h b/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h | |
index f04e434e56ab..cdb1ac8d9853 100644 | |
--- a/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h | |
+++ b/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h | |
@@ -501,10 +501,10 @@ void android_app_clear_motion_events(struct android_input_buffer* inputBuffer); | |
void android_app_clear_key_events(struct android_input_buffer* inputBuffer); | |
/** | |
- * This is the function that application code must implement, representing | |
- * the main entry to the app. | |
+ * This is a springboard into the Rust glue layer that wraps calling the | |
+ * main entry for the app itself. | |
*/ | |
-extern void android_main(struct android_app* app); | |
+extern void _rust_glue_entry(struct android_app* app); | |
/** | |
* Set the filter to use when processing key events. | |
diff --git a/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivity.cpp b/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivity.cpp | |
index d54f280a4e9e..a178793c9eb0 100644 | |
--- a/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivity.cpp | |
+++ b/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivity.cpp | |
@@ -538,7 +538,7 @@ static jlong initializeNativeCode_native( | |
// read configuration for the first time | |
readConfigurationValues(code, javaConfig); | |
- GameActivity_onCreate(code, rawSavedState, rawSavedSize); | |
+ GameActivity_onCreate_C(code, rawSavedState, rawSavedSize); | |
code->gameTextInput = GameTextInput_init(env, 0); | |
GameTextInput_setEventCallback(code->gameTextInput, | |
@@ -1382,8 +1382,12 @@ extern "C" int GameActivity_register(JNIEnv *env) { | |
NELEM(g_methods)); | |
} | |
+// XXX: This symbol is renamed with a _C suffix and then re-exported from | |
+// Rust because Rust/Cargo don't give us a way to directly export symbols | |
+// from C/C++ code: https://github.com/rust-lang/rfcs/issues/2771 | |
+// | |
extern "C" JNIEXPORT jlong JNICALL | |
-Java_com_google_androidgamesdk_GameActivity_initializeNativeCode( | |
+Java_com_google_androidgamesdk_GameActivity_initializeNativeCode_C( | |
JNIEnv *env, jobject javaGameActivity, jstring internalDataDir, | |
jstring obbDir, jstring externalDataDir, jobject jAssetMgr, | |
jbyteArray savedState, jobject javaConfig) { | |
diff --git a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c | |
index 745dc03d6de8..48069d9ce8a3 100644 | |
--- a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c | |
+++ b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c | |
@@ -234,7 +234,7 @@ static void* android_app_entry(void* param) { | |
pthread_cond_broadcast(&android_app->cond); | |
pthread_mutex_unlock(&android_app->mutex); | |
- android_main(android_app); | |
+ _rust_glue_entry(android_app); | |
android_app_destroy(android_app); | |
return NULL; | |
@@ -729,9 +729,13 @@ static bool onEditorAction(GameActivity* activity, int action) { | |
return true; | |
} | |
+// XXX: This symbol is renamed with a _C suffix and then re-exported from | |
+// Rust because Rust/Cargo don't give us a way to directly export symbols | |
+// from C/C++ code: https://github.com/rust-lang/rfcs/issues/2771 | |
+// | |
JNIEXPORT | |
-void GameActivity_onCreate(GameActivity* activity, void* savedState, | |
- size_t savedStateSize) { | |
+void GameActivity_onCreate_C(GameActivity* activity, void* savedState, | |
+ size_t savedStateSize) { | |
LOGV("Creating: %p", activity); | |
activity->callbacks->onDestroy = onDestroy; | |
activity->callbacks->onStart = onStart; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment