diff --git a/android-activity/game-activity-csrc/common/gamesdk_common.h b/android-activity/game-activity-csrc/common/gamesdk_common.h deleted file mode 100644 index d29ac01..0000000 --- a/android-activity/game-activity-csrc/common/gamesdk_common.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * This is the main interface to the Android Performance Tuner library, also - * known as Tuning Fork. - * - * It is part of the Android Games SDK and produces best results when integrated - * with the Swappy Frame Pacing Library. - * - * See the documentation at - * https://developer.android.com/games/sdk/performance-tuner/custom-engine for - * more information on using this library in a native Android game. - * - */ - -#pragma once - -// There are separate versions for each GameSDK component that use this format: -#define ANDROID_GAMESDK_PACKED_VERSION(MAJOR, MINOR, BUGFIX) \ - ((MAJOR << 16) | (MINOR << 8) | (BUGFIX)) -// Accessors -#define ANDROID_GAMESDK_MAJOR_VERSION(PACKED) ((PACKED) >> 16) -#define ANDROID_GAMESDK_MINOR_VERSION(PACKED) (((PACKED) >> 8) & 0xff) -#define ANDROID_GAMESDK_BUGFIX_VERSION(PACKED) ((PACKED) & 0xff) - -#define AGDK_STRING_VERSION(MAJOR, MINOR, BUGFIX, GIT) \ -#MAJOR "." #MINOR "." #BUGFIX "." #GIT diff --git a/android-activity/game-activity-csrc/game-activity/GameActivity.cpp b/android-activity/game-activity-csrc/game-activity/GameActivity.cpp deleted file mode 100644 index 4dbc9e8..0000000 --- a/android-activity/game-activity-csrc/game-activity/GameActivity.cpp +++ /dev/null @@ -1,1147 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "GameActivity.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "GameActivityLog.h" - -namespace { - -struct OwnedGameTextInputState { - OwnedGameTextInputState &operator=(const GameTextInputState &rhs) { - inner = rhs; - owned_string = std::string(rhs.text_UTF8, rhs.text_length); - inner.text_UTF8 = owned_string.data(); - return *this; - } - GameTextInputState inner; - std::string owned_string; -}; - -} // anonymous namespace - -#ifndef NELEM -#define NELEM(x) ((int)(sizeof(x) / sizeof((x)[0]))) -#endif - -/* - * JNI methods of the GameActivity Java class. - */ -static struct { - jmethodID finish; - jmethodID setWindowFlags; - jmethodID getWindowInsets; - jmethodID getWaterfallInsets; - jmethodID setImeEditorInfoFields; -} gGameActivityClassInfo; - -/* - * JNI fields of the androidx.core.graphics.Insets Java class. - */ -static struct { - jfieldID left; - jfieldID right; - jfieldID top; - jfieldID bottom; -} gInsetsClassInfo; - -/* - * JNI fields of the Configuration Java class. - */ -static struct ConfigurationClassInfo { - jfieldID colorMode; - jfieldID densityDpi; - jfieldID fontScale; - jfieldID fontWeightAdjustment; - jfieldID hardKeyboardHidden; - jfieldID keyboard; - jfieldID keyboardHidden; - jfieldID mcc; - jfieldID mnc; - jfieldID navigation; - jfieldID navigationHidden; - jfieldID orientation; - jfieldID screenHeightDp; - jfieldID screenLayout; - jfieldID screenWidthDp; - jfieldID smallestScreenWidthDp; - jfieldID touchscreen; - jfieldID uiMode; -} gConfigurationClassInfo; - -/* - * JNI methods of the WindowInsetsCompat.Type Java class. - */ -static struct { - jmethodID methods[GAMECOMMON_INSETS_TYPE_COUNT]; - jclass clazz; -} gWindowInsetsCompatTypeClassInfo; - -/* - * Contains a command to be executed by the GameActivity - * on the application main thread. - */ -struct ActivityWork { - int32_t cmd; - int64_t arg1; - int64_t arg2; - int64_t arg3; -}; - -/* - * The type of commands that can be passed to the GameActivity and that - * are executed on the application main thread. - */ -enum { - CMD_FINISH = 1, - CMD_SET_WINDOW_FORMAT, - CMD_SET_WINDOW_FLAGS, - CMD_SHOW_SOFT_INPUT, - CMD_HIDE_SOFT_INPUT, - CMD_SET_SOFT_INPUT_STATE, - CMD_SET_IME_EDITOR_INFO -}; - -/* - * Last known Configuration values. They may be accessed from the different - * thread, this is why they are made atomic. - */ -static struct Configuration { - std::atomic_int colorMode; - std::atomic_int densityDpi; - std::atomic fontScale; - std::atomic_int fontWeightAdjustment; - std::atomic_int hardKeyboardHidden; - std::atomic_int keyboard; - std::atomic_int keyboardHidden; - std::atomic_int mcc; - std::atomic_int mnc; - std::atomic_int navigation; - std::atomic_int navigationHidden; - std::atomic_int orientation; - std::atomic_int screenHeightDp; - std::atomic_int screenLayout; - std::atomic_int screenWidthDp; - std::atomic_int smallestScreenWidthDp; - std::atomic_int touchscreen; - std::atomic_int uiMode; -} gConfiguration; - -/* - * Write a command to be executed by the GameActivity on the application main - * thread. - */ -static void write_work(int fd, int32_t cmd, int64_t arg1 = 0, int64_t arg2 = 0, - int64_t arg3 = 0) { - ActivityWork work; - work.cmd = cmd; - work.arg1 = arg1; - work.arg2 = arg2; - work.arg3 = arg3; - - LOG_TRACE("write_work: cmd=%d", cmd); -restart: - int res = write(fd, &work, sizeof(work)); - if (res < 0 && errno == EINTR) { - goto restart; - } - - if (res == sizeof(work)) return; - - if (res < 0) { - ALOGW("Failed writing to work fd: %s", strerror(errno)); - } else { - ALOGW("Truncated writing to work fd: %d", res); - } -} - -/* - * Read commands to be executed by the GameActivity on the application main - * thread. - */ -static bool read_work(int fd, ActivityWork *outWork) { - int res = read(fd, outWork, sizeof(ActivityWork)); - // no need to worry about EINTR, poll loop will just come back again. - if (res == sizeof(ActivityWork)) return true; - - if (res < 0) { - ALOGW("Failed reading work fd: %s", strerror(errno)); - } else { - ALOGW("Truncated reading work fd: %d", res); - } - return false; -} - -/* - * Native state for interacting with the GameActivity class. - */ -struct NativeCode : public GameActivity { - NativeCode() { - memset((GameActivity *)this, 0, sizeof(GameActivity)); - memset(&callbacks, 0, sizeof(callbacks)); - memset(&insetsState, 0, sizeof(insetsState)); - nativeWindow = NULL; - mainWorkRead = mainWorkWrite = -1; - gameTextInput = NULL; - } - - ~NativeCode() { - if (callbacks.onDestroy != NULL) { - callbacks.onDestroy(this); - } - if (env != NULL) { - if (javaGameActivity != NULL) { - env->DeleteGlobalRef(javaGameActivity); - } - if (javaAssetManager != NULL) { - env->DeleteGlobalRef(javaAssetManager); - } - } - GameTextInput_destroy(gameTextInput); - if (looper != NULL && mainWorkRead >= 0) { - ALooper_removeFd(looper, mainWorkRead); - } - ALooper_release(looper); - looper = NULL; - - setSurface(NULL); - if (mainWorkRead >= 0) close(mainWorkRead); - if (mainWorkWrite >= 0) close(mainWorkWrite); - } - - void setSurface(jobject _surface) { - if (nativeWindow != NULL) { - ANativeWindow_release(nativeWindow); - } - if (_surface != NULL) { - nativeWindow = ANativeWindow_fromSurface(env, _surface); - } else { - nativeWindow = NULL; - } - } - - GameActivityCallbacks callbacks; - - std::string internalDataPathObj; - std::string externalDataPathObj; - std::string obbPathObj; - - ANativeWindow *nativeWindow; - int32_t lastWindowWidth; - int32_t lastWindowHeight; - - // These are used to wake up the main thread to process work. - int mainWorkRead; - int mainWorkWrite; - ALooper *looper; - - // Need to hold on to a reference here in case the upper layers destroy our - // AssetManager. - jobject javaAssetManager; - - GameTextInput *gameTextInput; - // Set by users in GameActivity_setTextInputState, then passed to - // GameTextInput. - OwnedGameTextInputState gameTextInputState; - std::mutex gameTextInputStateMutex; - - ARect insetsState[GAMECOMMON_INSETS_TYPE_COUNT]; -}; - -static void readConfigurationValues(NativeCode *code, jobject javaConfig); - -extern "C" void GameActivity_finish(GameActivity *activity) { - NativeCode *code = static_cast(activity); - write_work(code->mainWorkWrite, CMD_FINISH, 0); -} - -extern "C" void GameActivity_setWindowFlags(GameActivity *activity, - uint32_t values, uint32_t mask) { - NativeCode *code = static_cast(activity); - write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask); -} - -extern "C" void GameActivity_showSoftInput(GameActivity *activity, - uint32_t flags) { - NativeCode *code = static_cast(activity); - write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags); -} - -extern "C" void GameActivity_setTextInputState( - GameActivity *activity, const GameTextInputState *state) { - NativeCode *code = static_cast(activity); - std::lock_guard lock(code->gameTextInputStateMutex); - code->gameTextInputState = *state; - write_work(code->mainWorkWrite, CMD_SET_SOFT_INPUT_STATE); -} - -extern "C" void GameActivity_getTextInputState( - GameActivity *activity, GameTextInputGetStateCallback callback, - void *context) { - NativeCode *code = static_cast(activity); - return GameTextInput_getState(code->gameTextInput, callback, context); -} - -extern "C" void GameActivity_hideSoftInput(GameActivity *activity, - uint32_t flags) { - NativeCode *code = static_cast(activity); - write_work(code->mainWorkWrite, CMD_HIDE_SOFT_INPUT, flags); -} - -extern "C" void GameActivity_getWindowInsets(GameActivity *activity, - GameCommonInsetsType type, - ARect *insets) { - if (type < 0 || type >= GAMECOMMON_INSETS_TYPE_COUNT) return; - NativeCode *code = static_cast(activity); - *insets = code->insetsState[type]; -} - -extern "C" GameTextInput *GameActivity_getTextInput( - const GameActivity *activity) { - const NativeCode *code = static_cast(activity); - return code->gameTextInput; -} - -/* - * Log the JNI exception, if any. - */ -static void checkAndClearException(JNIEnv *env, const char *methodName) { - if (env->ExceptionCheck()) { - ALOGE("Exception while running %s", methodName); - env->ExceptionDescribe(); - env->ExceptionClear(); - } -} - -/* - * Callback for handling native events on the application's main thread. - */ -static int mainWorkCallback(int fd, int events, void *data) { - ALOGD("************** mainWorkCallback *********"); - NativeCode *code = (NativeCode *)data; - if ((events & POLLIN) == 0) { - return 1; - } - - ActivityWork work; - if (!read_work(code->mainWorkRead, &work)) { - return 1; - } - LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd); - switch (work.cmd) { - case CMD_FINISH: { - code->env->CallVoidMethod(code->javaGameActivity, - gGameActivityClassInfo.finish); - checkAndClearException(code->env, "finish"); - } break; - case CMD_SET_WINDOW_FLAGS: { - code->env->CallVoidMethod(code->javaGameActivity, - gGameActivityClassInfo.setWindowFlags, - work.arg1, work.arg2); - checkAndClearException(code->env, "setWindowFlags"); - } break; - case CMD_SHOW_SOFT_INPUT: { - GameTextInput_showIme(code->gameTextInput, work.arg1); - } break; - case CMD_SET_SOFT_INPUT_STATE: { - std::lock_guard lock(code->gameTextInputStateMutex); - GameTextInput_setState(code->gameTextInput, - &code->gameTextInputState.inner); - checkAndClearException(code->env, "setTextInputState"); - } break; - case CMD_HIDE_SOFT_INPUT: { - GameTextInput_hideIme(code->gameTextInput, work.arg1); - } break; - case CMD_SET_IME_EDITOR_INFO: { - code->env->CallVoidMethod( - code->javaGameActivity, - gGameActivityClassInfo.setImeEditorInfoFields, work.arg1, - work.arg2, work.arg3); - checkAndClearException(code->env, "setImeEditorInfo"); - } break; - default: - ALOGW("Unknown work command: %d", work.cmd); - break; - } - - return 1; -} - -// ------------------------------------------------------------------------ -static thread_local std::string g_error_msg; - -static jlong initializeNativeCode_native( - JNIEnv *env, jobject javaGameActivity, jstring internalDataDir, - jstring obbDir, jstring externalDataDir, jobject jAssetMgr, - jbyteArray savedState, jobject javaConfig) { - LOG_TRACE("initializeNativeCode_native"); - NativeCode *code = NULL; - - code = new NativeCode(); - - code->looper = ALooper_forThread(); - if (code->looper == nullptr) { - g_error_msg = "Unable to retrieve native ALooper"; - ALOGW("%s", g_error_msg.c_str()); - delete code; - return 0; - } - ALooper_acquire(code->looper); - - int msgpipe[2]; - if (pipe(msgpipe)) { - g_error_msg = "could not create pipe: "; - g_error_msg += strerror(errno); - - ALOGW("%s", g_error_msg.c_str()); - delete code; - return 0; - } - code->mainWorkRead = msgpipe[0]; - code->mainWorkWrite = msgpipe[1]; - int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK); - SLOGW_IF(result != 0, - "Could not make main work read pipe " - "non-blocking: %s", - strerror(errno)); - result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK); - SLOGW_IF(result != 0, - "Could not make main work write pipe " - "non-blocking: %s", - strerror(errno)); - ALooper_addFd(code->looper, code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, - mainWorkCallback, code); - - code->GameActivity::callbacks = &code->callbacks; - if (env->GetJavaVM(&code->vm) < 0) { - ALOGW("GameActivity GetJavaVM failed"); - delete code; - return 0; - } - code->env = env; - code->javaGameActivity = env->NewGlobalRef(javaGameActivity); - - const char *dirStr = - internalDataDir ? env->GetStringUTFChars(internalDataDir, NULL) : ""; - code->internalDataPathObj = dirStr; - code->internalDataPath = code->internalDataPathObj.c_str(); - if (internalDataDir) env->ReleaseStringUTFChars(internalDataDir, dirStr); - - dirStr = - externalDataDir ? env->GetStringUTFChars(externalDataDir, NULL) : ""; - code->externalDataPathObj = dirStr; - code->externalDataPath = code->externalDataPathObj.c_str(); - if (externalDataDir) env->ReleaseStringUTFChars(externalDataDir, dirStr); - - code->javaAssetManager = env->NewGlobalRef(jAssetMgr); - code->assetManager = AAssetManager_fromJava(env, jAssetMgr); - - dirStr = obbDir ? env->GetStringUTFChars(obbDir, NULL) : ""; - code->obbPathObj = dirStr; - code->obbPath = code->obbPathObj.c_str(); - if (obbDir) env->ReleaseStringUTFChars(obbDir, dirStr); - - jbyte *rawSavedState = NULL; - jsize rawSavedSize = 0; - if (savedState != NULL) { - rawSavedState = env->GetByteArrayElements(savedState, NULL); - rawSavedSize = env->GetArrayLength(savedState); - } - - readConfigurationValues(code, javaConfig); - - GameActivity_onCreate_C(code, rawSavedState, rawSavedSize); - - code->gameTextInput = GameTextInput_init(env, 0); - GameTextInput_setEventCallback(code->gameTextInput, - reinterpret_cast( - code->callbacks.onTextInputEvent), - code); - - if (rawSavedState != NULL) { - env->ReleaseByteArrayElements(savedState, rawSavedState, 0); - } - - return reinterpret_cast(code); -} - -static jstring getDlError_native(JNIEnv *env, jobject javaGameActivity) { - jstring result = env->NewStringUTF(g_error_msg.c_str()); - g_error_msg.clear(); - return result; -} - -static void terminateNativeCode_native(JNIEnv *env, jobject javaGameActivity, - jlong handle) { - LOG_TRACE("terminateNativeCode_native"); - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; - delete code; - } -} - -static void onStart_native(JNIEnv *env, jobject javaGameActivity, - jlong handle) { - ALOGV("onStart_native"); - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; - if (code->callbacks.onStart != NULL) { - code->callbacks.onStart(code); - } - } -} - -static void onResume_native(JNIEnv *env, jobject javaGameActivity, - jlong handle) { - LOG_TRACE("onResume_native"); - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; - if (code->callbacks.onResume != NULL) { - code->callbacks.onResume(code); - } - } -} - -struct SaveInstanceLocals { - JNIEnv *env; - jbyteArray array; -}; - -static jbyteArray onSaveInstanceState_native(JNIEnv *env, - jobject javaGameActivity, - jlong handle) { - LOG_TRACE("onSaveInstanceState_native"); - - SaveInstanceLocals locals{ - env, NULL}; // Passed through the user's state prep function. - - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; - if (code->callbacks.onSaveInstanceState != NULL) { - code->callbacks.onSaveInstanceState( - code, - [](const char *bytes, int len, void *context) { - auto locals = static_cast(context); - if (len > 0) { - locals->array = locals->env->NewByteArray(len); - if (locals->array != NULL) { - locals->env->SetByteArrayRegion( - locals->array, 0, len, (const jbyte *)bytes); - } - } - }, - &locals); - } - } - return locals.array; -} - -static void onPause_native(JNIEnv *env, jobject javaGameActivity, - jlong handle) { - LOG_TRACE("onPause_native"); - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; - if (code->callbacks.onPause != NULL) { - code->callbacks.onPause(code); - } - } -} - -static void onStop_native(JNIEnv *env, jobject javaGameActivity, jlong handle) { - LOG_TRACE("onStop_native"); - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; - if (code->callbacks.onStop != NULL) { - code->callbacks.onStop(code); - } - } -} - -static void readConfigurationValues(NativeCode *code, jobject javaConfig) { - if (gConfigurationClassInfo.colorMode != NULL) { - gConfiguration.colorMode = code->env->GetIntField( - javaConfig, gConfigurationClassInfo.colorMode); - } - - gConfiguration.densityDpi = - code->env->GetIntField(javaConfig, gConfigurationClassInfo.densityDpi); - gConfiguration.fontScale = - code->env->GetFloatField(javaConfig, gConfigurationClassInfo.fontScale); - - if (gConfigurationClassInfo.fontWeightAdjustment != NULL) { - gConfiguration.fontWeightAdjustment = code->env->GetIntField( - javaConfig, gConfigurationClassInfo.fontWeightAdjustment); - } - - gConfiguration.hardKeyboardHidden = code->env->GetIntField( - javaConfig, gConfigurationClassInfo.hardKeyboardHidden); - gConfiguration.mcc = - code->env->GetIntField(javaConfig, gConfigurationClassInfo.mcc); - gConfiguration.mnc = - code->env->GetIntField(javaConfig, gConfigurationClassInfo.mnc); - gConfiguration.navigation = - code->env->GetIntField(javaConfig, gConfigurationClassInfo.navigation); - gConfiguration.navigationHidden = code->env->GetIntField( - javaConfig, gConfigurationClassInfo.navigationHidden); - gConfiguration.orientation = - code->env->GetIntField(javaConfig, gConfigurationClassInfo.orientation); - gConfiguration.screenHeightDp = code->env->GetIntField( - javaConfig, gConfigurationClassInfo.screenHeightDp); - gConfiguration.screenLayout = code->env->GetIntField( - javaConfig, gConfigurationClassInfo.screenLayout); - gConfiguration.screenWidthDp = code->env->GetIntField( - javaConfig, gConfigurationClassInfo.screenWidthDp); - gConfiguration.smallestScreenWidthDp = code->env->GetIntField( - javaConfig, gConfigurationClassInfo.smallestScreenWidthDp); - gConfiguration.touchscreen = - code->env->GetIntField(javaConfig, gConfigurationClassInfo.touchscreen); - gConfiguration.uiMode = - code->env->GetIntField(javaConfig, gConfigurationClassInfo.uiMode); - - checkAndClearException(code->env, "Configuration.get"); -} - -static void onConfigurationChanged_native(JNIEnv *env, jobject javaGameActivity, - jlong handle, jobject javaNewConfig) { - LOG_TRACE("onConfigurationChanged_native"); - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; - readConfigurationValues(code, javaNewConfig); - - if (code->callbacks.onConfigurationChanged != NULL) { - code->callbacks.onConfigurationChanged(code); - } - } -} - -static void onTrimMemory_native(JNIEnv *env, jobject javaGameActivity, - jlong handle, jint level) { - LOG_TRACE("onTrimMemory_native"); - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; - if (code->callbacks.onTrimMemory != NULL) { - code->callbacks.onTrimMemory(code, level); - } - } -} - -static void onWindowFocusChanged_native(JNIEnv *env, jobject javaGameActivity, - jlong handle, jboolean focused) { - LOG_TRACE("onWindowFocusChanged_native"); - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; - if (code->callbacks.onWindowFocusChanged != NULL) { - code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0); - } - } -} - -static void onSurfaceCreated_native(JNIEnv *env, jobject javaGameActivity, - jlong handle, jobject surface) { - ALOGV("onSurfaceCreated_native"); - LOG_TRACE("onSurfaceCreated_native"); - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; - code->setSurface(surface); - - if (code->nativeWindow != NULL && - code->callbacks.onNativeWindowCreated != NULL) { - code->callbacks.onNativeWindowCreated(code, code->nativeWindow); - } - } -} - -static void onSurfaceChanged_native(JNIEnv *env, jobject javaGameActivity, - jlong handle, jobject surface, jint format, - jint width, jint height) { - LOG_TRACE("onSurfaceChanged_native"); - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; - ANativeWindow *oldNativeWindow = code->nativeWindow; - // Fix for window being destroyed behind the scenes on older Android - // versions. - if (oldNativeWindow != NULL) { - ANativeWindow_acquire(oldNativeWindow); - } - code->setSurface(surface); - if (oldNativeWindow != code->nativeWindow) { - if (oldNativeWindow != NULL && - code->callbacks.onNativeWindowDestroyed != NULL) { - code->callbacks.onNativeWindowDestroyed(code, oldNativeWindow); - } - if (code->nativeWindow != NULL) { - if (code->callbacks.onNativeWindowCreated != NULL) { - code->callbacks.onNativeWindowCreated(code, - code->nativeWindow); - } - - code->lastWindowWidth = - ANativeWindow_getWidth(code->nativeWindow); - code->lastWindowHeight = - ANativeWindow_getHeight(code->nativeWindow); - } - } else { - // Maybe it was resized? - int32_t newWidth = ANativeWindow_getWidth(code->nativeWindow); - int32_t newHeight = ANativeWindow_getHeight(code->nativeWindow); - - if (newWidth != code->lastWindowWidth || - newHeight != code->lastWindowHeight) { - code->lastWindowWidth = newWidth; - code->lastWindowHeight = newHeight; - - if (code->callbacks.onNativeWindowResized != NULL) { - code->callbacks.onNativeWindowResized( - code, code->nativeWindow, newWidth, newHeight); - } - } - } - // Release the window we acquired earlier. - if (oldNativeWindow != NULL) { - ANativeWindow_release(oldNativeWindow); - } - } -} - -static void onSurfaceRedrawNeeded_native(JNIEnv *env, jobject javaGameActivity, - jlong handle) { - LOG_TRACE("onSurfaceRedrawNeeded_native"); - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; - if (code->nativeWindow != NULL && - code->callbacks.onNativeWindowRedrawNeeded != NULL) { - code->callbacks.onNativeWindowRedrawNeeded(code, - code->nativeWindow); - } - } -} - -static void onSurfaceDestroyed_native(JNIEnv *env, jobject javaGameActivity, - jlong handle) { - LOG_TRACE("onSurfaceDestroyed_native"); - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; - if (code->nativeWindow != NULL && - code->callbacks.onNativeWindowDestroyed != NULL) { - code->callbacks.onNativeWindowDestroyed(code, code->nativeWindow); - } - code->setSurface(NULL); - } -} - -extern "C" void GameActivity_setImeEditorInfo(GameActivity *activity, - int inputType, int actionId, - int imeOptions) { - NativeCode *code = static_cast(activity); - write_work(code->mainWorkWrite, CMD_SET_IME_EDITOR_INFO, inputType, - actionId, imeOptions); -} - -extern "C" int GameActivity_getColorMode(GameActivity *) { - return gConfiguration.colorMode; -} - -extern "C" int GameActivity_getDensityDpi(GameActivity *) { - return gConfiguration.densityDpi; -} - -extern "C" float GameActivity_getFontScale(GameActivity *) { - return gConfiguration.fontScale; -} - -extern "C" int GameActivity_getFontWeightAdjustment(GameActivity *) { - return gConfiguration.fontWeightAdjustment; -} - -extern "C" int GameActivity_getHardKeyboardHidden(GameActivity *) { - return gConfiguration.hardKeyboardHidden; -} - -extern "C" int GameActivity_getKeyboard(GameActivity *) { - return gConfiguration.keyboard; -} - -extern "C" int GameActivity_getKeyboardHidden(GameActivity *) { - return gConfiguration.keyboardHidden; -} - -extern "C" int GameActivity_getMcc(GameActivity *) { - return gConfiguration.mcc; -} - -extern "C" int GameActivity_getMnc(GameActivity *) { - return gConfiguration.mnc; -} - -extern "C" int GameActivity_getNavigation(GameActivity *) { - return gConfiguration.navigation; -} - -extern "C" int GameActivity_getNavigationHidden(GameActivity *) { - return gConfiguration.navigationHidden; -} - -extern "C" int GameActivity_getOrientation(GameActivity *) { - return gConfiguration.orientation; -} - -extern "C" int GameActivity_getScreenHeightDp(GameActivity *) { - return gConfiguration.screenHeightDp; -} - -extern "C" int GameActivity_getScreenLayout(GameActivity *) { - return gConfiguration.screenLayout; -} - -extern "C" int GameActivity_getScreenWidthDp(GameActivity *) { - return gConfiguration.screenWidthDp; -} - -extern "C" int GameActivity_getSmallestScreenWidthDp(GameActivity *) { - return gConfiguration.smallestScreenWidthDp; -} - -extern "C" int GameActivity_getTouchscreen(GameActivity *) { - return gConfiguration.touchscreen; -} - -extern "C" int GameActivity_getUIMode(GameActivity *) { - return gConfiguration.uiMode; -} - -static bool onTouchEvent_native(JNIEnv *env, jobject javaGameActivity, - jlong handle, jobject motionEvent) { - if (handle == 0) return false; - NativeCode *code = (NativeCode *)handle; - if (code->callbacks.onTouchEvent == nullptr) return false; - - static GameActivityMotionEvent c_event; - GameActivityMotionEvent_fromJava(env, motionEvent, &c_event); - return code->callbacks.onTouchEvent(code, &c_event); - -} - -static bool onKeyUp_native(JNIEnv *env, jobject javaGameActivity, jlong handle, - jobject keyEvent) { - if (handle == 0) return false; - NativeCode *code = (NativeCode *)handle; - if (code->callbacks.onKeyUp == nullptr) return false; - - static GameActivityKeyEvent c_event; - GameActivityKeyEvent_fromJava(env, keyEvent, &c_event); - return code->callbacks.onKeyUp(code, &c_event); -} - -static bool onKeyDown_native(JNIEnv *env, jobject javaGameActivity, - jlong handle, jobject keyEvent) { - if (handle == 0) return false; - NativeCode *code = (NativeCode *)handle; - if (code->callbacks.onKeyDown == nullptr) return false; - - static GameActivityKeyEvent c_event; - GameActivityKeyEvent_fromJava(env, keyEvent, &c_event); - return code->callbacks.onKeyDown(code, &c_event); -} - -static void onTextInput_native(JNIEnv *env, jobject activity, jlong handle, - jobject textInputEvent) { - if (handle == 0) return; - NativeCode *code = (NativeCode *)handle; - GameTextInput_processEvent(code->gameTextInput, textInputEvent); -} - -static void onWindowInsetsChanged_native(JNIEnv *env, jobject activity, - jlong handle) { - if (handle == 0) return; - NativeCode *code = (NativeCode *)handle; - if (code->callbacks.onWindowInsetsChanged == nullptr) return; - for (int type = 0; type < GAMECOMMON_INSETS_TYPE_COUNT; ++type) { - jobject jinsets; - // Note that waterfall insets are handled differently on the Java side. - if (type == GAMECOMMON_INSETS_TYPE_WATERFALL) { - jinsets = env->CallObjectMethod( - code->javaGameActivity, - gGameActivityClassInfo.getWaterfallInsets); - } else { - jint jtype = env->CallStaticIntMethod( - gWindowInsetsCompatTypeClassInfo.clazz, - gWindowInsetsCompatTypeClassInfo.methods[type]); - jinsets = env->CallObjectMethod( - code->javaGameActivity, gGameActivityClassInfo.getWindowInsets, - jtype); - } - ARect &insets = code->insetsState[type]; - if (jinsets == nullptr) { - insets.left = 0; - insets.right = 0; - insets.top = 0; - insets.bottom = 0; - } else { - insets.left = env->GetIntField(jinsets, gInsetsClassInfo.left); - insets.right = env->GetIntField(jinsets, gInsetsClassInfo.right); - insets.top = env->GetIntField(jinsets, gInsetsClassInfo.top); - insets.bottom = env->GetIntField(jinsets, gInsetsClassInfo.bottom); - } - } - GameTextInput_processImeInsets( - code->gameTextInput, &code->insetsState[GAMECOMMON_INSETS_TYPE_IME]); - code->callbacks.onWindowInsetsChanged(code); -} - -static void setInputConnection_native(JNIEnv *env, jobject activity, - jlong handle, jobject inputConnection) { - NativeCode *code = (NativeCode *)handle; - GameTextInput_setInputConnection(code->gameTextInput, inputConnection); -} - -static void onContentRectChangedNative_native(JNIEnv *env, jobject activity, - jlong handle, jint x, jint y, - jint w, jint h) { - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; - - if (code->callbacks.onContentRectChanged != nullptr) { - ARect rect; - rect.left = x; - rect.top = y; - rect.right = x+w; - rect.bottom = y+h; - code->callbacks.onContentRectChanged(code, &rect); - } - } -} - -static const JNINativeMethod g_methods[] = { - {"initializeNativeCode", - "(Ljava/lang/String;Ljava/lang/String;" - "Ljava/lang/String;Landroid/content/res/AssetManager;" - "[BLandroid/content/res/Configuration;)J", - (void *)initializeNativeCode_native}, - {"getDlError", "()Ljava/lang/String;", (void *)getDlError_native}, - {"terminateNativeCode", "(J)V", (void *)terminateNativeCode_native}, - {"onStartNative", "(J)V", (void *)onStart_native}, - {"onResumeNative", "(J)V", (void *)onResume_native}, - {"onSaveInstanceStateNative", "(J)[B", (void *)onSaveInstanceState_native}, - {"onPauseNative", "(J)V", (void *)onPause_native}, - {"onStopNative", "(J)V", (void *)onStop_native}, - {"onConfigurationChangedNative", "(JLandroid/content/res/Configuration;)V", - (void *)onConfigurationChanged_native}, - {"onTrimMemoryNative", "(JI)V", (void *)onTrimMemory_native}, - {"onWindowFocusChangedNative", "(JZ)V", - (void *)onWindowFocusChanged_native}, - {"onSurfaceCreatedNative", "(JLandroid/view/Surface;)V", - (void *)onSurfaceCreated_native}, - {"onSurfaceChangedNative", "(JLandroid/view/Surface;III)V", - (void *)onSurfaceChanged_native}, - {"onSurfaceRedrawNeededNative", "(JLandroid/view/Surface;)V", - (void *)onSurfaceRedrawNeeded_native}, - {"onSurfaceDestroyedNative", "(J)V", (void *)onSurfaceDestroyed_native}, - {"onTouchEventNative", "(JLandroid/view/MotionEvent;)Z", - (void *)onTouchEvent_native}, - {"onKeyDownNative", "(JLandroid/view/KeyEvent;)Z", - (void *)onKeyDown_native}, - {"onKeyUpNative", "(JLandroid/view/KeyEvent;)Z", (void *)onKeyUp_native}, - {"onTextInputEventNative", - "(JLcom/google/androidgamesdk/gametextinput/State;)V", - (void *)onTextInput_native}, - {"onWindowInsetsChangedNative", "(J)V", - (void *)onWindowInsetsChanged_native}, - {"setInputConnectionNative", - "(JLcom/google/androidgamesdk/gametextinput/InputConnection;)V", - (void *)setInputConnection_native}, - {"onContentRectChangedNative", "(JIIII)V", - (void *)onContentRectChangedNative_native}, -}; - -static const char *const kGameActivityPathName = - "com/google/androidgamesdk/GameActivity"; - -static const char *const kInsetsPathName = "androidx/core/graphics/Insets"; -static const char *const kConfigurationPathName = - "android/content/res/Configuration"; -static const char *const kWindowInsetsCompatTypePathName = - "androidx/core/view/WindowInsetsCompat$Type"; - -#define FIND_CLASS(var, className) \ - var = env->FindClass(className); \ - LOG_FATAL_IF(!var, "Unable to find class %s", className); - -#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ - var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ - LOG_FATAL_IF(!var, "Unable to find method %s", methodName); - -#define GET_STATIC_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ - var = env->GetStaticMethodID(clazz, methodName, fieldDescriptor); \ - LOG_FATAL_IF(!var, "Unable to find static method %s", methodName); - -#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ - var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ - LOG_FATAL_IF(!var, "Unable to find field %s", fieldName); - -static int jniRegisterNativeMethods(JNIEnv *env, const char *className, - const JNINativeMethod *methods, - int numMethods) { - ALOGV("Registering %s's %d native methods...", className, numMethods); - jclass clazz = env->FindClass(className); - LOG_FATAL_IF(clazz == nullptr, - "Native registration unable to find class '%s'; aborting...", - className); - int result = env->RegisterNatives(clazz, methods, numMethods); - env->DeleteLocalRef(clazz); - if (result == 0) { - return 0; - } - - // Failure to register natives is fatal. Try to report the corresponding - // exception, otherwise abort with generic failure message. - jthrowable thrown = env->ExceptionOccurred(); - if (thrown != NULL) { - env->ExceptionDescribe(); - env->DeleteLocalRef(thrown); - } - LOG_FATAL("RegisterNatives failed for '%s'; aborting...", className); -} - -extern "C" int GameActivity_register(JNIEnv *env) { - ALOGD("GameActivity_register"); - jclass activity_class; - FIND_CLASS(activity_class, kGameActivityPathName); - GET_METHOD_ID(gGameActivityClassInfo.finish, activity_class, "finish", - "()V"); - GET_METHOD_ID(gGameActivityClassInfo.setWindowFlags, activity_class, - "setWindowFlags", "(II)V"); - GET_METHOD_ID(gGameActivityClassInfo.getWindowInsets, activity_class, - "getWindowInsets", "(I)Landroidx/core/graphics/Insets;"); - GET_METHOD_ID(gGameActivityClassInfo.getWaterfallInsets, activity_class, - "getWaterfallInsets", "()Landroidx/core/graphics/Insets;"); - GET_METHOD_ID(gGameActivityClassInfo.setImeEditorInfoFields, activity_class, - "setImeEditorInfoFields", "(III)V"); - - jclass insets_class; - FIND_CLASS(insets_class, kInsetsPathName); - GET_FIELD_ID(gInsetsClassInfo.left, insets_class, "left", "I"); - GET_FIELD_ID(gInsetsClassInfo.right, insets_class, "right", "I"); - GET_FIELD_ID(gInsetsClassInfo.top, insets_class, "top", "I"); - GET_FIELD_ID(gInsetsClassInfo.bottom, insets_class, "bottom", "I"); - - jclass configuration_class; - FIND_CLASS(configuration_class, kConfigurationPathName); - - if (android_get_device_api_level() >= 26) { - GET_FIELD_ID(gConfigurationClassInfo.colorMode, configuration_class, - "colorMode", "I"); - } - - GET_FIELD_ID(gConfigurationClassInfo.densityDpi, configuration_class, - "densityDpi", "I"); - GET_FIELD_ID(gConfigurationClassInfo.fontScale, configuration_class, - "fontScale", "F"); - - if (android_get_device_api_level() >= 31) { - GET_FIELD_ID(gConfigurationClassInfo.fontWeightAdjustment, - configuration_class, "fontWeightAdjustment", "I"); - } - - GET_FIELD_ID(gConfigurationClassInfo.hardKeyboardHidden, - configuration_class, "hardKeyboardHidden", "I"); - GET_FIELD_ID(gConfigurationClassInfo.keyboard, configuration_class, - "keyboard", "I"); - GET_FIELD_ID(gConfigurationClassInfo.keyboardHidden, configuration_class, - "keyboardHidden", "I"); - GET_FIELD_ID(gConfigurationClassInfo.mcc, configuration_class, "mcc", "I"); - GET_FIELD_ID(gConfigurationClassInfo.mnc, configuration_class, "mnc", "I"); - GET_FIELD_ID(gConfigurationClassInfo.navigation, configuration_class, - "navigation", "I"); - GET_FIELD_ID(gConfigurationClassInfo.navigationHidden, configuration_class, - "navigationHidden", "I"); - GET_FIELD_ID(gConfigurationClassInfo.orientation, configuration_class, - "orientation", "I"); - GET_FIELD_ID(gConfigurationClassInfo.screenHeightDp, configuration_class, - "screenHeightDp", "I"); - GET_FIELD_ID(gConfigurationClassInfo.screenLayout, configuration_class, - "screenLayout", "I"); - GET_FIELD_ID(gConfigurationClassInfo.screenWidthDp, configuration_class, - "screenWidthDp", "I"); - GET_FIELD_ID(gConfigurationClassInfo.smallestScreenWidthDp, - configuration_class, "smallestScreenWidthDp", "I"); - GET_FIELD_ID(gConfigurationClassInfo.touchscreen, configuration_class, - "touchscreen", "I"); - GET_FIELD_ID(gConfigurationClassInfo.uiMode, configuration_class, "uiMode", - "I"); - - jclass windowInsetsCompatType_class; - FIND_CLASS(windowInsetsCompatType_class, kWindowInsetsCompatTypePathName); - gWindowInsetsCompatTypeClassInfo.clazz = - (jclass)env->NewGlobalRef(windowInsetsCompatType_class); - // These names must match, in order, the GameCommonInsetsType enum fields - // Note that waterfall is handled differently by the insets API, so we - // exclude it here. - const char *methodNames[GAMECOMMON_INSETS_TYPE_WATERFALL] = { - "captionBar", - "displayCutout", - "ime", - "mandatorySystemGestures", - "navigationBars", - "statusBars", - "systemBars", - "systemGestures", - "tappableElement"}; - for (int i = 0; i < GAMECOMMON_INSETS_TYPE_WATERFALL; ++i) { - GET_STATIC_METHOD_ID(gWindowInsetsCompatTypeClassInfo.methods[i], - windowInsetsCompatType_class, methodNames[i], - "()I"); - } - return jniRegisterNativeMethods(env, kGameActivityPathName, g_methods, - 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_C( - JNIEnv *env, jobject javaGameActivity, jstring internalDataDir, - jstring obbDir, jstring externalDataDir, jobject jAssetMgr, - jbyteArray savedState, jobject javaConfig) { - GameActivity_register(env); - jlong nativeCode = initializeNativeCode_native( - env, javaGameActivity, internalDataDir, obbDir, externalDataDir, - jAssetMgr, savedState, javaConfig); - return nativeCode; -} diff --git a/android-activity/game-activity-csrc/game-activity/GameActivity.h b/android-activity/game-activity-csrc/game-activity/GameActivity.h deleted file mode 100644 index b945069..0000000 --- a/android-activity/game-activity-csrc/game-activity/GameActivity.h +++ /dev/null @@ -1,636 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @addtogroup GameActivity Game Activity - * The interface to use GameActivity. - * @{ - */ - -/** - * @file GameActivity.h - */ - -#ifndef ANDROID_GAME_SDK_GAME_ACTIVITY_H -#define ANDROID_GAME_SDK_GAME_ACTIVITY_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common/gamesdk_common.h" -#include "game-activity/GameActivityEvents.h" -#include "game-text-input/gametextinput.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define GAMEACTIVITY_MAJOR_VERSION 2 -#define GAMEACTIVITY_MINOR_VERSION 0 -#define GAMEACTIVITY_BUGFIX_VERSION 2 -#define GAMEACTIVITY_PACKED_VERSION \ - ANDROID_GAMESDK_PACKED_VERSION(GAMEACTIVITY_MAJOR_VERSION, \ - GAMEACTIVITY_MINOR_VERSION, \ - GAMEACTIVITY_BUGFIX_VERSION) - -/** - * {@link GameActivityCallbacks} - */ -struct GameActivityCallbacks; - -/** - * This structure defines the native side of an android.app.GameActivity. - * It is created by the framework, and handed to the application's native - * code as it is being launched. - */ -typedef struct GameActivity { - /** - * Pointer to the callback function table of the native application. - * You can set the functions here to your own callbacks. The callbacks - * pointer itself here should not be changed; it is allocated and managed - * for you by the framework. - */ - struct GameActivityCallbacks* callbacks; - - /** - * The global handle on the process's Java VM. - */ - JavaVM* vm; - - /** - * JNI context for the main thread of the app. Note that this field - * can ONLY be used from the main thread of the process; that is, the - * thread that calls into the GameActivityCallbacks. - */ - JNIEnv* env; - - /** - * The GameActivity object handle. - */ - jobject javaGameActivity; - - /** - * Path to this application's internal data directory. - */ - const char* internalDataPath; - - /** - * Path to this application's external (removable/mountable) data directory. - */ - const char* externalDataPath; - - /** - * The platform's SDK version code. - */ - int32_t sdkVersion; - - /** - * This is the native instance of the application. It is not used by - * the framework, but can be set by the application to its own instance - * state. - */ - void* instance; - - /** - * Pointer to the Asset Manager instance for the application. The - * application uses this to access binary assets bundled inside its own .apk - * file. - */ - AAssetManager* assetManager; - - /** - * Available starting with Honeycomb: path to the directory containing - * the application's OBB files (if any). If the app doesn't have any - * OBB files, this directory may not exist. - */ - const char* obbPath; -} GameActivity; - -/** - * A function the user should call from their callback with the data, its length - * and the library- supplied context. - */ -typedef void (*SaveInstanceStateRecallback)(const char* bytes, int len, - void* context); - -/** - * These are the callbacks the framework makes into a native application. - * All of these callbacks happen on the main thread of the application. - * By default, all callbacks are NULL; set to a pointer to your own function - * to have it called. - */ -typedef struct GameActivityCallbacks { - /** - * GameActivity has started. See Java documentation for Activity.onStart() - * for more information. - */ - void (*onStart)(GameActivity* activity); - - /** - * GameActivity has resumed. See Java documentation for Activity.onResume() - * for more information. - */ - void (*onResume)(GameActivity* activity); - - /** - * The framework is asking GameActivity to save its current instance state. - * See the Java documentation for Activity.onSaveInstanceState() for more - * information. The user should call the recallback with their data, its - * length and the provided context; they retain ownership of the data. Note - * that the saved state will be persisted, so it can not contain any active - * entities (pointers to memory, file descriptors, etc). - */ - void (*onSaveInstanceState)(GameActivity* activity, - SaveInstanceStateRecallback recallback, - void* context); - - /** - * GameActivity has paused. See Java documentation for Activity.onPause() - * for more information. - */ - void (*onPause)(GameActivity* activity); - - /** - * GameActivity has stopped. See Java documentation for Activity.onStop() - * for more information. - */ - void (*onStop)(GameActivity* activity); - - /** - * GameActivity is being destroyed. See Java documentation for - * Activity.onDestroy() for more information. - */ - void (*onDestroy)(GameActivity* activity); - - /** - * Focus has changed in this GameActivity's window. This is often used, - * for example, to pause a game when it loses input focus. - */ - void (*onWindowFocusChanged)(GameActivity* activity, bool hasFocus); - - /** - * The drawing window for this native activity has been created. You - * can use the given native window object to start drawing. - */ - void (*onNativeWindowCreated)(GameActivity* activity, - ANativeWindow* window); - - /** - * The drawing window for this native activity has been resized. You should - * retrieve the new size from the window and ensure that your rendering in - * it now matches. - */ - void (*onNativeWindowResized)(GameActivity* activity, ANativeWindow* window, - int32_t newWidth, int32_t newHeight); - - /** - * The drawing window for this native activity needs to be redrawn. To - * avoid transient artifacts during screen changes (such resizing after - * rotation), applications should not return from this function until they - * have finished drawing their window in its current state. - */ - void (*onNativeWindowRedrawNeeded)(GameActivity* activity, - ANativeWindow* window); - - /** - * The drawing window for this native activity is going to be destroyed. - * You MUST ensure that you do not touch the window object after returning - * from this function: in the common case of drawing to the window from - * another thread, that means the implementation of this callback must - * properly synchronize with the other thread to stop its drawing before - * returning from here. - */ - void (*onNativeWindowDestroyed)(GameActivity* activity, - ANativeWindow* window); - - /** - * The current device AConfiguration has changed. The new configuration can - * be retrieved from assetManager. - */ - void (*onConfigurationChanged)(GameActivity* activity); - - /** - * The system is running low on memory. Use this callback to release - * resources you do not need, to help the system avoid killing more - * important processes. - */ - void (*onTrimMemory)(GameActivity* activity, int level); - - /** - * Callback called for every MotionEvent done on the GameActivity - * SurfaceView. Ownership of `event` is maintained by the library and it is - * only valid during the callback. - */ - bool (*onTouchEvent)(GameActivity* activity, - const GameActivityMotionEvent* event); - - /** - * Callback called for every key down event on the GameActivity SurfaceView. - * Ownership of `event` is maintained by the library and it is only valid - * during the callback. - */ - bool (*onKeyDown)(GameActivity* activity, - const GameActivityKeyEvent* event); - - /** - * Callback called for every key up event on the GameActivity SurfaceView. - * Ownership of `event` is maintained by the library and it is only valid - * during the callback. - */ - bool (*onKeyUp)(GameActivity* activity, const GameActivityKeyEvent* event); - - /** - * Callback called for every soft-keyboard text input event. - * Ownership of `state` is maintained by the library and it is only valid - * during the callback. - */ - void (*onTextInputEvent)(GameActivity* activity, - const GameTextInputState* state); - - /** - * Callback called when WindowInsets of the main app window have changed. - * Call GameActivity_getWindowInsets to retrieve the insets themselves. - */ - void (*onWindowInsetsChanged)(GameActivity* activity); - - /** - * Callback called when the rectangle in the window where the content - * should be placed has changed. - */ - void (*onContentRectChanged)(GameActivity *activity, const ARect *rect); -} GameActivityCallbacks; - -/** - * This is the function that must be in the native code to instantiate the - * application's native activity. It is called with the activity instance (see - * above); if the code is being instantiated from a previously saved instance, - * the savedState will be non-NULL and point to the saved data. You must make - * any copy of this data you need -- it will be released after you return from - * this function. - */ -typedef void GameActivity_createFunc(GameActivity* activity, void* savedState, - size_t savedStateSize); - -/** - * The name of the function that NativeInstance looks for when launching its - * native code. This is the default function that is used, you can specify - * "android.app.func_name" string meta-data in your manifest to use a different - * function. - */ -extern GameActivity_createFunc GameActivity_onCreate_C; - -/** - * Finish the given activity. Its finish() method will be called, causing it - * to be stopped and destroyed. Note that this method can be called from - * *any* thread; it will send a message to the main thread of the process - * where the Java finish call will take place. - */ -void GameActivity_finish(GameActivity* activity); - -/** - * Flags for GameActivity_setWindowFlags, - * as per the Java API at android.view.WindowManager.LayoutParams. - */ -enum GameActivitySetWindowFlags { - /** - * As long as this window is visible to the user, allow the lock - * screen to activate while the screen is on. This can be used - * independently, or in combination with {@link - * GAMEACTIVITY_FLAG_KEEP_SCREEN_ON} and/or {@link - * GAMEACTIVITY_FLAG_SHOW_WHEN_LOCKED} - */ - GAMEACTIVITY_FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001, - /** Everything behind this window will be dimmed. */ - GAMEACTIVITY_FLAG_DIM_BEHIND = 0x00000002, - /** - * Blur everything behind this window. - * @deprecated Blurring is no longer supported. - */ - GAMEACTIVITY_FLAG_BLUR_BEHIND = 0x00000004, - /** - * This window won't ever get key input focus, so the - * user can not send key or other button events to it. Those will - * instead go to whatever focusable window is behind it. This flag - * will also enable {@link GAMEACTIVITY_FLAG_NOT_TOUCH_MODAL} whether or not - * that is explicitly set. - * - * Setting this flag also implies that the window will not need to - * interact with - * a soft input method, so it will be Z-ordered and positioned - * independently of any active input method (typically this means it - * gets Z-ordered on top of the input method, so it can use the full - * screen for its content and cover the input method if needed. You - * can use {@link GAMEACTIVITY_FLAG_ALT_FOCUSABLE_IM} to modify this - * behavior. - */ - GAMEACTIVITY_FLAG_NOT_FOCUSABLE = 0x00000008, - /** This window can never receive touch events. */ - GAMEACTIVITY_FLAG_NOT_TOUCHABLE = 0x00000010, - /** - * Even when this window is focusable (its - * {@link GAMEACTIVITY_FLAG_NOT_FOCUSABLE} is not set), allow any pointer - * events outside of the window to be sent to the windows behind it. - * Otherwise it will consume all pointer events itself, regardless of - * whether they are inside of the window. - */ - GAMEACTIVITY_FLAG_NOT_TOUCH_MODAL = 0x00000020, - /** - * When set, if the device is asleep when the touch - * screen is pressed, you will receive this first touch event. Usually - * the first touch event is consumed by the system since the user can - * not see what they are pressing on. - * - * @deprecated This flag has no effect. - */ - GAMEACTIVITY_FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040, - /** - * As long as this window is visible to the user, keep - * the device's screen turned on and bright. - */ - GAMEACTIVITY_FLAG_KEEP_SCREEN_ON = 0x00000080, - /** - * Place the window within the entire screen, ignoring - * decorations around the border (such as the status bar). The - * window must correctly position its contents to take the screen - * decoration into account. - */ - GAMEACTIVITY_FLAG_LAYOUT_IN_SCREEN = 0x00000100, - /** Allows the window to extend outside of the screen. */ - GAMEACTIVITY_FLAG_LAYOUT_NO_LIMITS = 0x00000200, - /** - * Hide all screen decorations (such as the status - * bar) while this window is displayed. This allows the window to - * use the entire display space for itself -- the status bar will - * be hidden when an app window with this flag set is on the top - * layer. A fullscreen window will ignore a value of {@link - * GAMEACTIVITY_SOFT_INPUT_ADJUST_RESIZE}; the window will stay - * fullscreen and will not resize. - */ - GAMEACTIVITY_FLAG_FULLSCREEN = 0x00000400, - /** - * Override {@link GAMEACTIVITY_FLAG_FULLSCREEN} and force the - * screen decorations (such as the status bar) to be shown. - */ - GAMEACTIVITY_FLAG_FORCE_NOT_FULLSCREEN = 0x00000800, - /** - * Turn on dithering when compositing this window to - * the screen. - * @deprecated This flag is no longer used. - */ - GAMEACTIVITY_FLAG_DITHER = 0x00001000, - /** - * Treat the content of the window as secure, preventing - * it from appearing in screenshots or from being viewed on non-secure - * displays. - */ - GAMEACTIVITY_FLAG_SECURE = 0x00002000, - /** - * A special mode where the layout parameters are used - * to perform scaling of the surface when it is composited to the - * screen. - */ - GAMEACTIVITY_FLAG_SCALED = 0x00004000, - /** - * Intended for windows that will often be used when the user is - * holding the screen against their face, it will aggressively - * filter the event stream to prevent unintended presses in this - * situation that may not be desired for a particular window, when - * such an event stream is detected, the application will receive - * a {@link AMOTION_EVENT_ACTION_CANCEL} to indicate this so - * applications can handle this accordingly by taking no action on - * the event until the finger is released. - */ - GAMEACTIVITY_FLAG_IGNORE_CHEEK_PRESSES = 0x00008000, - /** - * A special option only for use in combination with - * {@link GAMEACTIVITY_FLAG_LAYOUT_IN_SCREEN}. When requesting layout in - * the screen your window may appear on top of or behind screen decorations - * such as the status bar. By also including this flag, the window - * manager will report the inset rectangle needed to ensure your - * content is not covered by screen decorations. - */ - GAMEACTIVITY_FLAG_LAYOUT_INSET_DECOR = 0x00010000, - /** - * Invert the state of {@link GAMEACTIVITY_FLAG_NOT_FOCUSABLE} with - * respect to how this window interacts with the current method. - * That is, if FLAG_NOT_FOCUSABLE is set and this flag is set, - * then the window will behave as if it needs to interact with the - * input method and thus be placed behind/away from it; if {@link - * GAMEACTIVITY_FLAG_NOT_FOCUSABLE} is not set and this flag is set, - * then the window will behave as if it doesn't need to interact - * with the input method and can be placed to use more space and - * cover the input method. - */ - GAMEACTIVITY_FLAG_ALT_FOCUSABLE_IM = 0x00020000, - /** - * If you have set {@link GAMEACTIVITY_FLAG_NOT_TOUCH_MODAL}, you - * can set this flag to receive a single special MotionEvent with - * the action - * {@link AMOTION_EVENT_ACTION_OUTSIDE} for - * touches that occur outside of your window. Note that you will not - * receive the full down/move/up gesture, only the location of the - * first down as an {@link AMOTION_EVENT_ACTION_OUTSIDE}. - */ - GAMEACTIVITY_FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000, - /** - * Special flag to let windows be shown when the screen - * is locked. This will let application windows take precedence over - * key guard or any other lock screens. Can be used with - * {@link GAMEACTIVITY_FLAG_KEEP_SCREEN_ON} to turn screen on and display - * windows directly before showing the key guard window. Can be used with - * {@link GAMEACTIVITY_FLAG_DISMISS_KEYGUARD} to automatically fully - * dismisss non-secure keyguards. This flag only applies to the top-most - * full-screen window. - */ - GAMEACTIVITY_FLAG_SHOW_WHEN_LOCKED = 0x00080000, - /** - * Ask that the system wallpaper be shown behind - * your window. The window surface must be translucent to be able - * to actually see the wallpaper behind it; this flag just ensures - * that the wallpaper surface will be there if this window actually - * has translucent regions. - */ - GAMEACTIVITY_FLAG_SHOW_WALLPAPER = 0x00100000, - /** - * When set as a window is being added or made - * visible, once the window has been shown then the system will - * poke the power manager's user activity (as if the user had woken - * up the device) to turn the screen on. - */ - GAMEACTIVITY_FLAG_TURN_SCREEN_ON = 0x00200000, - /** - * When set the window will cause the keyguard to - * be dismissed, only if it is not a secure lock keyguard. Because such - * a keyguard is not needed for security, it will never re-appear if - * the user navigates to another window (in contrast to - * {@link GAMEACTIVITY_FLAG_SHOW_WHEN_LOCKED}, which will only temporarily - * hide both secure and non-secure keyguards but ensure they reappear - * when the user moves to another UI that doesn't hide them). - * If the keyguard is currently active and is secure (requires an - * unlock pattern) than the user will still need to confirm it before - * seeing this window, unless {@link GAMEACTIVITY_FLAG_SHOW_WHEN_LOCKED} has - * also been set. - */ - GAMEACTIVITY_FLAG_DISMISS_KEYGUARD = 0x00400000, -}; - -/** - * Change the window flags of the given activity. Calls getWindow().setFlags() - * of the given activity. - * Note that some flags must be set before the window decoration is created, - * see - * https://developer.android.com/reference/android/view/Window#setFlags(int,%20int). - * Note also that this method can be called from - * *any* thread; it will send a message to the main thread of the process - * where the Java finish call will take place. - */ -void GameActivity_setWindowFlags(GameActivity* activity, uint32_t addFlags, - uint32_t removeFlags); - -/** - * Flags for GameActivity_showSoftInput; see the Java InputMethodManager - * API for documentation. - */ -enum GameActivityShowSoftInputFlags { - /** - * Implicit request to show the input window, not as the result - * of a direct request by the user. - */ - GAMEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT = 0x0001, - - /** - * The user has forced the input method open (such as by - * long-pressing menu) so it should not be closed until they - * explicitly do so. - */ - GAMEACTIVITY_SHOW_SOFT_INPUT_FORCED = 0x0002, -}; - -/** - * Show the IME while in the given activity. Calls - * InputMethodManager.showSoftInput() for the given activity. Note that this - * method can be called from *any* thread; it will send a message to the main - * thread of the process where the Java call will take place. - */ -void GameActivity_showSoftInput(GameActivity* activity, uint32_t flags); - -/** - * Set the text entry state (see documentation of the GameTextInputState struct - * in the Game Text Input library reference). - * - * Ownership of the state is maintained by the caller. - */ -void GameActivity_setTextInputState(GameActivity* activity, - const GameTextInputState* state); - -/** - * Get the last-received text entry state (see documentation of the - * GameTextInputState struct in the Game Text Input library reference). - * - */ -void GameActivity_getTextInputState(GameActivity* activity, - GameTextInputGetStateCallback callback, - void* context); - -/** - * Get a pointer to the GameTextInput library instance. - */ -GameTextInput* GameActivity_getTextInput(const GameActivity* activity); - -/** - * Flags for GameActivity_hideSoftInput; see the Java InputMethodManager - * API for documentation. - */ -enum GameActivityHideSoftInputFlags { - /** - * The soft input window should only be hidden if it was not - * explicitly shown by the user. - */ - GAMEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY = 0x0001, - /** - * The soft input window should normally be hidden, unless it was - * originally shown with {@link GAMEACTIVITY_SHOW_SOFT_INPUT_FORCED}. - */ - GAMEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS = 0x0002, -}; - -/** - * Hide the IME while in the given activity. Calls - * InputMethodManager.hideSoftInput() for the given activity. Note that this - * method can be called from *any* thread; it will send a message to the main - * thread of the process where the Java finish call will take place. - */ -void GameActivity_hideSoftInput(GameActivity* activity, uint32_t flags); - -/** - * Get the current window insets of the particular component. See - * https://developer.android.com/reference/androidx/core/view/WindowInsetsCompat.Type - * for more details. - * You can use these insets to influence what you show on the screen. - */ -void GameActivity_getWindowInsets(GameActivity* activity, - GameCommonInsetsType type, ARect* insets); - -/** - * Set options on how the IME behaves when it is requested for text input. - * See - * https://developer.android.com/reference/android/view/inputmethod/EditorInfo - * for the meaning of inputType, actionId and imeOptions. - * - * Note that this function will attach the current thread to the JVM if it is - * not already attached, so the caller must detach the thread from the JVM - * before the thread is destroyed using DetachCurrentThread. - */ -void GameActivity_setImeEditorInfo(GameActivity* activity, int inputType, - int actionId, int imeOptions); - -/** - * These are getters for Configuration class members. They may be called from - * any thread. - */ -int GameActivity_getOrientation(GameActivity* activity); -int GameActivity_getColorMode(GameActivity* activity); -int GameActivity_getDensityDpi(GameActivity* activity); -float GameActivity_getFontScale(GameActivity* activity); -int GameActivity_getFontWeightAdjustment(GameActivity* activity); -int GameActivity_getHardKeyboardHidden(GameActivity* activity); -int GameActivity_getKeyboard(GameActivity* activity); -int GameActivity_getKeyboardHidden(GameActivity* activity); -int GameActivity_getMcc(GameActivity* activity); -int GameActivity_getMnc(GameActivity* activity); -int GameActivity_getNavigation(GameActivity* activity); -int GameActivity_getNavigationHidden(GameActivity* activity); -int GameActivity_getOrientation(GameActivity* activity); -int GameActivity_getScreenHeightDp(GameActivity* activity); -int GameActivity_getScreenLayout(GameActivity* activity); -int GameActivity_getScreenWidthDp(GameActivity* activity); -int GameActivity_getSmallestScreenWidthDp(GameActivity* activity); -int GameActivity_getTouchscreen(GameActivity* activity); -int GameActivity_getUIMode(GameActivity* activity); - -#ifdef __cplusplus -} -#endif - -/** @} */ - -#endif // ANDROID_GAME_SDK_GAME_ACTIVITY_H diff --git a/android-activity/game-activity-csrc/game-activity/GameActivityEvents.cpp b/android-activity/game-activity-csrc/game-activity/GameActivityEvents.cpp deleted file mode 100644 index 028b55e..0000000 --- a/android-activity/game-activity-csrc/game-activity/GameActivityEvents.cpp +++ /dev/null @@ -1,414 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "GameActivityEvents.h" - -#include - -#include - -#include "GameActivityLog.h" - -// TODO(b/187147166): these functions were extracted from the Game SDK -// (gamesdk/src/common/system_utils.h). system_utils.h/cpp should be used -// instead. -namespace { - -std::string getSystemPropViaGet(const char *key, - const char *default_value = "") { - char buffer[PROP_VALUE_MAX + 1] = ""; // +1 for terminator - int bufferLen = __system_property_get(key, buffer); - if (bufferLen > 0) - return buffer; - else - return ""; -} - -std::string GetSystemProp(const char *key, const char *default_value = "") { - return getSystemPropViaGet(key, default_value); -} - -int GetSystemPropAsInt(const char *key, int default_value = 0) { - std::string prop = GetSystemProp(key); - return prop == "" ? default_value : strtoll(prop.c_str(), nullptr, 10); -} - -} // anonymous namespace - -#ifndef NELEM -#define NELEM(x) ((int)(sizeof(x) / sizeof((x)[0]))) -#endif - -static bool enabledAxes[GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT] = { - /* AMOTION_EVENT_AXIS_X */ true, - /* AMOTION_EVENT_AXIS_Y */ true, - // Disable all other axes by default (they can be enabled using - // `GameActivityPointerAxes_enableAxis`). - false}; - -extern "C" void GameActivityPointerAxes_enableAxis(int32_t axis) { - if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) { - return; - } - - enabledAxes[axis] = true; -} - -float GameActivityPointerAxes_getAxisValue( - const GameActivityPointerAxes *pointerInfo, int32_t axis) { - if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) { - return 0; - } - - if (!enabledAxes[axis]) { - ALOGW("Axis %d must be enabled before it can be accessed.", axis); - return 0; - } - - return pointerInfo->axisValues[axis]; -} - -extern "C" void GameActivityPointerAxes_disableAxis(int32_t axis) { - if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) { - return; - } - - enabledAxes[axis] = false; -} - -float GameActivityMotionEvent_getHistoricalAxisValue( - const GameActivityMotionEvent *event, int axis, int pointerIndex, - int historyPos) { - if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) { - ALOGE("Invalid axis %d", axis); - return -1; - } - if (pointerIndex < 0 || pointerIndex >= event->pointerCount) { - ALOGE("Invalid pointer index %d", pointerIndex); - return -1; - } - if (historyPos < 0 || historyPos >= event->historySize) { - ALOGE("Invalid history index %d", historyPos); - return -1; - } - if (!enabledAxes[axis]) { - ALOGW("Axis %d must be enabled before it can be accessed.", axis); - return 0; - } - - int pointerOffset = pointerIndex * GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT; - int historyValuesOffset = historyPos * event->pointerCount * - GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT; - return event - ->historicalAxisValues[historyValuesOffset + pointerOffset + axis]; -} - -static struct { - jmethodID getDeviceId; - jmethodID getSource; - jmethodID getAction; - - jmethodID getEventTime; - jmethodID getDownTime; - - jmethodID getFlags; - jmethodID getMetaState; - - jmethodID getActionButton; - jmethodID getButtonState; - jmethodID getClassification; - jmethodID getEdgeFlags; - - jmethodID getHistorySize; - jmethodID getHistoricalEventTime; - - jmethodID getPointerCount; - jmethodID getPointerId; - - jmethodID getToolType; - - jmethodID getRawX; - jmethodID getRawY; - jmethodID getXPrecision; - jmethodID getYPrecision; - jmethodID getAxisValue; - - jmethodID getHistoricalAxisValue; -} gMotionEventClassInfo; - -extern "C" void GameActivityMotionEvent_destroy( - GameActivityMotionEvent *c_event) { - delete c_event->historicalAxisValues; - delete c_event->historicalEventTimesMillis; - delete c_event->historicalEventTimesNanos; -} - -extern "C" void GameActivityMotionEvent_fromJava( - JNIEnv *env, jobject motionEvent, GameActivityMotionEvent *out_event) { - static bool gMotionEventClassInfoInitialized = false; - if (!gMotionEventClassInfoInitialized) { - int sdkVersion = GetSystemPropAsInt("ro.build.version.sdk"); - gMotionEventClassInfo = {0}; - jclass motionEventClass = env->FindClass("android/view/MotionEvent"); - gMotionEventClassInfo.getDeviceId = - env->GetMethodID(motionEventClass, "getDeviceId", "()I"); - gMotionEventClassInfo.getSource = - env->GetMethodID(motionEventClass, "getSource", "()I"); - gMotionEventClassInfo.getAction = - env->GetMethodID(motionEventClass, "getAction", "()I"); - gMotionEventClassInfo.getEventTime = - env->GetMethodID(motionEventClass, "getEventTime", "()J"); - gMotionEventClassInfo.getDownTime = - env->GetMethodID(motionEventClass, "getDownTime", "()J"); - gMotionEventClassInfo.getFlags = - env->GetMethodID(motionEventClass, "getFlags", "()I"); - gMotionEventClassInfo.getMetaState = - env->GetMethodID(motionEventClass, "getMetaState", "()I"); - if (sdkVersion >= 23) { - gMotionEventClassInfo.getActionButton = - env->GetMethodID(motionEventClass, "getActionButton", "()I"); - } - if (sdkVersion >= 14) { - gMotionEventClassInfo.getButtonState = - env->GetMethodID(motionEventClass, "getButtonState", "()I"); - } - if (sdkVersion >= 29) { - gMotionEventClassInfo.getClassification = - env->GetMethodID(motionEventClass, "getClassification", "()I"); - } - gMotionEventClassInfo.getEdgeFlags = - env->GetMethodID(motionEventClass, "getEdgeFlags", "()I"); - - gMotionEventClassInfo.getHistorySize = - env->GetMethodID(motionEventClass, "getHistorySize", "()I"); - gMotionEventClassInfo.getHistoricalEventTime = env->GetMethodID( - motionEventClass, "getHistoricalEventTime", "(I)J"); - - gMotionEventClassInfo.getPointerCount = - env->GetMethodID(motionEventClass, "getPointerCount", "()I"); - gMotionEventClassInfo.getPointerId = - env->GetMethodID(motionEventClass, "getPointerId", "(I)I"); - gMotionEventClassInfo.getToolType = - env->GetMethodID(motionEventClass, "getToolType", "(I)I"); - if (sdkVersion >= 29) { - gMotionEventClassInfo.getRawX = - env->GetMethodID(motionEventClass, "getRawX", "(I)F"); - gMotionEventClassInfo.getRawY = - env->GetMethodID(motionEventClass, "getRawY", "(I)F"); - } - gMotionEventClassInfo.getXPrecision = - env->GetMethodID(motionEventClass, "getXPrecision", "()F"); - gMotionEventClassInfo.getYPrecision = - env->GetMethodID(motionEventClass, "getYPrecision", "()F"); - gMotionEventClassInfo.getAxisValue = - env->GetMethodID(motionEventClass, "getAxisValue", "(II)F"); - - gMotionEventClassInfo.getHistoricalAxisValue = env->GetMethodID( - motionEventClass, "getHistoricalAxisValue", "(III)F"); - gMotionEventClassInfoInitialized = true; - } - - int pointerCount = - env->CallIntMethod(motionEvent, gMotionEventClassInfo.getPointerCount); - pointerCount = - std::min(pointerCount, GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT); - out_event->pointerCount = pointerCount; - for (int i = 0; i < pointerCount; ++i) { - out_event->pointers[i] = { - /*id=*/env->CallIntMethod(motionEvent, - gMotionEventClassInfo.getPointerId, i), - /*toolType=*/ - env->CallIntMethod(motionEvent, gMotionEventClassInfo.getToolType, - i), - /*axisValues=*/{0}, - /*rawX=*/gMotionEventClassInfo.getRawX - ? env->CallFloatMethod(motionEvent, - gMotionEventClassInfo.getRawX, i) - : 0, - /*rawY=*/gMotionEventClassInfo.getRawY - ? env->CallFloatMethod(motionEvent, - gMotionEventClassInfo.getRawY, i) - : 0, - }; - - for (int axisIndex = 0; - axisIndex < GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT; ++axisIndex) { - if (enabledAxes[axisIndex]) { - out_event->pointers[i].axisValues[axisIndex] = - env->CallFloatMethod(motionEvent, - gMotionEventClassInfo.getAxisValue, - axisIndex, i); - } - } - } - - int historySize = - env->CallIntMethod(motionEvent, gMotionEventClassInfo.getHistorySize); - out_event->historySize = historySize; - out_event->historicalAxisValues = - new float[historySize * pointerCount * - GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT]; - out_event->historicalEventTimesMillis = new int64_t[historySize]; - out_event->historicalEventTimesNanos = new int64_t[historySize]; - - for (int historyIndex = 0; historyIndex < historySize; historyIndex++) { - out_event->historicalEventTimesMillis[historyIndex] = - env->CallLongMethod(motionEvent, - gMotionEventClassInfo.getHistoricalEventTime, - historyIndex); - out_event->historicalEventTimesNanos[historyIndex] = - out_event->historicalEventTimesMillis[historyIndex] * 1000000; - for (int i = 0; i < pointerCount; ++i) { - int pointerOffset = i * GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT; - int historyAxisOffset = historyIndex * pointerCount * - GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT; - float *axisValues = - &out_event - ->historicalAxisValues[historyAxisOffset + pointerOffset]; - for (int axisIndex = 0; - axisIndex < GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT; - ++axisIndex) { - if (enabledAxes[axisIndex]) { - axisValues[axisIndex] = env->CallFloatMethod( - motionEvent, - gMotionEventClassInfo.getHistoricalAxisValue, axisIndex, - i, historyIndex); - } - } - } - } - - out_event->deviceId = - env->CallIntMethod(motionEvent, gMotionEventClassInfo.getDeviceId); - out_event->source = - env->CallIntMethod(motionEvent, gMotionEventClassInfo.getSource); - out_event->action = - env->CallIntMethod(motionEvent, gMotionEventClassInfo.getAction); - out_event->eventTime = - env->CallLongMethod(motionEvent, gMotionEventClassInfo.getEventTime) * - 1000000; - out_event->downTime = - env->CallLongMethod(motionEvent, gMotionEventClassInfo.getDownTime) * - 1000000; - out_event->flags = - env->CallIntMethod(motionEvent, gMotionEventClassInfo.getFlags); - out_event->metaState = - env->CallIntMethod(motionEvent, gMotionEventClassInfo.getMetaState); - out_event->actionButton = - gMotionEventClassInfo.getActionButton - ? env->CallIntMethod(motionEvent, - gMotionEventClassInfo.getActionButton) - : 0; - out_event->buttonState = - gMotionEventClassInfo.getButtonState - ? env->CallIntMethod(motionEvent, - gMotionEventClassInfo.getButtonState) - : 0; - out_event->classification = - gMotionEventClassInfo.getClassification - ? env->CallIntMethod(motionEvent, - gMotionEventClassInfo.getClassification) - : 0; - out_event->edgeFlags = - env->CallIntMethod(motionEvent, gMotionEventClassInfo.getEdgeFlags); - out_event->precisionX = - env->CallFloatMethod(motionEvent, gMotionEventClassInfo.getXPrecision); - out_event->precisionY = - env->CallFloatMethod(motionEvent, gMotionEventClassInfo.getYPrecision); -} - -static struct { - jmethodID getDeviceId; - jmethodID getSource; - jmethodID getAction; - - jmethodID getEventTime; - jmethodID getDownTime; - - jmethodID getFlags; - jmethodID getMetaState; - - jmethodID getModifiers; - jmethodID getRepeatCount; - jmethodID getKeyCode; - jmethodID getScanCode; - //jmethodID getUnicodeChar; -} gKeyEventClassInfo; - -extern "C" void GameActivityKeyEvent_fromJava(JNIEnv *env, jobject keyEvent, - GameActivityKeyEvent *out_event) { - static bool gKeyEventClassInfoInitialized = false; - if (!gKeyEventClassInfoInitialized) { - int sdkVersion = GetSystemPropAsInt("ro.build.version.sdk"); - gKeyEventClassInfo = {0}; - jclass keyEventClass = env->FindClass("android/view/KeyEvent"); - gKeyEventClassInfo.getDeviceId = - env->GetMethodID(keyEventClass, "getDeviceId", "()I"); - gKeyEventClassInfo.getSource = - env->GetMethodID(keyEventClass, "getSource", "()I"); - gKeyEventClassInfo.getAction = - env->GetMethodID(keyEventClass, "getAction", "()I"); - gKeyEventClassInfo.getEventTime = - env->GetMethodID(keyEventClass, "getEventTime", "()J"); - gKeyEventClassInfo.getDownTime = - env->GetMethodID(keyEventClass, "getDownTime", "()J"); - gKeyEventClassInfo.getFlags = - env->GetMethodID(keyEventClass, "getFlags", "()I"); - gKeyEventClassInfo.getMetaState = - env->GetMethodID(keyEventClass, "getMetaState", "()I"); - if (sdkVersion >= 13) { - gKeyEventClassInfo.getModifiers = - env->GetMethodID(keyEventClass, "getModifiers", "()I"); - } - gKeyEventClassInfo.getRepeatCount = - env->GetMethodID(keyEventClass, "getRepeatCount", "()I"); - gKeyEventClassInfo.getKeyCode = - env->GetMethodID(keyEventClass, "getKeyCode", "()I"); - gKeyEventClassInfo.getScanCode = - env->GetMethodID(keyEventClass, "getScanCode", "()I"); - //gKeyEventClassInfo.getUnicodeChar = - // env->GetMethodID(keyEventClass, "getUnicodeChar", "()I"); - - gKeyEventClassInfoInitialized = true; - } - - *out_event = { - /*deviceId=*/env->CallIntMethod(keyEvent, - gKeyEventClassInfo.getDeviceId), - /*source=*/env->CallIntMethod(keyEvent, gKeyEventClassInfo.getSource), - /*action=*/env->CallIntMethod(keyEvent, gKeyEventClassInfo.getAction), - // TODO: introduce a millisecondsToNanoseconds helper: - /*eventTime=*/ - env->CallLongMethod(keyEvent, gKeyEventClassInfo.getEventTime) * - 1000000, - /*downTime=*/ - env->CallLongMethod(keyEvent, gKeyEventClassInfo.getDownTime) * 1000000, - /*flags=*/env->CallIntMethod(keyEvent, gKeyEventClassInfo.getFlags), - /*metaState=*/ - env->CallIntMethod(keyEvent, gKeyEventClassInfo.getMetaState), - /*modifiers=*/gKeyEventClassInfo.getModifiers - ? env->CallIntMethod(keyEvent, gKeyEventClassInfo.getModifiers) - : 0, - /*repeatCount=*/ - env->CallIntMethod(keyEvent, gKeyEventClassInfo.getRepeatCount), - /*keyCode=*/ - env->CallIntMethod(keyEvent, gKeyEventClassInfo.getKeyCode), - /*scanCode=*/ - env->CallIntMethod(keyEvent, gKeyEventClassInfo.getScanCode) - /*unicodeChar=*/ - //env->CallIntMethod(keyEvent, gKeyEventClassInfo.getUnicodeChar) - }; -} diff --git a/android-activity/game-activity-csrc/game-activity/GameActivityEvents.h b/android-activity/game-activity-csrc/game-activity/GameActivityEvents.h deleted file mode 100644 index 015ffbe..0000000 --- a/android-activity/game-activity-csrc/game-activity/GameActivityEvents.h +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @addtogroup GameActivity Game Activity Events - * The interface to use Game Activity Events. - * @{ - */ - -/** - * @file GameActivityEvents.h - */ -#ifndef ANDROID_GAME_SDK_GAME_ACTIVITY_EVENTS_H -#define ANDROID_GAME_SDK_GAME_ACTIVITY_EVENTS_H - -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * The maximum number of axes supported in an Android MotionEvent. - * See https://developer.android.com/ndk/reference/group/input. - */ -#define GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT 48 - -/** - * \brief Describe information about a pointer, found in a - * GameActivityMotionEvent. - * - * You can read values directly from this structure, or use helper functions - * (`GameActivityPointerAxes_getX`, `GameActivityPointerAxes_getY` and - * `GameActivityPointerAxes_getAxisValue`). - * - * The X axis and Y axis are enabled by default but any other axis that you want - * to read **must** be enabled first, using - * `GameActivityPointerAxes_enableAxis`. - * - * \see GameActivityMotionEvent - */ -typedef struct GameActivityPointerAxes { - int32_t id; - int32_t toolType; - float axisValues[GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT]; - float rawX; - float rawY; -} GameActivityPointerAxes; - -/** \brief Get the toolType of the pointer. */ -inline int32_t GameActivityPointerAxes_getToolType( - const GameActivityPointerAxes* pointerInfo) { - return pointerInfo->toolType; -} - -/** \brief Get the current X coordinate of the pointer. */ -inline float GameActivityPointerAxes_getX( - const GameActivityPointerAxes* pointerInfo) { - return pointerInfo->axisValues[AMOTION_EVENT_AXIS_X]; -} - -/** \brief Get the current Y coordinate of the pointer. */ -inline float GameActivityPointerAxes_getY( - const GameActivityPointerAxes* pointerInfo) { - return pointerInfo->axisValues[AMOTION_EVENT_AXIS_Y]; -} - -/** - * \brief Enable the specified axis, so that its value is reported in the - * GameActivityPointerAxes structures stored in a motion event. - * - * You must enable any axis that you want to read, apart from - * `AMOTION_EVENT_AXIS_X` and `AMOTION_EVENT_AXIS_Y` that are enabled by - * default. - * - * If the axis index is out of range, nothing is done. - */ -void GameActivityPointerAxes_enableAxis(int32_t axis); - -/** - * \brief Disable the specified axis. Its value won't be reported in the - * GameActivityPointerAxes structures stored in a motion event anymore. - * - * Apart from X and Y, any axis that you want to read **must** be enabled first, - * using `GameActivityPointerAxes_enableAxis`. - * - * If the axis index is out of range, nothing is done. - */ -void GameActivityPointerAxes_disableAxis(int32_t axis); - -/** - * \brief Get the value of the requested axis. - * - * Apart from X and Y, any axis that you want to read **must** be enabled first, - * using `GameActivityPointerAxes_enableAxis`. - * - * Find the valid enums for the axis (`AMOTION_EVENT_AXIS_X`, - * `AMOTION_EVENT_AXIS_Y`, `AMOTION_EVENT_AXIS_PRESSURE`...) - * in https://developer.android.com/ndk/reference/group/input. - * - * @param pointerInfo The structure containing information about the pointer, - * obtained from GameActivityMotionEvent. - * @param axis The axis to get the value from - * @return The value of the axis, or 0 if the axis is invalid or was not - * enabled. - */ -float GameActivityPointerAxes_getAxisValue( - const GameActivityPointerAxes* pointerInfo, int32_t axis); - -inline float GameActivityPointerAxes_getPressure( - const GameActivityPointerAxes* pointerInfo) { - return GameActivityPointerAxes_getAxisValue(pointerInfo, - AMOTION_EVENT_AXIS_PRESSURE); -} - -inline float GameActivityPointerAxes_getSize( - const GameActivityPointerAxes* pointerInfo) { - return GameActivityPointerAxes_getAxisValue(pointerInfo, - AMOTION_EVENT_AXIS_SIZE); -} - -inline float GameActivityPointerAxes_getTouchMajor( - const GameActivityPointerAxes* pointerInfo) { - return GameActivityPointerAxes_getAxisValue(pointerInfo, - AMOTION_EVENT_AXIS_TOUCH_MAJOR); -} - -inline float GameActivityPointerAxes_getTouchMinor( - const GameActivityPointerAxes* pointerInfo) { - return GameActivityPointerAxes_getAxisValue(pointerInfo, - AMOTION_EVENT_AXIS_TOUCH_MINOR); -} - -inline float GameActivityPointerAxes_getToolMajor( - const GameActivityPointerAxes* pointerInfo) { - return GameActivityPointerAxes_getAxisValue(pointerInfo, - AMOTION_EVENT_AXIS_TOOL_MAJOR); -} - -inline float GameActivityPointerAxes_getToolMinor( - const GameActivityPointerAxes* pointerInfo) { - return GameActivityPointerAxes_getAxisValue(pointerInfo, - AMOTION_EVENT_AXIS_TOOL_MINOR); -} - -inline float GameActivityPointerAxes_getOrientation( - const GameActivityPointerAxes* pointerInfo) { - return GameActivityPointerAxes_getAxisValue(pointerInfo, - AMOTION_EVENT_AXIS_ORIENTATION); -} - -/** - * The maximum number of pointers returned inside a motion event. - */ -#if (defined GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT_OVERRIDE) -#define GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT \ - GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT_OVERRIDE -#else -#define GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT 8 -#endif - -/** - * \brief Describe a motion event that happened on the GameActivity SurfaceView. - * - * This is 1:1 mapping to the information contained in a Java `MotionEvent` - * (see https://developer.android.com/reference/android/view/MotionEvent). - */ -typedef struct GameActivityMotionEvent { - int32_t deviceId; - int32_t source; - int32_t action; - - int64_t eventTime; - int64_t downTime; - - int32_t flags; - int32_t metaState; - - int32_t actionButton; - int32_t buttonState; - int32_t classification; - int32_t edgeFlags; - - uint32_t pointerCount; - GameActivityPointerAxes - pointers[GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT]; - - int historySize; - int64_t* historicalEventTimesMillis; - int64_t* historicalEventTimesNanos; - float* historicalAxisValues; - - float precisionX; - float precisionY; -} GameActivityMotionEvent; - -float GameActivityMotionEvent_getHistoricalAxisValue( - const GameActivityMotionEvent* event, int axis, int pointerIndex, - int historyPos); - -inline int GameActivityMotionEvent_getHistorySize( - const GameActivityMotionEvent* event) { - return event->historySize; -} - -inline float GameActivityMotionEvent_getHistoricalX( - const GameActivityMotionEvent* event, int pointerIndex, int historyPos) { - return GameActivityMotionEvent_getHistoricalAxisValue( - event, AMOTION_EVENT_AXIS_X, pointerIndex, historyPos); -} - -inline float GameActivityMotionEvent_getHistoricalY( - const GameActivityMotionEvent* event, int pointerIndex, int historyPos) { - return GameActivityMotionEvent_getHistoricalAxisValue( - event, AMOTION_EVENT_AXIS_Y, pointerIndex, historyPos); -} - -inline float GameActivityMotionEvent_getHistoricalPressure( - const GameActivityMotionEvent* event, int pointerIndex, int historyPos) { - return GameActivityMotionEvent_getHistoricalAxisValue( - event, AMOTION_EVENT_AXIS_PRESSURE, pointerIndex, historyPos); -} - -inline float GameActivityMotionEvent_getHistoricalSize( - const GameActivityMotionEvent* event, int pointerIndex, int historyPos) { - return GameActivityMotionEvent_getHistoricalAxisValue( - event, AMOTION_EVENT_AXIS_SIZE, pointerIndex, historyPos); -} - -inline float GameActivityMotionEvent_getHistoricalTouchMajor( - const GameActivityMotionEvent* event, int pointerIndex, int historyPos) { - return GameActivityMotionEvent_getHistoricalAxisValue( - event, AMOTION_EVENT_AXIS_TOUCH_MAJOR, pointerIndex, historyPos); -} - -inline float GameActivityMotionEvent_getHistoricalTouchMinor( - const GameActivityMotionEvent* event, int pointerIndex, int historyPos) { - return GameActivityMotionEvent_getHistoricalAxisValue( - event, AMOTION_EVENT_AXIS_TOUCH_MINOR, pointerIndex, historyPos); -} - -inline float GameActivityMotionEvent_getHistoricalToolMajor( - const GameActivityMotionEvent* event, int pointerIndex, int historyPos) { - return GameActivityMotionEvent_getHistoricalAxisValue( - event, AMOTION_EVENT_AXIS_TOOL_MAJOR, pointerIndex, historyPos); -} - -inline float GameActivityMotionEvent_getHistoricalToolMinor( - const GameActivityMotionEvent* event, int pointerIndex, int historyPos) { - return GameActivityMotionEvent_getHistoricalAxisValue( - event, AMOTION_EVENT_AXIS_TOOL_MINOR, pointerIndex, historyPos); -} - -inline float GameActivityMotionEvent_getHistoricalOrientation( - const GameActivityMotionEvent* event, int pointerIndex, int historyPos) { - return GameActivityMotionEvent_getHistoricalAxisValue( - event, AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historyPos); -} - -/** \brief Handle the freeing of the GameActivityMotionEvent struct. */ -void GameActivityMotionEvent_destroy(GameActivityMotionEvent* c_event); - -/** - * \brief Convert a Java `MotionEvent` to a `GameActivityMotionEvent`. - * - * 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. - * Ownership of out_event is maintained by the caller. - */ -void GameActivityMotionEvent_fromJava(JNIEnv* env, jobject motionEvent, - GameActivityMotionEvent* out_event); - -/** - * \brief Describe a key event that happened on the GameActivity SurfaceView. - * - * This is 1:1 mapping to the information contained in a Java `KeyEvent` - * (see https://developer.android.com/reference/android/view/KeyEvent). - * The only exception is the event times, which are reported as - * nanoseconds in this struct. - */ -typedef struct GameActivityKeyEvent { - int32_t deviceId; - int32_t source; - int32_t action; - - int64_t eventTime; - int64_t downTime; - - int32_t flags; - int32_t metaState; - - int32_t modifiers; - int32_t repeatCount; - int32_t keyCode; - int32_t scanCode; - //int32_t unicodeChar; -} GameActivityKeyEvent; - -/** - * \brief Convert a Java `KeyEvent` to a `GameActivityKeyEvent`. - * - * This is done automatically by the GameActivity: see `onKeyUp` and `onKeyDown` - * to set a callback to consume the received events. - * This function can be used if you re-implement events handling in your own - * activity. - * Ownership of out_event is maintained by the caller. - */ -void GameActivityKeyEvent_fromJava(JNIEnv* env, jobject motionEvent, - GameActivityKeyEvent* out_event); - -#ifdef __cplusplus -} -#endif - -/** @} */ - -#endif // ANDROID_GAME_SDK_GAME_ACTIVITY_EVENTS_H diff --git a/android-activity/game-activity-csrc/game-activity/GameActivityLog.h b/android-activity/game-activity-csrc/game-activity/GameActivityLog.h deleted file mode 100644 index ba9a9e9..0000000 --- a/android-activity/game-activity-csrc/game-activity/GameActivityLog.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef ANDROID_GAME_SDK_GAME_ACTIVITY_LOG_H_ -#define ANDROID_GAME_SDK_GAME_ACTIVITY_LOG_H_ - -#define LOG_TAG "GameActivity" -#include - -#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__); -#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__); -#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__); -#ifdef NDEBUG -#define ALOGV(...) -#else -#define ALOGV(...) \ - __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__); -#endif - -/* Returns 2nd arg. Used to substitute default value if caller's vararg list - * is empty. - */ -#define __android_second(first, second, ...) second - -/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise - * returns nothing. - */ -#define __android_rest(first, ...) , ##__VA_ARGS__ - -#define android_printAssert(cond, tag, fmt...) \ - __android_log_assert(cond, tag, \ - __android_second(0, ##fmt, NULL) __android_rest(fmt)) - -#define CONDITION(cond) (__builtin_expect((cond) != 0, 0)) - -#ifndef LOG_ALWAYS_FATAL_IF -#define LOG_ALWAYS_FATAL_IF(cond, ...) \ - ((CONDITION(cond)) \ - ? ((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__)) \ - : (void)0) -#endif - -#ifndef LOG_ALWAYS_FATAL -#define LOG_ALWAYS_FATAL(...) \ - (((void)android_printAssert(NULL, LOG_TAG, ##__VA_ARGS__))) -#endif - -/* - * Simplified macro to send a warning system log message using current LOG_TAG. - */ -#ifndef SLOGW -#define SLOGW(...) \ - ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef SLOGW_IF -#define SLOGW_IF(cond, ...) \ - ((__predict_false(cond)) \ - ? ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \ - : (void)0) -#endif - -/* - * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that - * are stripped out of release builds. - */ -#if LOG_NDEBUG - -#ifndef LOG_FATAL_IF -#define LOG_FATAL_IF(cond, ...) ((void)0) -#endif -#ifndef LOG_FATAL -#define LOG_FATAL(...) ((void)0) -#endif - -#else - -#ifndef LOG_FATAL_IF -#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ##__VA_ARGS__) -#endif -#ifndef LOG_FATAL -#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__) -#endif - -#endif - -/* - * Assertion that generates a log message when the assertion fails. - * Stripped out of release builds. Uses the current LOG_TAG. - */ -#ifndef ALOG_ASSERT -#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__) -#endif - -#define LOG_TRACE(...) - -#endif // ANDROID_GAME_SDK_GAME_ACTIVITY_LOG_H_ 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 deleted file mode 100644 index a7bcb85..0000000 --- a/android-activity/game-activity-csrc/game-activity/native_app_glue/android_native_app_glue.c +++ /dev/null @@ -1,732 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "android_native_app_glue.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#define NATIVE_APP_GLUE_MOTION_EVENTS_DEFAULT_BUF_SIZE 16 -#define NATIVE_APP_GLUE_KEY_EVENTS_DEFAULT_BUF_SIZE 4 - -#define LOGI(...) \ - ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__)) -#define LOGE(...) \ - ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__)) -#define LOGW(...) \ - ((void)__android_log_print(ANDROID_LOG_WARN, "threaded_app", __VA_ARGS__)) -#define LOGW_ONCE(...) \ - do { \ - static bool alogw_once##__FILE__##__LINE__##__ = true; \ - if (alogw_once##__FILE__##__LINE__##__) { \ - alogw_once##__FILE__##__LINE__##__ = false; \ - LOGW(__VA_ARGS__); \ - } \ - } while (0) - -/* For debug builds, always enable the debug traces in this library */ -#ifndef NDEBUG -#define LOGV(...) \ - ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", \ - __VA_ARGS__)) -#else -#define LOGV(...) ((void)0) -#endif - -static void free_saved_state(struct android_app* android_app) { - pthread_mutex_lock(&android_app->mutex); - if (android_app->savedState != NULL) { - free(android_app->savedState); - android_app->savedState = NULL; - android_app->savedStateSize = 0; - } - pthread_mutex_unlock(&android_app->mutex); -} - -int8_t android_app_read_cmd(struct android_app* android_app) { - int8_t cmd; - if (read(android_app->msgread, &cmd, sizeof(cmd)) != sizeof(cmd)) { - LOGE("No data on command pipe!"); - return -1; - } - if (cmd == APP_CMD_SAVE_STATE) free_saved_state(android_app); - return cmd; -} - -static void print_cur_config(struct android_app* android_app) { - char lang[2], country[2]; - AConfiguration_getLanguage(android_app->config, lang); - AConfiguration_getCountry(android_app->config, country); - - LOGV( - "Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d " - "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d " - "modetype=%d modenight=%d", - AConfiguration_getMcc(android_app->config), - AConfiguration_getMnc(android_app->config), lang[0], lang[1], - country[0], country[1], - AConfiguration_getOrientation(android_app->config), - AConfiguration_getTouchscreen(android_app->config), - AConfiguration_getDensity(android_app->config), - AConfiguration_getKeyboard(android_app->config), - AConfiguration_getNavigation(android_app->config), - AConfiguration_getKeysHidden(android_app->config), - AConfiguration_getNavHidden(android_app->config), - AConfiguration_getSdkVersion(android_app->config), - AConfiguration_getScreenSize(android_app->config), - AConfiguration_getScreenLong(android_app->config), - AConfiguration_getUiModeType(android_app->config), - AConfiguration_getUiModeNight(android_app->config)); -} - -void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) { - switch (cmd) { - case UNUSED_APP_CMD_INPUT_CHANGED: - LOGV("UNUSED_APP_CMD_INPUT_CHANGED"); - // Do nothing. This can be used in the future to handle AInputQueue - // natively, like done in NativeActivity. - break; - - case APP_CMD_INIT_WINDOW: - LOGV("APP_CMD_INIT_WINDOW"); - pthread_mutex_lock(&android_app->mutex); - android_app->window = android_app->pendingWindow; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - break; - - case APP_CMD_TERM_WINDOW: - LOGV("APP_CMD_TERM_WINDOW"); - pthread_cond_broadcast(&android_app->cond); - break; - - case APP_CMD_RESUME: - case APP_CMD_START: - case APP_CMD_PAUSE: - case APP_CMD_STOP: - LOGV("activityState=%d", cmd); - pthread_mutex_lock(&android_app->mutex); - android_app->activityState = cmd; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - break; - - case APP_CMD_CONFIG_CHANGED: - LOGV("APP_CMD_CONFIG_CHANGED"); - AConfiguration_fromAssetManager( - android_app->config, android_app->activity->assetManager); - print_cur_config(android_app); - break; - - case APP_CMD_DESTROY: - LOGV("APP_CMD_DESTROY"); - android_app->destroyRequested = 1; - break; - } -} - -void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) { - switch (cmd) { - case APP_CMD_TERM_WINDOW: - LOGV("APP_CMD_TERM_WINDOW"); - pthread_mutex_lock(&android_app->mutex); - android_app->window = NULL; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - break; - - case APP_CMD_SAVE_STATE: - LOGV("APP_CMD_SAVE_STATE"); - pthread_mutex_lock(&android_app->mutex); - android_app->stateSaved = 1; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - break; - - case APP_CMD_RESUME: - free_saved_state(android_app); - break; - } -} - -void app_dummy() {} - -static void android_app_destroy(struct android_app* android_app) { - LOGV("android_app_destroy!"); - free_saved_state(android_app); - pthread_mutex_lock(&android_app->mutex); - - AConfiguration_delete(android_app->config); - android_app->destroyed = 1; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - // Can't touch android_app object after this. -} - -static void process_cmd(struct android_app* app, - struct android_poll_source* source) { - int8_t cmd = android_app_read_cmd(app); - android_app_pre_exec_cmd(app, cmd); - if (app->onAppCmd != NULL) app->onAppCmd(app, cmd); - android_app_post_exec_cmd(app, cmd); -} - -// This is run on a separate thread (i.e: not the main thread). -static void* android_app_entry(void* param) { - struct android_app* android_app = (struct android_app*)param; - int input_buf_idx = 0; - - LOGV("android_app_entry called"); - android_app->config = AConfiguration_new(); - LOGV("android_app = %p", android_app); - LOGV("config = %p", android_app->config); - LOGV("activity = %p", android_app->activity); - LOGV("assetmanager = %p", android_app->activity->assetManager); - AConfiguration_fromAssetManager(android_app->config, - android_app->activity->assetManager); - - print_cur_config(android_app); - - /* initialize event buffers */ - for (input_buf_idx = 0; input_buf_idx < NATIVE_APP_GLUE_MAX_INPUT_BUFFERS; input_buf_idx++) { - struct android_input_buffer *buf = &android_app->inputBuffers[input_buf_idx]; - - buf->motionEventsBufferSize = NATIVE_APP_GLUE_MOTION_EVENTS_DEFAULT_BUF_SIZE; - buf->motionEvents = (GameActivityMotionEvent *) malloc(sizeof(GameActivityMotionEvent) * - buf->motionEventsBufferSize); - - buf->keyEventsBufferSize = NATIVE_APP_GLUE_KEY_EVENTS_DEFAULT_BUF_SIZE; - buf->keyEvents = (GameActivityKeyEvent *) malloc(sizeof(GameActivityKeyEvent) * - buf->keyEventsBufferSize); - } - - android_app->cmdPollSource.id = LOOPER_ID_MAIN; - android_app->cmdPollSource.app = android_app; - android_app->cmdPollSource.process = process_cmd; - - 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); - android_app->looper = looper; - - pthread_mutex_lock(&android_app->mutex); - android_app->running = 1; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - - _rust_glue_entry(android_app); - - android_app_destroy(android_app); - return NULL; -} - -// Codes from https://developer.android.com/reference/android/view/KeyEvent -#define KEY_EVENT_KEYCODE_VOLUME_DOWN 25 -#define KEY_EVENT_KEYCODE_VOLUME_MUTE 164 -#define KEY_EVENT_KEYCODE_VOLUME_UP 24 -#define KEY_EVENT_KEYCODE_CAMERA 27 -#define KEY_EVENT_KEYCODE_ZOOM_IN 168 -#define KEY_EVENT_KEYCODE_ZOOM_OUT 169 - -// Double-buffer the key event filter to avoid race condition. -static bool default_key_filter(const GameActivityKeyEvent* event) { - // Ignore camera, volume, etc. buttons - return !(event->keyCode == KEY_EVENT_KEYCODE_VOLUME_DOWN || - event->keyCode == KEY_EVENT_KEYCODE_VOLUME_MUTE || - event->keyCode == KEY_EVENT_KEYCODE_VOLUME_UP || - event->keyCode == KEY_EVENT_KEYCODE_CAMERA || - event->keyCode == KEY_EVENT_KEYCODE_ZOOM_IN || - event->keyCode == KEY_EVENT_KEYCODE_ZOOM_OUT); -} - -// See -// https://developer.android.com/reference/android/view/InputDevice#SOURCE_TOUCHSCREEN -#define SOURCE_TOUCHSCREEN 0x00001002 - -static bool default_motion_filter(const GameActivityMotionEvent* event) { - // Ignore any non-touch events. - return event->source == SOURCE_TOUCHSCREEN; -} - -// -------------------------------------------------------------------- -// Native activity interaction (called from main thread) -// -------------------------------------------------------------------- - -static struct android_app* android_app_create(GameActivity* activity, - void* savedState, - size_t savedStateSize) { - // struct android_app* android_app = calloc(1, sizeof(struct android_app)); - struct android_app* android_app = - (struct android_app*)malloc(sizeof(struct android_app)); - memset(android_app, 0, sizeof(struct android_app)); - android_app->activity = activity; - - pthread_mutex_init(&android_app->mutex, NULL); - pthread_cond_init(&android_app->cond, NULL); - - if (savedState != NULL) { - android_app->savedState = malloc(savedStateSize); - android_app->savedStateSize = savedStateSize; - memcpy(android_app->savedState, savedState, savedStateSize); - } - - int msgpipe[2]; - if (pipe(msgpipe)) { - LOGE("could not create pipe: %s", strerror(errno)); - return NULL; - } - android_app->msgread = msgpipe[0]; - android_app->msgwrite = msgpipe[1]; - - android_app->keyEventFilter = default_key_filter; - android_app->motionEventFilter = default_motion_filter; - - LOGV("Launching android_app_entry in a thread"); - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&android_app->thread, &attr, android_app_entry, android_app); - - // Wait for thread to start. - pthread_mutex_lock(&android_app->mutex); - while (!android_app->running) { - pthread_cond_wait(&android_app->cond, &android_app->mutex); - } - pthread_mutex_unlock(&android_app->mutex); - - return android_app; -} - -static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { - if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { - LOGE("Failure writing android_app cmd: %s", strerror(errno)); - } -} - -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); - } - android_app->pendingWindow = window; - if (window != NULL) { - android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW); - } - while (android_app->window != android_app->pendingWindow) { - pthread_cond_wait(&android_app->cond, &android_app->mutex); - } - pthread_mutex_unlock(&android_app->mutex); -} - -static void android_app_set_activity_state(struct android_app* android_app, - int8_t cmd) { - 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) { - 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); -} - -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); - } - pthread_mutex_unlock(&android_app->mutex); - - for (input_buf_idx = 0; input_buf_idx < NATIVE_APP_GLUE_MAX_INPUT_BUFFERS; input_buf_idx++) { - struct android_input_buffer *buf = &android_app->inputBuffers[input_buf_idx]; - - android_app_clear_motion_events(buf); - free(buf->motionEvents); - free(buf->keyEvents); - } - - close(android_app->msgread); - close(android_app->msgwrite); - pthread_cond_destroy(&android_app->cond); - pthread_mutex_destroy(&android_app->mutex); - free(android_app); -} - -static inline struct android_app* ToApp(GameActivity* activity) { - return (struct android_app*)activity->instance; -} - -static void onDestroy(GameActivity* activity) { - LOGV("Destroy: %p", activity); - android_app_free(ToApp(activity)); -} - -static void onStart(GameActivity* activity) { - LOGV("Start: %p", activity); - android_app_set_activity_state(ToApp(activity), APP_CMD_START); -} - -static void onResume(GameActivity* activity) { - LOGV("Resume: %p", activity); - android_app_set_activity_state(ToApp(activity), APP_CMD_RESUME); -} - -static void onSaveInstanceState(GameActivity* activity, - SaveInstanceStateRecallback recallback, - void* context) { - LOGV("SaveInstanceState: %p", 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) { - pthread_cond_wait(&android_app->cond, &android_app->mutex); - } - - if (android_app->savedState != NULL) { - // Tell the Java side about our state. - recallback((const char*)android_app->savedState, - android_app->savedStateSize, context); - // Now we can free it. - free(android_app->savedState); - android_app->savedState = NULL; - android_app->savedStateSize = 0; - } - - pthread_mutex_unlock(&android_app->mutex); -} - -static void onPause(GameActivity* activity) { - LOGV("Pause: %p", activity); - android_app_set_activity_state(ToApp(activity), APP_CMD_PAUSE); -} - -static void onStop(GameActivity* activity) { - LOGV("Stop: %p", activity); - android_app_set_activity_state(ToApp(activity), APP_CMD_STOP); -} - -static void onConfigurationChanged(GameActivity* activity) { - LOGV("ConfigurationChanged: %p", activity); - android_app_write_cmd(ToApp(activity), APP_CMD_CONFIG_CHANGED); -} - -static void onTrimMemory(GameActivity* activity, int level) { - LOGV("TrimMemory: %p %d", activity, level); - android_app_write_cmd(ToApp(activity), APP_CMD_LOW_MEMORY); -} - -static void onWindowFocusChanged(GameActivity* activity, bool focused) { - LOGV("WindowFocusChanged: %p -- %d", activity, focused); - android_app_write_cmd(ToApp(activity), - focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); -} - -static void onNativeWindowCreated(GameActivity* activity, - ANativeWindow* window) { - LOGV("NativeWindowCreated: %p -- %p", activity, window); - android_app_set_window(ToApp(activity), window); -} - -static void onNativeWindowDestroyed(GameActivity* activity, - ANativeWindow* window) { - LOGV("NativeWindowDestroyed: %p -- %p", activity, window); - android_app_set_window(ToApp(activity), NULL); -} - -static void onNativeWindowRedrawNeeded(GameActivity* activity, - ANativeWindow* window) { - LOGV("NativeWindowRedrawNeeded: %p -- %p", activity, window); - android_app_write_cmd(ToApp(activity), APP_CMD_WINDOW_REDRAW_NEEDED); -} - -static void onNativeWindowResized(GameActivity* activity, ANativeWindow* window, - int32_t width, int32_t height) { - LOGV("NativeWindowResized: %p -- %p ( %d x %d )", activity, window, width, - height); - android_app_write_cmd(ToApp(activity), APP_CMD_WINDOW_RESIZED); -} - -void android_app_set_motion_event_filter(struct android_app* app, - android_motion_event_filter filter) { - pthread_mutex_lock(&app->mutex); - app->motionEventFilter = filter; - 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); - 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); - return false; - } - - struct android_input_buffer* inputBuffer = - &android_app->inputBuffers[android_app->currentInputBuffer]; - - // Add to the list of active motion events - if (inputBuffer->motionEventsCount >= inputBuffer->motionEventsBufferSize) { - inputBuffer->motionEventsBufferSize *= 2; - inputBuffer->motionEvents = (GameActivityMotionEvent *) realloc(inputBuffer->motionEvents, - sizeof(GameActivityMotionEvent) * inputBuffer->motionEventsBufferSize); - - if (inputBuffer->motionEvents == NULL) { - LOGE("onTouchEvent: out of memory"); - abort(); - } - } - - 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; -} - -struct android_input_buffer* android_app_swap_input_buffers( - struct android_app* android_app) { - pthread_mutex_lock(&android_app->mutex); - - struct android_input_buffer* inputBuffer = - &android_app->inputBuffers[android_app->currentInputBuffer]; - - if (inputBuffer->motionEventsCount == 0 && - inputBuffer->keyEventsCount == 0) { - inputBuffer = NULL; - } else { - android_app->currentInputBuffer = - (android_app->currentInputBuffer + 1) % - NATIVE_APP_GLUE_MAX_INPUT_BUFFERS; - } - - android_app->inputSwapPending = false; - android_app->inputAvailableWakeUp = false; - - pthread_mutex_unlock(&android_app->mutex); - - return inputBuffer; -} - -void android_app_clear_motion_events(struct android_input_buffer* inputBuffer) { - // We do not need to lock here if the inputBuffer has already been swapped - // as is handled by the game loop thread - while (inputBuffer->motionEventsCount > 0) { - GameActivityMotionEvent_destroy( - &inputBuffer->motionEvents[inputBuffer->motionEventsCount - 1]); - - inputBuffer->motionEventsCount--; - } - assert(inputBuffer->motionEventsCount == 0); -} - -void android_app_set_key_event_filter(struct android_app* app, - android_key_event_filter filter) { - pthread_mutex_lock(&app->mutex); - app->keyEventFilter = filter; - pthread_mutex_unlock(&app->mutex); -} - -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); - return false; - } - - struct android_input_buffer* inputBuffer = - &android_app->inputBuffers[android_app->currentInputBuffer]; - - // Add to the list of active key down events - if (inputBuffer->keyEventsCount >= inputBuffer->keyEventsBufferSize) { - inputBuffer->keyEventsBufferSize = inputBuffer->keyEventsBufferSize * 2; - inputBuffer->keyEvents = (GameActivityKeyEvent *) realloc(inputBuffer->keyEvents, - sizeof(GameActivityKeyEvent) * inputBuffer->keyEventsBufferSize); - - if (inputBuffer->keyEvents == NULL) { - LOGE("onKey: out of memory"); - abort(); - } - } - - 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; -} - -void android_app_clear_key_events(struct android_input_buffer* inputBuffer) { - inputBuffer->keyEventsCount = 0; -} - -static void onTextInputEvent(GameActivity* activity, - const GameTextInputState* state) { - struct android_app* android_app = ToApp(activity); - pthread_mutex_lock(&android_app->mutex); - if (!android_app->destroyed) { - android_app->textInputState = 1; - notifyInput(android_app); - } - pthread_mutex_unlock(&android_app->mutex); -} - -static void onWindowInsetsChanged(GameActivity* activity) { - LOGV("WindowInsetsChanged: %p", activity); - android_app_write_cmd(ToApp(activity), APP_CMD_WINDOW_INSETS_CHANGED); -} - -static void onContentRectChanged(GameActivity* activity, const ARect *rect) { - LOGV("ContentRectChanged: %p -- (%d %d) (%d %d)", activity, rect->left, rect->top, - rect->right, rect->bottom); - - struct android_app* android_app = ToApp(activity); - - pthread_mutex_lock(&android_app->mutex); - android_app->contentRect = *rect; - - android_app_write_cmd(android_app, APP_CMD_CONTENT_RECT_CHANGED); - pthread_mutex_unlock(&android_app->mutex); -} - -// 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_C(GameActivity* activity, void* savedState, - size_t savedStateSize) { - LOGV("Creating: %p", activity); - activity->callbacks->onDestroy = onDestroy; - activity->callbacks->onStart = onStart; - activity->callbacks->onResume = onResume; - activity->callbacks->onSaveInstanceState = onSaveInstanceState; - activity->callbacks->onPause = onPause; - activity->callbacks->onStop = onStop; - activity->callbacks->onTouchEvent = onTouchEvent; - activity->callbacks->onKeyDown = onKey; - activity->callbacks->onKeyUp = onKey; - activity->callbacks->onTextInputEvent = onTextInputEvent; - activity->callbacks->onConfigurationChanged = onConfigurationChanged; - activity->callbacks->onTrimMemory = onTrimMemory; - activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; - activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; - activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; - activity->callbacks->onNativeWindowRedrawNeeded = - onNativeWindowRedrawNeeded; - activity->callbacks->onNativeWindowResized = onNativeWindowResized; - activity->callbacks->onWindowInsetsChanged = onWindowInsetsChanged; - activity->callbacks->onContentRectChanged = onContentRectChanged; - LOGV("Callbacks set: %p", activity->callbacks); - - activity->instance = - android_app_create(activity, savedState, savedStateSize); -} 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 deleted file mode 100644 index 6fa5721..0000000 --- a/android-activity/game-activity-csrc/game-activity/native_app_glue/android_native_app_glue.h +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -/** - * @addtogroup android_native_app_glue Native App Glue library - * The glue library to interface your game loop with GameActivity. - * @{ - */ - -#include -#include -#include -#include -#include - -#include "game-activity/GameActivity.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * The GameActivity interface provided by - * is based on a set of application-provided callbacks that will be called - * by the Activity's main thread when certain events occur. - * - * This means that each one of this callbacks _should_ _not_ block, or they - * risk having the system force-close the application. This programming - * model is direct, lightweight, but constraining. - * - * The 'android_native_app_glue' static library is used to provide a different - * execution model where the application can implement its own main event - * loop in a different thread instead. Here's how it works: - * - * 1/ The application must provide a function named "android_main()" that - * will be called when the activity is created, in a new thread that is - * distinct from the activity's main thread. - * - * 2/ android_main() receives a pointer to a valid "android_app" structure - * that contains references to other important objects, e.g. the - * GameActivity obejct instance the application is running in. - * - * 3/ the "android_app" object holds an ALooper instance that already - * listens to activity lifecycle events (e.g. "pause", "resume"). - * See APP_CMD_XXX declarations below. - * - * This corresponds to an ALooper identifier returned by - * ALooper_pollOnce with value LOOPER_ID_MAIN. - * - * Your application can use the same ALooper to listen to additional - * file-descriptors. They can either be callback based, or with return - * identifiers starting with LOOPER_ID_USER. - * - * 4/ Whenever you receive a LOOPER_ID_MAIN event, - * the returned data will point to an android_poll_source structure. You - * can call the process() function on it, and fill in android_app->onAppCmd - * to be called for your own processing of the event. - * - * Alternatively, you can call the low-level functions to read and process - * the data directly... look at the process_cmd() and process_input() - * implementations in the glue to see how to do this. - * - * See the sample named "native-activity" that comes with the NDK with a - * full usage example. Also look at the documentation of GameActivity. - */ - -struct android_app; - -/** - * Data associated with an ALooper fd that will be returned as the "outData" - * when that source has data ready. - */ -struct android_poll_source { - /** - * The identifier of this source. May be LOOPER_ID_MAIN or - * LOOPER_ID_INPUT. - */ - int32_t id; - - /** The android_app this ident is associated with. */ - struct android_app* app; - - /** - * Function to call to perform the standard processing of data from - * this source. - */ - void (*process)(struct android_app* app, - struct android_poll_source* source); -}; - -struct android_input_buffer { - /** - * Pointer to a read-only array of GameActivityMotionEvent. - * Only the first motionEventsCount events are valid. - */ - GameActivityMotionEvent *motionEvents; - - /** - * The number of valid motion events in `motionEvents`. - */ - uint64_t motionEventsCount; - - /** - * The size of the `motionEvents` buffer. - */ - uint64_t motionEventsBufferSize; - - /** - * Pointer to a read-only array of GameActivityKeyEvent. - * Only the first keyEventsCount events are valid. - */ - GameActivityKeyEvent *keyEvents; - - /** - * The number of valid "Key" events in `keyEvents`. - */ - uint64_t keyEventsCount; - - /** - * The size of the `keyEvents` buffer. - */ - uint64_t keyEventsBufferSize; -}; - -/** - * Function pointer declaration for the filtering of key events. - * A function with this signature should be passed to - * android_app_set_key_event_filter and return false for any events that should - * not be handled by android_native_app_glue. These events will be handled by - * the system instead. - */ -typedef bool (*android_key_event_filter)(const GameActivityKeyEvent*); - -/** - * Function pointer definition for the filtering of motion events. - * A function with this signature should be passed to - * android_app_set_motion_event_filter and return false for any events that - * should not be handled by android_native_app_glue. These events will be - * handled by the system instead. - */ -typedef bool (*android_motion_event_filter)(const GameActivityMotionEvent*); - -/** - * This is the interface for the standard glue code of a threaded - * application. In this model, the application's code is running - * in its own thread separate from the main thread of the process. - * It is not required that this thread be associated with the Java - * VM, although it will need to be in order to make JNI calls any - * Java objects. - */ -struct android_app { - /** - * An optional pointer to application-defined state. - */ - void* userData; - - /** - * A required callback for processing main app commands (`APP_CMD_*`). - * This is called each frame if there are app commands that need processing. - */ - void (*onAppCmd)(struct android_app* app, int32_t cmd); - - /** The GameActivity object instance that this app is running in. */ - GameActivity* activity; - - /** The current configuration the app is running in. */ - AConfiguration* config; - - /** - * The last activity saved state, as provided at creation time. - * It is NULL if there was no state. You can use this as you need; the - * memory will remain around until you call android_app_exec_cmd() for - * APP_CMD_RESUME, at which point it will be freed and savedState set to - * NULL. These variables should only be changed when processing a - * APP_CMD_SAVE_STATE, at which point they will be initialized to NULL and - * you can malloc your state and place the information here. In that case - * the memory will be freed for you later. - */ - void* savedState; - - /** - * The size of the activity saved state. It is 0 if `savedState` is NULL. - */ - size_t savedStateSize; - - /** The ALooper associated with the app's thread. */ - ALooper* looper; - - /** When non-NULL, this is the window surface that the app can draw in. */ - ANativeWindow* window; - - /** - * Current content rectangle of the window; this is the area where the - * window's content should be placed to be seen by the user. - */ - ARect contentRect; - - /** - * Current state of the app's activity. May be either APP_CMD_START, - * APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP. - */ - int activityState; - - /** - * This is non-zero when the application's GameActivity is being - * destroyed and waiting for the app thread to complete. - */ - int destroyRequested; - -#define NATIVE_APP_GLUE_MAX_INPUT_BUFFERS 2 - - /** - * This is used for buffering input from GameActivity. Once ready, the - * application thread switches the buffers and processes what was - * accumulated. - */ - struct android_input_buffer inputBuffers[NATIVE_APP_GLUE_MAX_INPUT_BUFFERS]; - - int currentInputBuffer; - - /** - * 0 if no text input event is outstanding, 1 if it is. - * Use `GameActivity_getTextInputState` to get information - * about the text entered by the user. - */ - int textInputState; - - // Below are "private" implementation of the glue code. - /** @cond INTERNAL */ - - pthread_mutex_t mutex; - pthread_cond_t cond; - - int msgread; - int msgwrite; - - pthread_t thread; - - struct android_poll_source cmdPollSource; - - int running; - int stateSaved; - int destroyed; - int redrawNeeded; - ANativeWindow* pendingWindow; - ARect pendingContentRect; - - 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 */ -}; - -/** - * Looper ID of commands coming from the app's main thread, an AInputQueue or - * user-defined sources. - */ -enum NativeAppGlueLooperId { - /** - * Looper data ID of commands coming from the app's main thread, which - * is returned as an identifier from ALooper_pollOnce(). The data for this - * identifier is a pointer to an android_poll_source structure. - * These can be retrieved and processed with android_app_read_cmd() - * and android_app_exec_cmd(). - */ - LOOPER_ID_MAIN = 1, - - /** - * Unused. Reserved for future use when usage of AInputQueue will be - * supported. - */ - LOOPER_ID_INPUT = 2, - - /** - * Start of user-defined ALooper identifiers. - */ - LOOPER_ID_USER = 3, -}; - -/** - * Commands passed from the application's main Java thread to the game's thread. - */ -enum NativeAppGlueAppCmd { - /** - * Unused. Reserved for future use when usage of AInputQueue will be - * supported. - */ - UNUSED_APP_CMD_INPUT_CHANGED, - - /** - * Command from main thread: a new ANativeWindow is ready for use. Upon - * receiving this command, android_app->window will contain the new window - * surface. - */ - APP_CMD_INIT_WINDOW, - - /** - * Command from main thread: the existing ANativeWindow needs to be - * terminated. Upon receiving this command, android_app->window still - * contains the existing window; after calling android_app_exec_cmd - * it will be set to NULL. - */ - APP_CMD_TERM_WINDOW, - - /** - * Command from main thread: the current ANativeWindow has been resized. - * Please redraw with its new size. - */ - APP_CMD_WINDOW_RESIZED, - - /** - * Command from main thread: the system needs that the current ANativeWindow - * be redrawn. You should redraw the window before handing this to - * android_app_exec_cmd() in order to avoid transient drawing glitches. - */ - APP_CMD_WINDOW_REDRAW_NEEDED, - - /** - * Command from main thread: the content area of the window has changed, - * such as from the soft input window being shown or hidden. You can - * find the new content rect in android_app::contentRect. - */ - APP_CMD_CONTENT_RECT_CHANGED, - - /** - * Command from main thread: the app's activity window has gained - * input focus. - */ - APP_CMD_GAINED_FOCUS, - - /** - * Command from main thread: the app's activity window has lost - * input focus. - */ - APP_CMD_LOST_FOCUS, - - /** - * Command from main thread: the current device configuration has changed. - */ - APP_CMD_CONFIG_CHANGED, - - /** - * Command from main thread: the system is running low on memory. - * Try to reduce your memory use. - */ - APP_CMD_LOW_MEMORY, - - /** - * Command from main thread: the app's activity has been started. - */ - APP_CMD_START, - - /** - * Command from main thread: the app's activity has been resumed. - */ - APP_CMD_RESUME, - - /** - * Command from main thread: the app should generate a new saved state - * for itself, to restore from later if needed. If you have saved state, - * allocate it with malloc and place it in android_app.savedState with - * the size in android_app.savedStateSize. The will be freed for you - * later. - */ - APP_CMD_SAVE_STATE, - - /** - * Command from main thread: the app's activity has been paused. - */ - APP_CMD_PAUSE, - - /** - * Command from main thread: the app's activity has been stopped. - */ - APP_CMD_STOP, - - /** - * Command from main thread: the app's activity is being destroyed, - * and waiting for the app thread to clean up and exit before proceeding. - */ - APP_CMD_DESTROY, - - /** - * Command from main thread: the app's insets have changed. - */ - APP_CMD_WINDOW_INSETS_CHANGED, - -}; - -/** - * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next - * app command message. - */ -int8_t android_app_read_cmd(struct android_app* android_app); - -/** - * Call with the command returned by android_app_read_cmd() to do the - * initial pre-processing of the given command. You can perform your own - * actions for the command after calling this function. - */ -void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd); - -/** - * Call with the command returned by android_app_read_cmd() to do the - * final post-processing of the given command. You must have done your own - * actions for the command before calling this function. - */ -void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd); - -/** - * Call this before processing input events to get the events buffer. - * The function returns NULL if there are no events to process. - */ -struct android_input_buffer* android_app_swap_input_buffers( - struct android_app* android_app); - -/** - * Clear the array of motion events that were waiting to be handled, and release - * each of them. - * - * This method should be called after you have processed the motion events in - * your game loop. You should handle events at each iteration of your game loop. - */ -void android_app_clear_motion_events(struct android_input_buffer* inputBuffer); - -/** - * Clear the array of key events that were waiting to be handled, and release - * each of them. - * - * This method should be called after you have processed the key up events in - * your game loop. You should handle events at each iteration of your game loop. - */ -void android_app_clear_key_events(struct android_input_buffer* inputBuffer); - -/** - * This is a springboard into the Rust glue layer that wraps calling the - * main entry for the app itself. - */ -extern void _rust_glue_entry(struct android_app* app); - -/** - * Set the filter to use when processing key events. - * Any events for which the filter returns false will be ignored by - * android_native_app_glue. If filter is set to NULL, no filtering is done. - * - * The default key filter will filter out volume and camera button presses. - */ -void android_app_set_key_event_filter(struct android_app* app, - android_key_event_filter filter); - -/** - * Set the filter to use when processing touch and motion events. - * Any events for which the filter returns false will be ignored by - * android_native_app_glue. If filter is set to NULL, no filtering is done. - * - * Note that the default motion event filter will only allow touchscreen events - * through, in order to mimic NativeActivity's behaviour, so for controller - * events to be passed to the app, set the filter to NULL. - */ -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 - -/** @} */ diff --git a/android-activity/game-activity-csrc/game-text-input/gamecommon.h b/android-activity/game-activity-csrc/game-text-input/gamecommon.h deleted file mode 100755 index 38bffff..0000000 --- a/android-activity/game-activity-csrc/game-text-input/gamecommon.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @defgroup game_common Game Common - * Common structures and functions used within AGDK - * @{ - */ - -#pragma once - -/** - * The type of a component for which to retrieve insets. See - * https://developer.android.com/reference/androidx/core/view/WindowInsetsCompat.Type - */ -typedef enum GameCommonInsetsType { - GAMECOMMON_INSETS_TYPE_CAPTION_BAR = 0, - GAMECOMMON_INSETS_TYPE_DISPLAY_CUTOUT, - GAMECOMMON_INSETS_TYPE_IME, - GAMECOMMON_INSETS_TYPE_MANDATORY_SYSTEM_GESTURES, - GAMECOMMON_INSETS_TYPE_NAVIGATION_BARS, - GAMECOMMON_INSETS_TYPE_STATUS_BARS, - GAMECOMMON_INSETS_TYPE_SYSTEM_BARS, - GAMECOMMON_INSETS_TYPE_SYSTEM_GESTURES, - GAMECOMMON_INSETS_TYPE_TAPABLE_ELEMENT, - GAMECOMMON_INSETS_TYPE_WATERFALL, - GAMECOMMON_INSETS_TYPE_COUNT -} GameCommonInsetsType; diff --git a/android-activity/game-activity-csrc/game-text-input/gametextinput.cpp b/android-activity/game-activity-csrc/game-text-input/gametextinput.cpp deleted file mode 100755 index b8244fb..0000000 --- a/android-activity/game-activity-csrc/game-text-input/gametextinput.cpp +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "game-text-input/gametextinput.h" - -#include -#include -#include -#include - -#include -#include -#include - -#define LOG_TAG "GameTextInput" - -static constexpr int32_t DEFAULT_MAX_STRING_SIZE = 1 << 16; - -// Cache of field ids in the Java GameTextInputState class -struct StateClassInfo { - jfieldID text; - jfieldID selectionStart; - jfieldID selectionEnd; - jfieldID composingRegionStart; - jfieldID composingRegionEnd; -}; - -// Main GameTextInput object. -struct GameTextInput { - public: - GameTextInput(JNIEnv *env, uint32_t max_string_size); - ~GameTextInput(); - void setState(const GameTextInputState &state); - const GameTextInputState &getState() const { return currentState_; } - void setInputConnection(jobject inputConnection); - void processEvent(jobject textInputEvent); - void showIme(uint32_t flags); - void hideIme(uint32_t flags); - void restartInput(); - void setEventCallback(GameTextInputEventCallback callback, void *context); - jobject stateToJava(const GameTextInputState &state) const; - void stateFromJava(jobject textInputEvent, - GameTextInputGetStateCallback callback, - void *context) const; - void setImeInsetsCallback(GameTextInputImeInsetsCallback callback, - void *context); - void processImeInsets(const ARect *insets); - const ARect &getImeInsets() const { return currentInsets_; } - - private: - // Copy string and set other fields - void setStateInner(const GameTextInputState &state); - static void processCallback(void *context, const GameTextInputState *state); - JNIEnv *env_ = nullptr; - // Cached at initialization from - // com/google/androidgamesdk/gametextinput/State. - jclass stateJavaClass_ = nullptr; - // The latest text input update. - GameTextInputState currentState_ = {}; - // An instance of gametextinput.InputConnection. - jclass inputConnectionClass_ = nullptr; - jobject inputConnection_ = nullptr; - jmethodID inputConnectionSetStateMethod_; - jmethodID setSoftKeyboardActiveMethod_; - jmethodID restartInputMethod_; - void (*eventCallback_)(void *context, - const struct GameTextInputState *state) = nullptr; - void *eventCallbackContext_ = nullptr; - void (*insetsCallback_)(void *context, - const struct ARect *insets) = nullptr; - ARect currentInsets_ = {}; - void *insetsCallbackContext_ = nullptr; - StateClassInfo stateClassInfo_ = {}; - // Constant-sized buffer used to store state text. - std::vector stateStringBuffer_; -}; - -std::unique_ptr s_gameTextInput; - -extern "C" { - -/////////////////////////////////////////////////////////// -/// GameTextInputState C Functions -/////////////////////////////////////////////////////////// - -// Convert to a Java structure. -jobject currentState_toJava(const GameTextInput *gameTextInput, - const GameTextInputState *state) { - if (state == nullptr) return NULL; - return gameTextInput->stateToJava(*state); -} - -// Convert from Java structure. -void currentState_fromJava(const GameTextInput *gameTextInput, - jobject textInputEvent, - GameTextInputGetStateCallback callback, - void *context) { - gameTextInput->stateFromJava(textInputEvent, callback, context); -} - -/////////////////////////////////////////////////////////// -/// GameTextInput C Functions -/////////////////////////////////////////////////////////// - -struct GameTextInput *GameTextInput_init(JNIEnv *env, - uint32_t max_string_size) { - if (s_gameTextInput.get() != nullptr) { - __android_log_print(ANDROID_LOG_WARN, LOG_TAG, - "Warning: called GameTextInput_init twice without " - "calling GameTextInput_destroy"); - return s_gameTextInput.get(); - } - // Don't use make_unique, for C++11 compatibility - s_gameTextInput = - std::unique_ptr(new GameTextInput(env, max_string_size)); - return s_gameTextInput.get(); -} - -void GameTextInput_destroy(GameTextInput *input) { - if (input == nullptr || s_gameTextInput.get() == nullptr) return; - s_gameTextInput.reset(); -} - -void GameTextInput_setState(GameTextInput *input, - const GameTextInputState *state) { - if (state == nullptr) return; - input->setState(*state); -} - -void GameTextInput_getState(GameTextInput *input, - GameTextInputGetStateCallback callback, - void *context) { - callback(context, &input->getState()); -} - -void GameTextInput_setInputConnection(GameTextInput *input, - jobject inputConnection) { - input->setInputConnection(inputConnection); -} - -void GameTextInput_processEvent(GameTextInput *input, jobject textInputEvent) { - input->processEvent(textInputEvent); -} - -void GameTextInput_processImeInsets(GameTextInput *input, const ARect *insets) { - input->processImeInsets(insets); -} - -void GameTextInput_showIme(struct GameTextInput *input, uint32_t flags) { - input->showIme(flags); -} - -void GameTextInput_hideIme(struct GameTextInput *input, uint32_t flags) { - input->hideIme(flags); -} - -void GameTextInput_restartInput(struct GameTextInput *input) { - input->restartInput(); -} - -void GameTextInput_setEventCallback(struct GameTextInput *input, - GameTextInputEventCallback callback, - void *context) { - input->setEventCallback(callback, context); -} - -void GameTextInput_setImeInsetsCallback(struct GameTextInput *input, - GameTextInputImeInsetsCallback callback, - void *context) { - input->setImeInsetsCallback(callback, context); -} - -void GameTextInput_getImeInsets(const GameTextInput *input, ARect *insets) { - *insets = input->getImeInsets(); -} - -} // extern "C" - -/////////////////////////////////////////////////////////// -/// GameTextInput C++ class Implementation -/////////////////////////////////////////////////////////// - -GameTextInput::GameTextInput(JNIEnv *env, uint32_t max_string_size) - : env_(env), - stateStringBuffer_(max_string_size == 0 ? DEFAULT_MAX_STRING_SIZE - : max_string_size) { - stateJavaClass_ = (jclass)env_->NewGlobalRef( - env_->FindClass("com/google/androidgamesdk/gametextinput/State")); - inputConnectionClass_ = (jclass)env_->NewGlobalRef(env_->FindClass( - "com/google/androidgamesdk/gametextinput/InputConnection")); - inputConnectionSetStateMethod_ = - env_->GetMethodID(inputConnectionClass_, "setState", - "(Lcom/google/androidgamesdk/gametextinput/State;)V"); - setSoftKeyboardActiveMethod_ = env_->GetMethodID( - inputConnectionClass_, "setSoftKeyboardActive", "(ZI)V"); - restartInputMethod_ = - env_->GetMethodID(inputConnectionClass_, "restartInput", "()V"); - - stateClassInfo_.text = - env_->GetFieldID(stateJavaClass_, "text", "Ljava/lang/String;"); - stateClassInfo_.selectionStart = - env_->GetFieldID(stateJavaClass_, "selectionStart", "I"); - stateClassInfo_.selectionEnd = - env_->GetFieldID(stateJavaClass_, "selectionEnd", "I"); - stateClassInfo_.composingRegionStart = - env_->GetFieldID(stateJavaClass_, "composingRegionStart", "I"); - stateClassInfo_.composingRegionEnd = - env_->GetFieldID(stateJavaClass_, "composingRegionEnd", "I"); -} - -GameTextInput::~GameTextInput() { - if (stateJavaClass_ != NULL) { - env_->DeleteGlobalRef(stateJavaClass_); - stateJavaClass_ = NULL; - } - if (inputConnectionClass_ != NULL) { - env_->DeleteGlobalRef(inputConnectionClass_); - inputConnectionClass_ = NULL; - } - if (inputConnection_ != NULL) { - env_->DeleteGlobalRef(inputConnection_); - inputConnection_ = NULL; - } -} - -void GameTextInput::setState(const GameTextInputState &state) { - if (inputConnection_ == nullptr) return; - jobject jstate = stateToJava(state); - env_->CallVoidMethod(inputConnection_, inputConnectionSetStateMethod_, - jstate); - env_->DeleteLocalRef(jstate); - setStateInner(state); -} - -void GameTextInput::setStateInner(const GameTextInputState &state) { - // Check if we're setting using our own string (other parts may be - // different) - if (state.text_UTF8 == currentState_.text_UTF8) { - currentState_ = state; - return; - } - // Otherwise, copy across the string. - auto bytes_needed = - std::min(static_cast(state.text_length + 1), - static_cast(stateStringBuffer_.size())); - currentState_.text_UTF8 = stateStringBuffer_.data(); - std::copy(state.text_UTF8, state.text_UTF8 + bytes_needed - 1, - stateStringBuffer_.data()); - currentState_.text_length = state.text_length; - currentState_.selection = state.selection; - currentState_.composingRegion = state.composingRegion; - stateStringBuffer_[bytes_needed - 1] = 0; -} - -void GameTextInput::setInputConnection(jobject inputConnection) { - if (inputConnection_ != NULL) { - env_->DeleteGlobalRef(inputConnection_); - } - inputConnection_ = env_->NewGlobalRef(inputConnection); -} - -/*static*/ void GameTextInput::processCallback( - void *context, const GameTextInputState *state) { - auto thiz = static_cast(context); - if (state != nullptr) thiz->setStateInner(*state); -} - -void GameTextInput::processEvent(jobject textInputEvent) { - stateFromJava(textInputEvent, processCallback, this); - if (eventCallback_) { - eventCallback_(eventCallbackContext_, ¤tState_); - } -} - -void GameTextInput::showIme(uint32_t flags) { - if (inputConnection_ == nullptr) return; - env_->CallVoidMethod(inputConnection_, setSoftKeyboardActiveMethod_, true, - flags); -} - -void GameTextInput::setEventCallback(GameTextInputEventCallback callback, - void *context) { - eventCallback_ = callback; - eventCallbackContext_ = context; -} - -void GameTextInput::setImeInsetsCallback( - GameTextInputImeInsetsCallback callback, void *context) { - insetsCallback_ = callback; - insetsCallbackContext_ = context; -} - -void GameTextInput::processImeInsets(const ARect *insets) { - currentInsets_ = *insets; - if (insetsCallback_) { - insetsCallback_(insetsCallbackContext_, ¤tInsets_); - } -} - -void GameTextInput::hideIme(uint32_t flags) { - if (inputConnection_ == nullptr) return; - env_->CallVoidMethod(inputConnection_, setSoftKeyboardActiveMethod_, false, - flags); -} - -void GameTextInput::restartInput() { - if (inputConnection_ == nullptr) return; - env_->CallVoidMethod(inputConnection_, restartInputMethod_, false); -} - -jobject GameTextInput::stateToJava(const GameTextInputState &state) const { - static jmethodID constructor = nullptr; - if (constructor == nullptr) { - constructor = env_->GetMethodID(stateJavaClass_, "", - "(Ljava/lang/String;IIII)V"); - if (constructor == nullptr) { - __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, - "Can't find gametextinput.State constructor"); - return nullptr; - } - } - const char *text = state.text_UTF8; - if (text == nullptr) { - static char empty_string[] = ""; - text = empty_string; - } - // Note that this expects 'modified' UTF-8 which is not the same as UTF-8 - // https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8 - jstring jtext = env_->NewStringUTF(text); - jobject jobj = - env_->NewObject(stateJavaClass_, constructor, jtext, - state.selection.start, state.selection.end, - state.composingRegion.start, state.composingRegion.end); - env_->DeleteLocalRef(jtext); - return jobj; -} - -void GameTextInput::stateFromJava(jobject textInputEvent, - GameTextInputGetStateCallback callback, - void *context) const { - jstring text = - (jstring)env_->GetObjectField(textInputEvent, stateClassInfo_.text); - // Note this is 'modified' UTF-8, not true UTF-8. It has no NULLs in it, - // except at the end. It's actually not specified whether the value returned - // by GetStringUTFChars includes a null at the end, but it *seems to* on - // Android. - const char *text_chars = env_->GetStringUTFChars(text, NULL); - int text_len = env_->GetStringUTFLength( - text); // Length in bytes, *not* including the null. - int selectionStart = - env_->GetIntField(textInputEvent, stateClassInfo_.selectionStart); - int selectionEnd = - env_->GetIntField(textInputEvent, stateClassInfo_.selectionEnd); - int composingRegionStart = - env_->GetIntField(textInputEvent, stateClassInfo_.composingRegionStart); - int composingRegionEnd = - env_->GetIntField(textInputEvent, stateClassInfo_.composingRegionEnd); - GameTextInputState state{text_chars, - text_len, - {selectionStart, selectionEnd}, - {composingRegionStart, composingRegionEnd}}; - callback(context, &state); - env_->ReleaseStringUTFChars(text, text_chars); - env_->DeleteLocalRef(text); -} diff --git a/android-activity/game-activity-csrc/game-text-input/gametextinput.h b/android-activity/game-activity-csrc/game-text-input/gametextinput.h deleted file mode 100755 index 8c33da5..0000000 --- a/android-activity/game-activity-csrc/game-text-input/gametextinput.h +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @defgroup game_text_input Game Text Input - * The interface to use GameTextInput. - * @{ - */ - -#pragma once - -#include -#include -#include - -#include "common/gamesdk_common.h" -#include "gamecommon.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define GAMETEXTINPUT_MAJOR_VERSION 2 -#define GAMETEXTINPUT_MINOR_VERSION 0 -#define GAMETEXTINPUT_BUGFIX_VERSION 0 -#define GAMETEXTINPUT_PACKED_VERSION \ - ANDROID_GAMESDK_PACKED_VERSION(GAMETEXTINPUT_MAJOR_VERSION, \ - GAMETEXTINPUT_MINOR_VERSION, \ - GAMETEXTINPUT_BUGFIX_VERSION) - -/** - * This struct holds a span within a region of text from start (inclusive) to - * end (exclusive). An empty span or cursor position is specified with - * start==end. An undefined span is specified with start = end = SPAN_UNDEFINED. - */ -typedef struct GameTextInputSpan { - /** The start of the region (inclusive). */ - int32_t start; - /** The end of the region (exclusive). */ - int32_t end; -} GameTextInputSpan; - -/** - * Values with special meaning in a GameTextInputSpan. - */ -enum GameTextInputSpanFlag { SPAN_UNDEFINED = -1 }; - -/** - * This struct holds the state of an editable section of text. - * The text can have a selection and a composing region defined on it. - * A composing region is used by IMEs that allow input using multiple steps to - * compose a glyph or word. Use functions GameTextInput_getState and - * GameTextInput_setState to read and modify the state that an IME is editing. - */ -typedef struct GameTextInputState { - /** - * Text owned by the state, as a modified UTF-8 string. Null-terminated. - * https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8 - */ - const char *text_UTF8; - /** - * Length in bytes of text_UTF8, *not* including the null at end. - */ - int32_t text_length; - /** - * A selection defined on the text. - */ - GameTextInputSpan selection; - /** - * A composing region defined on the text. - */ - GameTextInputSpan composingRegion; -} GameTextInputState; - -/** - * A callback called by GameTextInput_getState. - * @param context User-defined context. - * @param state State, owned by the library, that will be valid for the duration - * of the callback. - */ -typedef void (*GameTextInputGetStateCallback)( - void *context, const struct GameTextInputState *state); - -/** - * Opaque handle to the GameTextInput API. - */ -typedef struct GameTextInput GameTextInput; - -/** - * Initialize the GameTextInput library. - * If called twice without GameTextInput_destroy being called, the same pointer - * will be returned and a warning will be issued. - * @param env A JNI env valid on the calling thread. - * @param max_string_size The maximum length of a string that can be edited. If - * zero, the maximum defaults to 65536 bytes. A buffer of this size is allocated - * at initialization. - * @return A handle to the library. - */ -GameTextInput *GameTextInput_init(JNIEnv *env, uint32_t max_string_size); - -/** - * When using GameTextInput, you need to create a gametextinput.InputConnection - * on the Java side and pass it using this function to the library, unless using - * GameActivity in which case this will be done for you. See the GameActivity - * source code or GameTextInput samples for examples of usage. - * @param input A valid GameTextInput library handle. - * @param inputConnection A gametextinput.InputConnection object. - */ -void GameTextInput_setInputConnection(GameTextInput *input, - jobject inputConnection); - -/** - * Unless using GameActivity, it is required to call this function from your - * Java gametextinput.Listener.stateChanged method to convert eventState and - * trigger any event callbacks. When using GameActivity, this does not need to - * be called as event processing is handled by the Activity. - * @param input A valid GameTextInput library handle. - * @param eventState A Java gametextinput.State object. - */ -void GameTextInput_processEvent(GameTextInput *input, jobject eventState); - -/** - * Free any resources owned by the GameTextInput library. - * Any subsequent calls to the library will fail until GameTextInput_init is - * called again. - * @param input A valid GameTextInput library handle. - */ -void GameTextInput_destroy(GameTextInput *input); - -/** - * Flags to be passed to GameTextInput_showIme. - */ -enum ShowImeFlags { - SHOW_IME_UNDEFINED = 0, // Default value. - SHOW_IMPLICIT = - 1, // Indicates that the user has forced the input method open so it - // should not be closed until they explicitly do so. - SHOW_FORCED = 2 // Indicates that this is an implicit request to show the - // input window, not as the result of a direct request by - // the user. The window may not be shown in this case. -}; - -/** - * Show the IME. Calls InputMethodManager.showSoftInput(). - * @param input A valid GameTextInput library handle. - * @param flags Defined in ShowImeFlags above. For more information see: - * https://developer.android.com/reference/android/view/inputmethod/InputMethodManager - */ -void GameTextInput_showIme(GameTextInput *input, uint32_t flags); - -/** - * Flags to be passed to GameTextInput_hideIme. - */ -enum HideImeFlags { - HIDE_IME_UNDEFINED = 0, // Default value. - HIDE_IMPLICIT_ONLY = - 1, // Indicates that the soft input window should only be hidden if it - // was not explicitly shown by the user. - HIDE_NOT_ALWAYS = - 2, // Indicates that the soft input window should normally be hidden, - // unless it was originally shown with SHOW_FORCED. -}; - -/** - * Show the IME. Calls InputMethodManager.hideSoftInputFromWindow(). - * @param input A valid GameTextInput library handle. - * @param flags Defined in HideImeFlags above. For more information see: - * https://developer.android.com/reference/android/view/inputmethod/InputMethodManager - */ -void GameTextInput_hideIme(GameTextInput *input, uint32_t flags); - -/** - * Restarts the input method. Calls InputMethodManager.restartInput(). - * @param input A valid GameTextInput library handle. - */ -void GameTextInput_restartInput(GameTextInput *input); - -/** - * Call a callback with the current GameTextInput state, which may have been - * modified by changes in the IME and calls to GameTextInput_setState. We use a - * callback rather than returning the state in order to simplify ownership of - * text_UTF8 strings. These strings are only valid during the calling of the - * callback. - * @param input A valid GameTextInput library handle. - * @param callback A function that will be called with valid state. - * @param context Context used by the callback. - */ -void GameTextInput_getState(GameTextInput *input, - GameTextInputGetStateCallback callback, - void *context); - -/** - * Set the current GameTextInput state. This state is reflected to any active - * IME. - * @param input A valid GameTextInput library handle. - * @param state The state to set. Ownership is maintained by the caller and must - * remain valid for the duration of the call. - */ -void GameTextInput_setState(GameTextInput *input, - const GameTextInputState *state); - -/** - * Type of the callback needed by GameTextInput_setEventCallback that will be - * called every time the IME state changes. - * @param context User-defined context set in GameTextInput_setEventCallback. - * @param current_state Current IME state, owned by the library and valid during - * the callback. - */ -typedef void (*GameTextInputEventCallback)( - void *context, const GameTextInputState *current_state); - -/** - * Optionally set a callback to be called whenever the IME state changes. - * Not necessary if you are using GameActivity, which handles these callbacks - * for you. - * @param input A valid GameTextInput library handle. - * @param callback Called by the library when the IME state changes. - * @param context Context passed as first argument to the callback. - */ -void GameTextInput_setEventCallback(GameTextInput *input, - GameTextInputEventCallback callback, - void *context); - -/** - * Type of the callback needed by GameTextInput_setImeInsetsCallback that will - * be called every time the IME window insets change. - * @param context User-defined context set in - * GameTextInput_setImeWIndowInsetsCallback. - * @param current_insets Current IME insets, owned by the library and valid - * during the callback. - */ -typedef void (*GameTextInputImeInsetsCallback)(void *context, - const ARect *current_insets); - -/** - * Optionally set a callback to be called whenever the IME insets change. - * Not necessary if you are using GameActivity, which handles these callbacks - * for you. - * @param input A valid GameTextInput library handle. - * @param callback Called by the library when the IME insets change. - * @param context Context passed as first argument to the callback. - */ -void GameTextInput_setImeInsetsCallback(GameTextInput *input, - GameTextInputImeInsetsCallback callback, - void *context); - -/** - * Get the current window insets for the IME. - * @param input A valid GameTextInput library handle. - * @param insets Filled with the current insets by this function. - */ -void GameTextInput_getImeInsets(const GameTextInput *input, ARect *insets); - -/** - * Unless using GameActivity, it is required to call this function from your - * Java gametextinput.Listener.onImeInsetsChanged method to - * trigger any event callbacks. When using GameActivity, this does not need to - * be called as insets processing is handled by the Activity. - * @param input A valid GameTextInput library handle. - * @param eventState A Java gametextinput.State object. - */ -void GameTextInput_processImeInsets(GameTextInput *input, const ARect *insets); - -/** - * Convert a GameTextInputState struct to a Java gametextinput.State object. - * Don't forget to delete the returned Java local ref when you're done. - * @param input A valid GameTextInput library handle. - * @param state Input state to convert. - * @return A Java object of class gametextinput.State. The caller is required to - * delete this local reference. - */ -jobject GameTextInputState_toJava(const GameTextInput *input, - const GameTextInputState *state); - -/** - * Convert from a Java gametextinput.State object into a C GameTextInputState - * struct. - * @param input A valid GameTextInput library handle. - * @param state A Java gametextinput.State object. - * @param callback A function called with the C struct, valid for the duration - * of the call. - * @param context Context passed to the callback. - */ -void GameTextInputState_fromJava(const GameTextInput *input, jobject state, - GameTextInputGetStateCallback callback, - void *context); - -#ifdef __cplusplus -} -#endif - -/** @} */