mirror of
https://github.com/rust-mobile/android-activity.git
synced 2026-07-14 02:28:54 +00:00
GameActivity PATCH: support capturing historic pointer samples
This commit is contained in:
@@ -840,6 +840,27 @@ extern "C" void GameActivityPointerAxes_disableAxis(int32_t axis) {
|
||||
enabledAxes[axis] = false;
|
||||
}
|
||||
|
||||
static bool enabledHistoricalAxes[GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT] = {
|
||||
// Disable all axes by default (they can be enabled using
|
||||
// `GameActivityPointerAxes_enableHistoricalAxis`).
|
||||
false};
|
||||
|
||||
extern "C" void GameActivityHistoricalPointerAxes_enableAxis(int32_t axis) {
|
||||
if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) {
|
||||
return;
|
||||
}
|
||||
|
||||
enabledHistoricalAxes[axis] = true;
|
||||
}
|
||||
|
||||
extern "C" void GameActivityHistoricalPointerAxes_disableAxis(int32_t axis) {
|
||||
if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) {
|
||||
return;
|
||||
}
|
||||
|
||||
enabledHistoricalAxes[axis] = false;
|
||||
}
|
||||
|
||||
extern "C" void GameActivity_setImeEditorInfo(GameActivity *activity,
|
||||
int inputType, int actionId,
|
||||
int imeOptions) {
|
||||
@@ -874,10 +895,15 @@ static struct {
|
||||
jmethodID getXPrecision;
|
||||
jmethodID getYPrecision;
|
||||
jmethodID getAxisValue;
|
||||
|
||||
jmethodID getHistorySize;
|
||||
jmethodID getHistoricalEventTime;
|
||||
jmethodID getHistoricalAxisValue;
|
||||
} gMotionEventClassInfo;
|
||||
|
||||
extern "C" void GameActivityMotionEvent_fromJava(
|
||||
JNIEnv *env, jobject motionEvent, GameActivityMotionEvent *out_event) {
|
||||
extern "C" int GameActivityMotionEvent_fromJava(
|
||||
JNIEnv *env, jobject motionEvent, GameActivityMotionEvent *out_event,
|
||||
GameActivityHistoricalPointerAxes *out_historical) {
|
||||
static bool gMotionEventClassInfoInitialized = false;
|
||||
if (!gMotionEventClassInfoInitialized) {
|
||||
int sdkVersion = GetSystemPropAsInt("ro.build.version.sdk");
|
||||
@@ -928,9 +954,44 @@ extern "C" void GameActivityMotionEvent_fromJava(
|
||||
gMotionEventClassInfo.getAxisValue =
|
||||
env->GetMethodID(motionEventClass, "getAxisValue", "(II)F");
|
||||
|
||||
gMotionEventClassInfo.getHistorySize =
|
||||
env->GetMethodID(motionEventClass, "getHistorySize", "()I");
|
||||
gMotionEventClassInfo.getHistoricalEventTime =
|
||||
env->GetMethodID(motionEventClass, "getHistoricalEventTime", "(I)J");
|
||||
gMotionEventClassInfo.getHistoricalAxisValue =
|
||||
env->GetMethodID(motionEventClass, "getHistoricalAxisValue", "(III)F");
|
||||
|
||||
gMotionEventClassInfoInitialized = true;
|
||||
}
|
||||
|
||||
int historySize =
|
||||
env->CallIntMethod(motionEvent, gMotionEventClassInfo.getHistorySize);
|
||||
historySize =
|
||||
std::min(historySize, GAMEACTIVITY_MAX_NUM_HISTORICAL_IN_MOTION_EVENT);
|
||||
|
||||
int localEnabledHistoricalAxis[GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT];
|
||||
int enabledHistoricalAxisCount = 0;
|
||||
|
||||
for (int axisIndex = 0;
|
||||
axisIndex < GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT;
|
||||
++axisIndex) {
|
||||
if (enabledHistoricalAxes[axisIndex]) {
|
||||
localEnabledHistoricalAxis[enabledHistoricalAxisCount++] = axisIndex;
|
||||
}
|
||||
}
|
||||
out_event->historicalCount = enabledHistoricalAxisCount == 0 ? 0 : historySize;
|
||||
out_event->historicalStart = 0; // Free for caller to use
|
||||
|
||||
// The historical event times aren't unique per-pointer but for simplicity
|
||||
// we output a per-pointer event time, copied from here...
|
||||
int64_t historicalEventTimes[GAMEACTIVITY_MAX_NUM_HISTORICAL_IN_MOTION_EVENT];
|
||||
for (int histIndex = 0; histIndex < historySize; ++histIndex) {
|
||||
historicalEventTimes[histIndex] =
|
||||
env->CallLongMethod(motionEvent,
|
||||
gMotionEventClassInfo.getHistoricalEventTime) *
|
||||
1000000;
|
||||
}
|
||||
|
||||
int pointerCount =
|
||||
env->CallIntMethod(motionEvent, gMotionEventClassInfo.getPointerCount);
|
||||
pointerCount =
|
||||
@@ -960,6 +1021,20 @@ extern "C" void GameActivityMotionEvent_fromJava(
|
||||
axisIndex, i);
|
||||
}
|
||||
}
|
||||
|
||||
if (enabledHistoricalAxisCount > 0) {
|
||||
for (int histIndex = 0; histIndex < historySize; ++histIndex) {
|
||||
int pointerHistIndex = historySize * i;
|
||||
out_historical[pointerHistIndex].eventTime = historicalEventTimes[histIndex];
|
||||
for (int c = 0; c < enabledHistoricalAxisCount; ++c) {
|
||||
int axisIndex = localEnabledHistoricalAxis[c];
|
||||
out_historical[pointerHistIndex].axisValues[axisIndex] =
|
||||
env->CallFloatMethod(motionEvent,
|
||||
gMotionEventClassInfo.getHistoricalAxisValue,
|
||||
axisIndex, i, histIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out_event->deviceId =
|
||||
@@ -999,6 +1074,8 @@ extern "C" void GameActivityMotionEvent_fromJava(
|
||||
env->CallFloatMethod(motionEvent, gMotionEventClassInfo.getXPrecision);
|
||||
out_event->precisionY =
|
||||
env->CallFloatMethod(motionEvent, gMotionEventClassInfo.getYPrecision);
|
||||
|
||||
return out_event->pointerCount * out_event->historicalCount;
|
||||
}
|
||||
|
||||
static struct {
|
||||
@@ -1085,8 +1162,11 @@ static bool onTouchEvent_native(JNIEnv *env, jobject javaGameActivity,
|
||||
if (code->callbacks.onTouchEvent == nullptr) return false;
|
||||
|
||||
static GameActivityMotionEvent c_event;
|
||||
GameActivityMotionEvent_fromJava(env, motionEvent, &c_event);
|
||||
return code->callbacks.onTouchEvent(code, &c_event);
|
||||
// Note the actual data is written contiguously as numPointers x historySize
|
||||
// entries.
|
||||
static GameActivityHistoricalPointerAxes historical[GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT * GAMEACTIVITY_MAX_NUM_HISTORICAL_IN_MOTION_EVENT];
|
||||
int historicalLen = GameActivityMotionEvent_fromJava(env, motionEvent, &c_event, historical);
|
||||
return code->callbacks.onTouchEvent(code, &c_event, historical, historicalLen);
|
||||
}
|
||||
|
||||
static bool onKeyUp_native(JNIEnv *env, jobject javaGameActivity, jlong handle,
|
||||
|
||||
@@ -142,6 +142,11 @@ typedef struct GameActivityPointerAxes {
|
||||
float rawY;
|
||||
} GameActivityPointerAxes;
|
||||
|
||||
typedef struct GameActivityHistoricalPointerAxes {
|
||||
int64_t eventTime;
|
||||
float axisValues[GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT];
|
||||
} GameActivityHistoricalPointerAxes;
|
||||
|
||||
/** \brief Get the current X coordinate of the pointer. */
|
||||
inline float GameActivityPointerAxes_getX(
|
||||
const GameActivityPointerAxes* pointerInfo) {
|
||||
@@ -177,6 +182,26 @@ void GameActivityPointerAxes_enableAxis(int32_t axis);
|
||||
*/
|
||||
void GameActivityPointerAxes_disableAxis(int32_t axis);
|
||||
|
||||
/**
|
||||
* \brief Enable the specified axis, so that its value is reported in the
|
||||
* GameActivityHistoricalPointerAxes structures associated with a motion event.
|
||||
*
|
||||
* You must enable any axis that you want to read (no axes are enabled by
|
||||
* default).
|
||||
*
|
||||
* If the axis index is out of range, nothing is done.
|
||||
*/
|
||||
void GameActivityHistoricalPointerAxes_enableAxis(int32_t axis);
|
||||
|
||||
/**
|
||||
* \brief Disable the specified axis. Its value won't be reported in the
|
||||
* GameActivityHistoricalPointerAxes structures associated with motion events
|
||||
* anymore.
|
||||
*
|
||||
* If the axis index is out of range, nothing is done.
|
||||
*/
|
||||
void GameActivityHistoricalPointerAxes_disableAxis(int32_t axis);
|
||||
|
||||
/**
|
||||
* \brief Get the value of the requested axis.
|
||||
*
|
||||
@@ -212,6 +237,16 @@ inline float GameActivityPointerAxes_getAxisValue(
|
||||
#define GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT 8
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The maximum number of historic samples associated with a single motion event.
|
||||
*/
|
||||
#if (defined GAMEACTIVITY_MAX_NUM_HISTORICAL_IN_MOTION_EVENT_OVERRIDE)
|
||||
#define GAMEACTIVITY_MAX_NUM_HISTORICAL_IN_MOTION_EVENT \
|
||||
GAMEACTIVITY_MAX_NUM_HISTORICAL_IN_MOTION_EVENT_OVERRIDE
|
||||
#else
|
||||
#define GAMEACTIVITY_MAX_NUM_HISTORICAL_IN_MOTION_EVENT 8
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Describe a motion event that happened on the GameActivity SurfaceView.
|
||||
*
|
||||
@@ -240,6 +275,13 @@ typedef struct GameActivityMotionEvent {
|
||||
|
||||
float precisionX;
|
||||
float precisionY;
|
||||
|
||||
int16_t historicalStart;
|
||||
|
||||
// Note the actual buffer of historical data has a length of
|
||||
// pointerCount * historicalCount, since the historical axis
|
||||
// data is per-pointer.
|
||||
int16_t historicalCount;
|
||||
} GameActivityMotionEvent;
|
||||
|
||||
/**
|
||||
@@ -381,7 +423,9 @@ typedef struct GameActivityCallbacks {
|
||||
* only valid during the callback.
|
||||
*/
|
||||
bool (*onTouchEvent)(GameActivity* activity,
|
||||
const GameActivityMotionEvent* event);
|
||||
const GameActivityMotionEvent* event,
|
||||
const GameActivityHistoricalPointerAxes* historical,
|
||||
int historicalLen);
|
||||
|
||||
/**
|
||||
* Callback called for every key down event on the GameActivity SurfaceView.
|
||||
@@ -419,11 +463,16 @@ typedef struct GameActivityCallbacks {
|
||||
* This is done automatically by the GameActivity: see `onTouchEvent` to set
|
||||
* a callback to consume the received events.
|
||||
* This function can be used if you re-implement events handling in your own
|
||||
* activity.
|
||||
* activity. On return, the out_event->historicalStart will be zero, and should
|
||||
* be updated to index into whatever buffer out_historical is copied.
|
||||
* On return the length of out_historical is
|
||||
* (out_event->pointerCount x out_event->historicalCount) and is in a
|
||||
* pointer-major order (i.e. all axis for a pointer are contiguous)
|
||||
* Ownership of out_event is maintained by the caller.
|
||||
*/
|
||||
void GameActivityMotionEvent_fromJava(JNIEnv* env, jobject motionEvent,
|
||||
GameActivityMotionEvent* out_event);
|
||||
int GameActivityMotionEvent_fromJava(JNIEnv* env, jobject motionEvent,
|
||||
GameActivityMotionEvent* out_event,
|
||||
GameActivityHistoricalPointerAxes *out_historical);
|
||||
|
||||
/**
|
||||
* \brief Convert a Java `KeyEvent` to a `GameActivityKeyEvent`.
|
||||
|
||||
@@ -441,7 +441,9 @@ void android_app_set_motion_event_filter(struct android_app* app,
|
||||
}
|
||||
|
||||
static bool onTouchEvent(GameActivity* activity,
|
||||
const GameActivityMotionEvent* event) {
|
||||
const GameActivityMotionEvent* event,
|
||||
const GameActivityHistoricalPointerAxes* historical,
|
||||
int historicalLen) {
|
||||
struct android_app* android_app = ToApp(activity);
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
|
||||
@@ -461,6 +463,20 @@ static bool onTouchEvent(GameActivity* activity,
|
||||
memcpy(&inputBuffer->motionEvents[new_ix], event,
|
||||
sizeof(GameActivityMotionEvent));
|
||||
++inputBuffer->motionEventsCount;
|
||||
|
||||
if (inputBuffer->historicalSamplesCount + historicalLen <=
|
||||
NATIVE_APP_GLUE_MAX_HISTORICAL_POINTER_SAMPLES) {
|
||||
|
||||
int start_ix = inputBuffer->historicalSamplesCount;
|
||||
memcpy(&inputBuffer->historicalAxisSamples[start_ix], historical,
|
||||
sizeof(historical[0]) * historicalLen);
|
||||
inputBuffer->historicalSamplesCount += event->historicalCount;
|
||||
|
||||
inputBuffer->motionEvents[new_ix].historicalStart = start_ix;
|
||||
inputBuffer->motionEvents[new_ix].historicalCount = historicalLen;
|
||||
} else {
|
||||
inputBuffer->motionEvents[new_ix].historicalCount = 0;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
return true;
|
||||
|
||||
@@ -37,6 +37,13 @@
|
||||
#define NATIVE_APP_GLUE_MAX_NUM_MOTION_EVENTS 16
|
||||
#endif
|
||||
|
||||
#if (defined NATIVE_APP_GLUE_MAX_HISTORICAL_POINTER_SAMPLES_OVERRIDE)
|
||||
#define NATIVE_APP_GLUE_MAX_HISTORICAL_POINTER_SAMPLES \
|
||||
NATIVE_APP_GLUE_MAX_HISTORICAL_POINTER_SAMPLES_OVERRIDE
|
||||
#else
|
||||
#define NATIVE_APP_GLUE_MAX_HISTORICAL_POINTER_SAMPLES 64
|
||||
#endif
|
||||
|
||||
#if (defined NATIVE_APP_GLUE_MAX_NUM_KEY_EVENTS_OVERRIDE)
|
||||
#define NATIVE_APP_GLUE_MAX_NUM_KEY_EVENTS \
|
||||
NATIVE_APP_GLUE_MAX_NUM_KEY_EVENTS_OVERRIDE
|
||||
@@ -129,6 +136,27 @@ struct android_input_buffer {
|
||||
*/
|
||||
uint64_t motionEventsCount;
|
||||
|
||||
/**
|
||||
* Pointer to a read-only array of pointers to GameActivityHistoricalPointerAxes.
|
||||
*
|
||||
* Only the first historicalSamplesCount samples are valid.
|
||||
* Refer to event->historicalStart, event->pointerCount and event->historicalCount
|
||||
* to access the specific samples that relate to an event.
|
||||
*
|
||||
* Each slice of samples for one event has a length of
|
||||
* (event->pointerCount and event->historicalCount) and is in pointer-major
|
||||
* order so the historic samples for each pointer are contiguous.
|
||||
* E.g. you would access historic sample index 3 for pointer 2 of an event with:
|
||||
*
|
||||
* historicalAxisSamples[event->historicalStart + (event->historicalCount * 2) + 3];
|
||||
*/
|
||||
GameActivityHistoricalPointerAxes historicalAxisSamples[NATIVE_APP_GLUE_MAX_HISTORICAL_POINTER_SAMPLES];
|
||||
|
||||
/**
|
||||
* The number of valid historical samples in `historicalAxisSamples`.
|
||||
*/
|
||||
uint64_t historicalSamplesCount;
|
||||
|
||||
/**
|
||||
* Pointer to a read-only array of pointers to GameActivityKeyEvent.
|
||||
* Only the first keyEventsCount events are valid.
|
||||
|
||||
Reference in New Issue
Block a user