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.
This commit is contained in:
Robert Bragg
2022-08-13 05:42:37 +01:00
parent 3e3fb84c03
commit d2d18154d9
2 changed files with 53 additions and 0 deletions
@@ -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;
@@ -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