From d2d18154d9cf6773d543619f1d3021104e77413a Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sat, 13 Aug 2022 05:42:37 +0100 Subject: [PATCH] GameActivity PATCH: 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. --- .../native_app_glue/android_native_app_glue.c | 28 +++++++++++++++++++ .../native_app_glue/android_native_app_glue.h | 25 +++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/android-activity/game-activity-csrc/game-activity/native_app_glue/android_native_app_glue.c b/android-activity/game-activity-csrc/game-activity/native_app_glue/android_native_app_glue.c index 01ee70c..8500ff6 100644 --- a/android-activity/game-activity-csrc/game-activity/native_app_glue/android_native_app_glue.c +++ b/android-activity/game-activity-csrc/game-activity/native_app_glue/android_native_app_glue.c @@ -477,6 +477,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); @@ -506,6 +529,7 @@ static bool onTouchEvent(GameActivity* activity, int new_ix = inputBuffer->motionEventsCount; memcpy(&inputBuffer->motionEvents[new_ix], event, sizeof(GameActivityMotionEvent)); ++inputBuffer->motionEventsCount; + notifyInput(android_app); pthread_mutex_unlock(&android_app->mutex); return true; @@ -527,6 +551,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; @@ -579,6 +606,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); pthread_mutex_unlock(&android_app->mutex); return true; diff --git a/android-activity/game-activity-csrc/game-activity/native_app_glue/android_native_app_glue.h b/android-activity/game-activity-csrc/game-activity/native_app_glue/android_native_app_glue.h index f504ac4..6fa5721 100644 --- a/android-activity/game-activity-csrc/game-activity/native_app_glue/android_native_app_glue.h +++ b/android-activity/game-activity-csrc/game-activity/native_app_glue/android_native_app_glue.h @@ -263,6 +263,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 */ }; @@ -475,6 +495,11 @@ void android_app_set_key_event_filter(struct android_app* app, void android_app_set_motion_event_filter(struct android_app* app, android_motion_event_filter filter); +/** + * 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