From fbdbc2e72d74d3f1e1552a416a0b91564c9443c9 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Fri, 20 Mar 2026 11:45:46 +0000 Subject: [PATCH] Import android-games-sdk changes for 4.4.0 This imports the Android Games SDK from: - repo: https://github.com/rust-mobile/android-games-sdk - branch: android-activity-4.4.0 - commit: 78daa4adfc4a619daeab9f96181190b145f1e544 This is based on the GameActivity 4.4.0 release from: - repo: https://android.googlesource.com/platform/frameworks/opt/gamesdk - branch: android-games-sdk-game-activity-release - commit: 541587a073871a9d2659f90335dcae345007eeed Our integration branch includes the following patches: - 78daa4ad Add mainLooper to android_app - 179eaa92 notify android_main of editor actions - 5102e14c Don't send (unused) APP_CMD_EDITOR_ACTION - 223936cf Don't send (unused) APP_CMD_KEY/TOUCH_EVENTs - a24b21a4 android-activity: don't read unicode via getUnicodeChar - 9e3926b1 android-activity: rename C symbols that need export - 32ac1c73 glue: support InputAvailable events - 69a1868c glue: fix deadlocks in java callbacks after app destroyed - b5a2df04 glue: remove unused variable This re-runs `generate-bindings.sh` Notes: Reviewing the upstream changes, it doesn't look like much has changed since the 4.0.0 release (at least in the glue layer). It was quite painful rebasing on the latest upstream release due to upstream running clang-format across their whole repo In this case I ended up using `git filter-branch` to reformat our patches before rebasing: ``` git filter-branch -f --tree-filter ' find game-activity/prefab-src/modules/game-activity \ \( -iname "*.c" -o -iname "*.h" -o -iname "*.cpp" \) \ -print0 | xargs -0 clang-format -i --style="{BasedOnStyle: Google, AccessModifierOffset: -4, AlignOperands: false, AllowShortFunctionsOnASingleLine: Empty, AlwaysBreakBeforeMultilineStrings: false, ColumnLimit: 100, CommentPragmas: \"NOLINT:.*\", ConstructorInitializerIndentWidth: 6, ContinuationIndentWidth: 8, IndentWidth: 4, PenaltyBreakBeforeFirstCallParameter: 100000, SpacesBeforeTrailingComments: 1}" ' cdf4eee808130cc007a6203904d1d6c9acbf53a3^..HEAD ``` Previously, we were apparently based on Google's `android-games-sdk-game-text-input-release` branch instead of their `android-games-sdk-game-activity-release` branch, which made it awkward to diff changes because both branches include the game-activity SDK but all of the same patches have different commit hashes between branches. Our previous base commit for GameActivity 4.0.0 was: 7f54c13ee549e4511dcdc15a8ca73864e87be605 which corresponds to: 65ee0100ead8cf73c851f150bffad2779dfa8704 on the game-activity-release branch Note: The upstream release notes are also confusing because where they list what commits are included in each release, then for the 4.0.0 release those commits only exist on the `game-text` release branch but for the 4.4.0 release the commits exist on the `game-activity` release branch. These are the upstream patches to the game-activity glue since 4.0.0: - c28257b2 Push new version of GameActivity 4.4.0 - no functional change - e32db80f Fixed formatting of gamesdk repo - no functional change - a7cdb8c6 Migrate from deprecate ALooper_pollAll to ALooper_pollOnce - only affects examples - 163d7fcb Improve android_app_set_activity_state ANR protection - this adds a timeout for how long android_app_set_activity_state will block waiting for the android_main thread to handle synchronous callbacks (such as onStart, onResume) - this is backwards compatible - d3fbe82a Improve version revision macro updating - no functional change - 2ae5d1f4 Release a new alpha version for AGDK components. - no functional change - 3e5fc4cd Add JNI_OnLoad function - an (optional) alternative means to call `GameActivity_register` which won't affect us since the `JNI_OnLoad` exported from C++ won't be exported when compiling with the Rust toolchain. - 044fd03c Release a new alpha version for AGDK components. - no functional change - 1198bb06 Fix GameActivity getLocale* functions. - this is a backwards compatible fix that doesn't interact without integration changes - 07eff729 Change GameActivity and GameTextInput to 4.1 alpha. - no functional change Based on this audit, our integration should be backwards compatible with GameActivity 4.0.0 --- android-activity/CHANGELOG.md | 26 +- .../include/game-activity/GameActivity.h | 880 ++++--- .../game-activity/GameActivityEvents.h | 220 +- .../include/game-activity/GameActivityLog.h | 27 +- .../native_app_glue/android_native_app_glue.h | 668 +++--- .../src/game-activity/GameActivity.cpp | 2042 ++++++++--------- .../src/game-activity/GameActivityEvents.cpp | 495 ++-- .../GameActivityEvents_internal.h | 6 +- .../native_app_glue/android_native_app_glue.c | 1109 +++++---- .../include/game-text-input/gametextinput.h | 1211 +++++----- .../src/game-text-input/gametextinput.cpp | 490 ++-- .../include/common/gamesdk_common.h | 4 +- .../src/common/system_utils.cpp | 13 +- .../src/common/system_utils.h | 2 +- .../src/game_activity/ffi_aarch64.rs | 12 +- android-activity/src/game_activity/ffi_arm.rs | 12 +- .../src/game_activity/ffi_i686.rs | 12 +- .../src/game_activity/ffi_x86_64.rs | 12 +- 18 files changed, 3530 insertions(+), 3711 deletions(-) diff --git a/android-activity/CHANGELOG.md b/android-activity/CHANGELOG.md index 373ad87e..16bf4f5f 100644 --- a/android-activity/CHANGELOG.md +++ b/android-activity/CHANGELOG.md @@ -53,7 +53,31 @@ fn android_on_create(state: &OnCreateState) { ### Changed - rust-version bumped to 1.85.0 ([#193](https://github.com/rust-mobile/android-activity/pull/193), [#219](https://github.com/rust-mobile/android-activity/pull/219)) -- GameActivity updated to 4.0.0 (requires the corresponding 4.0.0 `.aar` release from Google) ([#191](https://github.com/rust-mobile/android-activity/pull/191)) +- GameActivity updated to 4.4.0 ([#191](https://github.com/rust-mobile/android-activity/pull/191), [#240](https://github.com/rust-mobile/android-activity/pull/240)) + +**Important:** This release is no longer compatible with GameActivity 2.0.2 + +**Android Packaging:** Your Android application must be packaged with the corresponding androidX, GameActivity 4.x.x library from Google. + +This release has been tested with the [`androidx.games:games-activity:4.4.0` stable +release](https://developer.android.com/jetpack/androidx/releases/games#games-activity-4.4.0), and is backwards +compatible with the 4.0.0 stable release. + +If you use Gradle to build your Android application, you can depend on the 4.4.0 release of the GameActivity library via: + +```gradle +dependencies { + implementation 'androidx.appcompat:appcompat:1.7.1' + + // To use the Games Activity library + implementation "androidx.games:games-activity:4.4.0" + // Note: don't include game-text-input separately, since it's integrated into game-activity +} +``` + +Note: there is no guarantee that later 4.x.x releases of GameActivity will be compatible with this release of +`android-activity`, so please refer to the `android-activity` release notes for any future updates regarding +GameActivity compatibility. ### Fixed diff --git a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h index 42f450a1..aafe4fe0 100644 --- a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h +++ b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h @@ -43,30 +43,30 @@ extern "C" { #endif +#define GAMEACTIVITY_VERSION_REVISION a0e943c3a84fd7f344c3d36cdf4e88fd595f81b8 #define GAMEACTIVITY_MAJOR_VERSION 4 -#define GAMEACTIVITY_MINOR_VERSION 0 +#define GAMEACTIVITY_MINOR_VERSION 4 #define GAMEACTIVITY_BUGFIX_VERSION 0 -#define GAMEACTIVITY_PACKED_VERSION \ - ANDROID_GAMESDK_PACKED_VERSION(GAMEACTIVITY_MAJOR_VERSION, \ - GAMEACTIVITY_MINOR_VERSION, \ - GAMEACTIVITY_BUGFIX_VERSION) +#define GAMEACTIVITY_PACKED_VERSION \ + ANDROID_GAMESDK_PACKED_VERSION(GAMEACTIVITY_MAJOR_VERSION, GAMEACTIVITY_MINOR_VERSION, \ + GAMEACTIVITY_BUGFIX_VERSION) /** * The type of a component for which to retrieve insets. See * https://developer.android.com/reference/androidx/core/view/WindowInsetsCompat.Type */ typedef enum GameCommonInsetsType : uint8_t { - 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 + 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; /** @@ -80,74 +80,73 @@ struct GameActivityCallbacks; * 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; + /** + * 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); +typedef void (*SaveInstanceStateRecallback)(const char* bytes, int len, void* context); /** * These are the callbacks the framework makes into a native application. @@ -156,153 +155,147 @@ typedef void (*SaveInstanceStateRecallback)(const char* bytes, int len, * 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); - - /** - * Callback called when the software keyboard is shown or hidden. - */ - void (*onSoftwareKeyboardVisibilityChanged)(GameActivity* activity, - bool visible); - - /** - * Callback called when the software keyboard is shown or hidden. - */ - bool (*onEditorAction)(GameActivity* activity, int action); + /** + * 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); + + /** + * Callback called when the software keyboard is shown or hidden. + */ + void (*onSoftwareKeyboardVisibilityChanged)(GameActivity* activity, bool visible); + + /** + * Callback called when the software keyboard is shown or hidden. + */ + bool (*onEditorAction)(GameActivity* activity, int action); } GameActivityCallbacks; /** @@ -337,186 +330,186 @@ void GameActivity_finish(GameActivity* activity); * as per the Java API at android.view.WindowManager.LayoutParams. */ enum GameActivitySetWindowFlags : uint32_t { - /** - * 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, + /** + * 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, }; /** @@ -529,26 +522,25 @@ enum GameActivitySetWindowFlags : uint32_t { * *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); +void GameActivity_setWindowFlags(GameActivity* activity, uint32_t addFlags, uint32_t removeFlags); /** * Flags for GameActivity_showSoftInput; see the Java InputMethodManager * API for documentation. */ enum GameActivityShowSoftInputFlags : uint8_t { - /** - * 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, + /** + * 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, }; /** @@ -572,16 +564,14 @@ void GameActivity_restartInput(GameActivity* activity); * * Ownership of the state is maintained by the caller. */ -void GameActivity_setTextInputState(GameActivity* activity, - const GameTextInputState* state); +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 GameActivity_getTextInputState(GameActivity* activity, GameTextInputGetStateCallback callback, void* context); /** @@ -594,16 +584,16 @@ GameTextInput* GameActivity_getTextInput(const GameActivity* activity); * API for documentation. */ enum GameActivityHideSoftInputFlags : uint16_t { - /** - * 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, + /** + * 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, }; /** @@ -620,8 +610,7 @@ void GameActivity_hideSoftInput(GameActivity* activity, uint32_t flags); * 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); +void GameActivity_getWindowInsets(GameActivity* activity, GameCommonInsetsType type, ARect* insets); /** * Tells whether the software keyboard is visible or not. @@ -636,8 +625,7 @@ bool GameActivity_isSoftwareKeyboardVisible(GameActivity* activity); * * Note: currently only TYPE_NULL AND TYPE_CLASS_NUMBER are supported. */ -void GameActivity_setImeEditorInfo(GameActivity* activity, - enum GameTextInputType inputType, +void GameActivity_setImeEditorInfo(GameActivity* activity, enum GameTextInputType inputType, enum GameTextInputActionType actionId, enum GameTextInputImeOptions imeOptions); @@ -693,14 +681,14 @@ int GameActivity_getUIMode(GameActivity* activity); * * Refer to Java documentation of locales for more information. */ -int GameActivity_getLocaleLanguage(char* dst, size_t dst_size, - GameActivity* activity, size_t localeIdx); -int GameActivity_getLocaleScript(char* dst, size_t dst_size, - GameActivity* activity, size_t localeIdx); -int GameActivity_getLocaleCountry(char* dst, size_t dst_size, - GameActivity* activity, size_t localeIdx); -int GameActivity_getLocaleVariant(char* dst, size_t dst_size, - GameActivity* activity, size_t localeIdx); +int GameActivity_getLocaleLanguage(char* dst, size_t dst_size, GameActivity* activity, + size_t localeIdx); +int GameActivity_getLocaleScript(char* dst, size_t dst_size, GameActivity* activity, + size_t localeIdx); +int GameActivity_getLocaleCountry(char* dst, size_t dst_size, GameActivity* activity, + size_t localeIdx); +int GameActivity_getLocaleVariant(char* dst, size_t dst_size, GameActivity* activity, + size_t localeIdx); #ifdef __cplusplus } @@ -708,4 +696,4 @@ int GameActivity_getLocaleVariant(char* dst, size_t dst_size, /** @} */ -#endif // ANDROID_GAME_SDK_GAME_ACTIVITY_H +#endif // ANDROID_GAME_SDK_GAME_ACTIVITY_H diff --git a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityEvents.h b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityEvents.h index 2a7982af..64c70e02 100644 --- a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityEvents.h +++ b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityEvents.h @@ -57,29 +57,26 @@ extern "C" { * \see GameActivityMotionEvent */ typedef struct GameActivityPointerAxes { - int32_t id; - int32_t toolType; - float axisValues[GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT]; - float rawX; - float rawY; + 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; +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]; +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]; +inline float GameActivityPointerAxes_getY(const GameActivityPointerAxes* pointerInfo) { + return pointerInfo->axisValues[AMOTION_EVENT_AXIS_Y]; } /** @@ -121,49 +118,35 @@ void GameActivityPointerAxes_disableAxis(int32_t axis); * @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); +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_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_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_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_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_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_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); +inline float GameActivityPointerAxes_getOrientation(const GameActivityPointerAxes* pointerInfo) { + return GameActivityPointerAxes_getAxisValue(pointerInfo, AMOTION_EVENT_AXIS_ORIENTATION); } /** @@ -171,7 +154,7 @@ inline float GameActivityPointerAxes_getOrientation( */ #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 + GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT_OVERRIDE #else #define GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT 8 #endif @@ -183,95 +166,92 @@ inline float GameActivityPointerAxes_getOrientation( * (see https://developer.android.com/reference/android/view/MotionEvent). */ typedef struct GameActivityMotionEvent { - int32_t deviceId; - int32_t source; - int32_t action; + int32_t deviceId; + int32_t source; + int32_t action; - int64_t eventTime; - int64_t downTime; + int64_t eventTime; + int64_t downTime; - int32_t flags; - int32_t metaState; + int32_t flags; + int32_t metaState; - int32_t actionButton; - int32_t buttonState; - int32_t classification; - int32_t edgeFlags; + int32_t actionButton; + int32_t buttonState; + int32_t classification; + int32_t edgeFlags; - uint32_t pointerCount; - GameActivityPointerAxes - pointers[GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT]; + uint32_t pointerCount; + GameActivityPointerAxes pointers[GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT]; - int historySize; - int64_t* historicalEventTimesMillis; - int64_t* historicalEventTimesNanos; - float* historicalAxisValues; + int historySize; + int64_t* historicalEventTimesMillis; + int64_t* historicalEventTimesNanos; + float* historicalAxisValues; - float precisionX; - float precisionY; + float precisionX; + float precisionY; } GameActivityMotionEvent; -float GameActivityMotionEvent_getHistoricalAxisValue( - const GameActivityMotionEvent* event, int axis, int pointerIndex, - int historyPos); +float GameActivityMotionEvent_getHistoricalAxisValue(const GameActivityMotionEvent* event, int axis, + int pointerIndex, int historyPos); -inline int GameActivityMotionEvent_getHistorySize( - const GameActivityMotionEvent* event) { - return event->historySize; +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_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_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_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_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_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_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_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_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); +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. */ @@ -286,21 +266,21 @@ void GameActivityMotionEvent_destroy(GameActivityMotionEvent* c_event); * nanoseconds in this struct. */ typedef struct GameActivityKeyEvent { - int32_t deviceId; - int32_t source; - int32_t action; + int32_t deviceId; + int32_t source; + int32_t action; - int64_t eventTime; - int64_t downTime; + int64_t eventTime; + int64_t downTime; - int32_t flags; - int32_t metaState; + int32_t flags; + int32_t metaState; - int32_t modifiers; - int32_t repeatCount; - int32_t keyCode; - int32_t scanCode; - // int32_t unicodeChar; + int32_t modifiers; + int32_t repeatCount; + int32_t keyCode; + int32_t scanCode; + // int32_t unicodeChar; } GameActivityKeyEvent; #ifdef __cplusplus @@ -309,4 +289,4 @@ typedef struct GameActivityKeyEvent { /** @} */ -#endif // ANDROID_GAME_SDK_GAME_ACTIVITY_EVENTS_H +#endif // ANDROID_GAME_SDK_GAME_ACTIVITY_EVENTS_H diff --git a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityLog.h b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityLog.h index 311b3c4d..c9554b61 100644 --- a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityLog.h +++ b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivityLog.h @@ -25,8 +25,7 @@ #ifdef NDEBUG #define ALOGV(...) #else -#define ALOGV(...) \ - __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__); +#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 @@ -40,36 +39,30 @@ #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)) + __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) +#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__))) +#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__)) +#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) +#define SLOGW_IF(cond, ...) \ + ((__predict_false(cond)) ? ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0) #endif /* @@ -106,4 +99,4 @@ #define LOG_TRACE(...) -#endif // ANDROID_GAME_SDK_GAME_ACTIVITY_LOG_H_ +#endif // ANDROID_GAME_SDK_GAME_ACTIVITY_LOG_H_ diff --git a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h index 6029bc9c..5aa37434 100644 --- a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h +++ b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h @@ -87,54 +87,54 @@ struct android_app; * 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); + /** + * 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; + /** + * 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; }; /** @@ -164,150 +164,150 @@ typedef bool (*android_motion_event_filter)(const GameActivityMotionEvent*); * 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; - - /** The ALooper associated with the app's Java main/UI thread. */ - ALooper* mainLooper; - - /** 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; - - /** - * Whether the software keyboard is visible or not. - */ - bool softwareKeyboardVisible; - - /** - * Last editor action. Valid within APP_CMD_SOFTWARE_KB_VIS_CHANGED handler. - * - * Note: the upstream comment above isn't accurate. - * - `APP_CMD_SOFTWARE_KB_VIS_CHANGED` is associated with `softwareKeyboardVisible` - * changes, not `editorAction`. - * - `APP_CMD_EDITOR_ACTION` is associated with this state but unlike for - * `window` state there's no synchonization that blocks the Java main - * thread, so we can't say that this is only valid within the `APP_CMD_` handler. - */ - int editorAction; - /** - * true when editorAction has been set - */ - bool pendingEditorAction; - - /** - * 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; + /** + * 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; + + /** The ALooper associated with the app's Java main/UI thread. */ + ALooper* mainLooper; + + /** 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; + + /** + * Whether the software keyboard is visible or not. + */ + bool softwareKeyboardVisible; + + /** + * Last editor action. Valid within APP_CMD_SOFTWARE_KB_VIS_CHANGED handler. + * + * Note: the upstream comment above isn't accurate. + * - `APP_CMD_SOFTWARE_KB_VIS_CHANGED` is associated with `softwareKeyboardVisible` + * changes, not `editorAction`. + * - `APP_CMD_EDITOR_ACTION` is associated with this state but unlike for + * `window` state there's no synchonization that blocks the Java main + * thread, so we can't say that this is only valid within the `APP_CMD_` handler. + */ + int editorAction; + /** + * true when editorAction has been set + */ + bool pendingEditorAction; + + /** + * 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 */ + /** + * 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 */ }; /** @@ -315,25 +315,25 @@ struct android_app { * user-defined sources. */ enum NativeAppGlueLooperId : int8_t { - /** - * 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, + /** + * 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, }; /** @@ -343,134 +343,134 @@ enum NativeAppGlueLooperId : int8_t { * can be used for custom user's events. */ enum NativeAppGlueAppCmd : int8_t { - /** - * 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 software keyboard was shown or hidden. - */ - APP_CMD_SOFTWARE_KB_VIS_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, - - /** - * Command from main thread: an editor action has been triggered. - */ - //APP_CMD_EDITOR_ACTION, - - /** - * Command from main thread: a keyboard event has been received. - */ - //APP_CMD_KEY_EVENT, - - /** - * Command from main thread: a touch event has been received. - */ - //APP_CMD_TOUCH_EVENT, + /** + * 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 software keyboard was shown or hidden. + */ + APP_CMD_SOFTWARE_KB_VIS_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, + + /** + * Command from main thread: an editor action has been triggered. + */ + // APP_CMD_EDITOR_ACTION, + + /** + * Command from main thread: a keyboard event has been received. + */ + // APP_CMD_KEY_EVENT, + + /** + * Command from main thread: a touch event has been received. + */ + // APP_CMD_TOUCH_EVENT, }; /** - * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next + * Call when ALooper_pollOnce() returns LOOPER_ID_MAIN, reading the next * app command message. */ int8_t android_app_read_cmd(struct android_app* android_app); @@ -493,8 +493,7 @@ 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); +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 @@ -527,8 +526,7 @@ extern void _rust_glue_entry(struct android_app* app); * * 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); +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. @@ -549,8 +547,10 @@ void android_app_set_motion_event_filter(struct android_app* app, * * Values from 0 to 127 are reserved for this library; values from -128 to -1 * can be used for custom user's events. + * + * The function returns true if the write operation was successful. */ -void android_app_write_cmd(struct android_app* android_app, int8_t cmd); +bool android_app_write_cmd(struct android_app* android_app, int8_t cmd); /** * Determines if a looper wake up was due to new input becoming available diff --git a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivity.cpp b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivity.cpp index 045dbdf7..e4987249 100644 --- a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivity.cpp +++ b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivity.cpp @@ -44,18 +44,20 @@ namespace { +static std::atomic g_methodsRegistered(false); + 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; + 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 +} // anonymous namespace #ifndef NELEM #define NELEM(x) ((int)(sizeof(x) / sizeof((x)[0]))) @@ -65,73 +67,73 @@ struct OwnedGameTextInputState { * JNI methods of the GameActivity Java class. */ static struct { - jmethodID finish; - jmethodID setWindowFlags; - jmethodID getWindowInsets; - jmethodID getWaterfallInsets; - jmethodID setImeEditorInfoFields; + jmethodID finish; + jmethodID setWindowFlags; + jmethodID getWindowInsets; + jmethodID getWaterfallInsets; + jmethodID setImeEditorInfoFields; } gGameActivityClassInfo; /* * JNI methods of the android.os.LocaleList class. */ static struct { - jmethodID size; - jmethodID get; + jmethodID size; + jmethodID get; } gLocaleListClassInfo; /* * JNI methods of the java.util.Locale class. */ static struct { - jmethodID getLanguage; - jmethodID getScript; - jmethodID getCountry; - jmethodID getVariant; + jmethodID getLanguage; + jmethodID getScript; + jmethodID getCountry; + jmethodID getVariant; } gLocaleClassInfo; /* * JNI fields of the androidx.core.graphics.Insets Java class. */ static struct { - jfieldID left; - jfieldID right; - jfieldID top; - jfieldID bottom; + 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; - - jmethodID getLocales; + 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; + + jmethodID getLocales; } gConfigurationClassInfo; /* * JNI methods of the WindowInsetsCompat.Type Java class. */ static struct { - jmethodID methods[GAMECOMMON_INSETS_TYPE_COUNT]; - jclass clazz; + jmethodID methods[GAMECOMMON_INSETS_TYPE_COUNT]; + jclass clazz; } gWindowInsetsCompatTypeClassInfo; /* @@ -139,10 +141,10 @@ static struct { * on the application main thread. */ struct ActivityWork { - int32_t cmd; - int64_t arg1; - int64_t arg2; - int64_t arg3; + int32_t cmd; + int64_t arg1; + int64_t arg2; + int64_t arg3; }; /* @@ -150,24 +152,24 @@ struct ActivityWork { * 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, - CMD_RESTART_INPUT + 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, + CMD_RESTART_INPUT }; /* * A class for locale information, matching Android Java Locale class. */ struct Locale { - std::string language; - std::string script; - std::string country; - std::string variant; + std::string language; + std::string script; + std::string country; + std::string variant; }; /* @@ -175,25 +177,25 @@ struct Locale { * thread, this is why they are made atomic. */ static struct Configuration { - int colorMode; - int densityDpi; - float fontScale; - int fontWeightAdjustment; - int hardKeyboardHidden; - int keyboard; - int keyboardHidden; - int mcc; - int mnc; - int navigation; - int navigationHidden; - int orientation; - int screenHeightDp; - int screenLayout; - int screenWidthDp; - int smallestScreenWidthDp; - int touchscreen; - int uiMode; - std::vector locales; + int colorMode; + int densityDpi; + float fontScale; + int fontWeightAdjustment; + int hardKeyboardHidden; + int keyboard; + int keyboardHidden; + int mcc; + int mnc; + int navigation; + int navigationHidden; + int orientation; + int screenHeightDp; + int screenLayout; + int screenWidthDp; + int smallestScreenWidthDp; + int touchscreen; + int uiMode; + std::vector locales; } gConfiguration; static std::mutex gConfigMutex; @@ -202,1184 +204,1098 @@ static std::mutex gConfigMutex; * 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); +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; - } + int res = write(fd, &work, sizeof(work)); + if (res < 0 && errno == EINTR) { + goto restart; + } - if (res == sizeof(work)) return; + 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); - } + 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; +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; + 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; - softwareKeyboardVisible = false; - sdkVersion = gamesdk::GetSystemPropAsInt("ro.build.version.sdk"); - ALOGD("SDK version: %d", sdkVersion); - } - - ~NativeCode() { - if (callbacks.onDestroy != NULL) { - callbacks.onDestroy(this); - } - if (env != NULL) { - if (javaGameActivity != NULL) { - env->DeleteGlobalRef(javaGameActivity); - } - if (javaAssetManager != NULL) { - env->DeleteGlobalRef(javaAssetManager); - } + NativeCode() { + memset((GameActivity*)this, 0, sizeof(GameActivity)); + memset(&callbacks, 0, sizeof(callbacks)); + memset(&insetsState, 0, sizeof(insetsState)); + nativeWindow = NULL; + mainWorkRead = mainWorkWrite = -1; + gameTextInput = NULL; + softwareKeyboardVisible = false; + sdkVersion = gamesdk::GetSystemPropAsInt("ro.build.version.sdk"); + ALOGD("SDK version: %d", sdkVersion); } - 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); - } + ~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; - void setSurface(jobject _surface) { - if (nativeWindow != NULL) { - ANativeWindow_release(nativeWindow); + setSurface(NULL); + if (mainWorkRead >= 0) close(mainWorkRead); + if (mainWorkWrite >= 0) close(mainWorkWrite); } - if (_surface != NULL) { - nativeWindow = ANativeWindow_fromSurface(env, _surface); - } else { - nativeWindow = NULL; + + void setSurface(jobject _surface) { + if (nativeWindow != NULL) { + ANativeWindow_release(nativeWindow); + } + if (_surface != NULL) { + nativeWindow = ANativeWindow_fromSurface(env, _surface); + } else { + nativeWindow = NULL; + } } - } - GameActivityCallbacks callbacks; + GameActivityCallbacks callbacks; - std::string internalDataPathObj; - std::string externalDataPathObj; - std::string obbPathObj; + std::string internalDataPathObj; + std::string externalDataPathObj; + std::string obbPathObj; - ANativeWindow *nativeWindow; - int32_t lastWindowWidth; - int32_t lastWindowHeight; + 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; + // 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; + // 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; + GameTextInput* gameTextInput; + // Set by users in GameActivity_setTextInputState, then passed to + // GameTextInput. + OwnedGameTextInputState gameTextInputState; + std::mutex gameTextInputStateMutex; - ARect insetsState[GAMECOMMON_INSETS_TYPE_COUNT]; - bool softwareKeyboardVisible; + ARect insetsState[GAMECOMMON_INSETS_TYPE_COUNT]; + bool softwareKeyboardVisible; }; -static void readConfigurationValues(NativeCode *code, jobject javaConfig); +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_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_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_showSoftInput(GameActivity* activity, uint32_t flags) { + NativeCode* code = static_cast(activity); + write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags); } -extern "C" void GameActivity_restartInput(GameActivity *activity) { - NativeCode *code = static_cast(activity); - write_work(code->mainWorkWrite, CMD_RESTART_INPUT); +extern "C" void GameActivity_restartInput(GameActivity* activity) { + NativeCode* code = static_cast(activity); + write_work(code->mainWorkWrite, CMD_RESTART_INPUT); } -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_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_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_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" 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" bool GameActivity_isSoftwareKeyboardVisible(GameActivity *activity) { - NativeCode *code = static_cast(activity); - return code->softwareKeyboardVisible; +extern "C" bool GameActivity_isSoftwareKeyboardVisible(GameActivity* activity) { + NativeCode* code = static_cast(activity); + return code->softwareKeyboardVisible; } -extern "C" GameTextInput *GameActivity_getTextInput( - const GameActivity *activity) { - const NativeCode *code = static_cast(activity); - return code->gameTextInput; +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(); - } +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; - } +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, + static_cast(work.arg1), static_cast(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, + static_cast(work.arg1), static_cast(work.arg2), + static_cast(work.arg3)); + checkAndClearException(code->env, "setImeEditorInfo"); + } break; + case CMD_RESTART_INPUT: { + GameTextInput_restartInput(code->gameTextInput); + } break; + default: + ALOGW("Unknown work command: %d", work.cmd); + break; + } - 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, - static_cast(work.arg1), static_cast(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, - static_cast(work.arg1), static_cast(work.arg2), - static_cast(work.arg3)); - checkAndClearException(code->env, "setImeEditorInfo"); - } break; - case CMD_RESTART_INPUT: { - GameTextInput_restartInput(code->gameTextInput); - } 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; +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); - code = new NativeCode(); + int msgpipe[2]; + if (pipe(msgpipe)) { + g_error_msg = "could not create pipe: "; + g_error_msg += strerror(errno); - 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); + 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); + } - int msgpipe[2]; - if (pipe(msgpipe)) { - g_error_msg = "could not create pipe: "; - g_error_msg += strerror(errno); + // read configuration for the first time + readConfigurationValues(code, javaConfig); - 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); - } - - // read configuration for the first time - readConfigurationValues(code, javaConfig); - - GameActivity_onCreate(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); + GameActivity_onCreate(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 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); +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; + 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); +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; + 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 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 std::string getStringField(JNIEnv *env, jobject obj, jmethodID method) { - jstring str = (jstring)env->CallObjectMethod(obj, method); - const char *chars = env->GetStringUTFChars(str, NULL); - std::string res(chars); - env->ReleaseStringUTFChars(str, chars); - checkAndClearException(env, "getStringField"); - return res; -} - -static void readConfigurationValues(NativeCode *code, jobject javaConfig) { - const std::lock_guard lock(gConfigMutex); - - 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.keyboard = - code->env->GetIntField(javaConfig, gConfigurationClassInfo.keyboard); - gConfiguration.keyboardHidden = code->env->GetIntField( - javaConfig, gConfigurationClassInfo.keyboardHidden); - 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"); - - jobject locales = code->env->CallObjectMethod( - javaConfig, gConfigurationClassInfo.getLocales); - checkAndClearException(code->env, "getLocales"); - - int localesCount = - code->env->CallIntMethod(locales, gLocaleListClassInfo.size); - checkAndClearException(code->env, "size"); - gConfiguration.locales.resize(localesCount); - - // extract the data for every locale - for (int i = 0; i < localesCount; ++i) { - Locale &locale = gConfiguration.locales[i]; - - // get locale object from the array - jobject jniLocale = - code->env->CallObjectMethod(locales, gLocaleListClassInfo.get, i); - checkAndClearException(code->env, "GetObjectArrayElement"); - - // get data strings for this locale - locale.language = - getStringField(code->env, jniLocale, gLocaleClassInfo.getLanguage); - locale.script = - getStringField(code->env, jniLocale, gLocaleClassInfo.getScript); - locale.country = - getStringField(code->env, jniLocale, gLocaleClassInfo.getCountry); - locale.variant = - getStringField(code->env, jniLocale, gLocaleClassInfo.getVariant); - } -} - -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 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 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 std::string getStringField(JNIEnv* env, jobject obj, jmethodID method) { + jstring str = (jstring)env->CallObjectMethod(obj, method); + const char* chars = env->GetStringUTFChars(str, NULL); + std::string res(chars); + env->ReleaseStringUTFChars(str, chars); + checkAndClearException(env, "getStringField"); + return res; } -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 readConfigurationValues(NativeCode* code, jobject javaConfig) { + const std::lock_guard lock(gConfigMutex); + + 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.keyboard = code->env->GetIntField(javaConfig, gConfigurationClassInfo.keyboard); + gConfiguration.keyboardHidden = + code->env->GetIntField(javaConfig, gConfigurationClassInfo.keyboardHidden); + 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"); + + jobject locales = code->env->CallObjectMethod(javaConfig, gConfigurationClassInfo.getLocales); + checkAndClearException(code->env, "getLocales"); + + int localesCount = code->env->CallIntMethod(locales, gLocaleListClassInfo.size); + checkAndClearException(code->env, "size"); + gConfiguration.locales.resize(localesCount); + + // extract the data for every locale + for (int i = 0; i < localesCount; ++i) { + Locale& locale = gConfiguration.locales[i]; + + // get locale object from the array + jobject jniLocale = code->env->CallObjectMethod(locales, gLocaleListClassInfo.get, i); + checkAndClearException(code->env, "GetObjectArrayElement"); + + // get data strings for this locale + locale.language = getStringField(code->env, jniLocale, gLocaleClassInfo.getLanguage); + locale.script = getStringField(code->env, jniLocale, gLocaleClassInfo.getScript); + locale.country = getStringField(code->env, jniLocale, gLocaleClassInfo.getCountry); + locale.variant = getStringField(code->env, jniLocale, gLocaleClassInfo.getVariant); } - } } -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); +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->nativeWindow != NULL && - code->callbacks.onNativeWindowCreated != NULL) { - code->callbacks.onNativeWindowCreated(code, code->nativeWindow); + if (code->callbacks.onConfigurationChanged != NULL) { + code->callbacks.onConfigurationChanged(code); + } } - } -} - -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); +} + +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); + } } - 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); +} + +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); } + } +} - 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); +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); } - } } - // Release the window we acquired earlier. - if (oldNativeWindow != NULL) { - ANativeWindow_release(oldNativeWindow); +} + +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 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); +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); } - code->setSurface(NULL); - } } -extern "C" void GameActivity_setImeEditorInfo( - GameActivity *activity, GameTextInputType inputType, - GameTextInputActionType actionId, GameTextInputImeOptions imeOptions) { - NativeCode *code = static_cast(activity); - write_work(code->mainWorkWrite, CMD_SET_IME_EDITOR_INFO, inputType, actionId, - imeOptions); +extern "C" void GameActivity_setImeEditorInfo(GameActivity* activity, GameTextInputType inputType, + GameTextInputActionType actionId, + GameTextInputImeOptions imeOptions) { + NativeCode* code = static_cast(activity); + write_work(code->mainWorkWrite, CMD_SET_IME_EDITOR_INFO, inputType, actionId, imeOptions); } -extern "C" int GameActivity_getColorMode(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.colorMode; +extern "C" int GameActivity_getColorMode(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.colorMode; } -extern "C" int GameActivity_getDensityDpi(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.densityDpi; +extern "C" int GameActivity_getDensityDpi(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.densityDpi; } -extern "C" float GameActivity_getFontScale(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.fontScale; +extern "C" float GameActivity_getFontScale(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.fontScale; } -extern "C" int GameActivity_getFontWeightAdjustment(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.fontWeightAdjustment; +extern "C" int GameActivity_getFontWeightAdjustment(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.fontWeightAdjustment; } -extern "C" int GameActivity_getHardKeyboardHidden(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.hardKeyboardHidden; +extern "C" int GameActivity_getHardKeyboardHidden(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.hardKeyboardHidden; } -extern "C" int GameActivity_getKeyboard(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.keyboard; +extern "C" int GameActivity_getKeyboard(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.keyboard; } -extern "C" int GameActivity_getKeyboardHidden(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.keyboardHidden; +extern "C" int GameActivity_getKeyboardHidden(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.keyboardHidden; } -extern "C" int GameActivity_getLocalesCount(GameActivity *activity) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.locales.size(); +extern "C" int GameActivity_getLocalesCount(GameActivity* activity) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.locales.size(); } -extern "C" int GameActivity_getMcc(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.mcc; +extern "C" int GameActivity_getMcc(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.mcc; } -extern "C" int GameActivity_getMnc(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.mnc; +extern "C" int GameActivity_getMnc(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.mnc; } -extern "C" int GameActivity_getNavigation(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.navigation; +extern "C" int GameActivity_getNavigation(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.navigation; } -extern "C" int GameActivity_getNavigationHidden(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.navigationHidden; +extern "C" int GameActivity_getNavigationHidden(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.navigationHidden; } -extern "C" int GameActivity_getOrientation(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.orientation; +extern "C" int GameActivity_getOrientation(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.orientation; } -extern "C" int GameActivity_getScreenHeightDp(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.screenHeightDp; +extern "C" int GameActivity_getScreenHeightDp(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.screenHeightDp; } -extern "C" int GameActivity_getScreenLayout(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.screenLayout; +extern "C" int GameActivity_getScreenLayout(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.screenLayout; } -extern "C" int GameActivity_getScreenWidthDp(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.screenWidthDp; +extern "C" int GameActivity_getScreenWidthDp(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.screenWidthDp; } -extern "C" int GameActivity_getSmallestScreenWidthDp(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.smallestScreenWidthDp; +extern "C" int GameActivity_getSmallestScreenWidthDp(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.smallestScreenWidthDp; } -extern "C" int GameActivity_getTouchscreen(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.touchscreen; +extern "C" int GameActivity_getTouchscreen(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.touchscreen; } -extern "C" int GameActivity_getUIMode(GameActivity *) { - const std::lock_guard lock(gConfigMutex); - return gConfiguration.uiMode; +extern "C" int GameActivity_getUIMode(GameActivity*) { + const std::lock_guard lock(gConfigMutex); + return gConfiguration.uiMode; } -static int copyStringIfFits(char *dst, size_t dst_size, const std::string &s) { - if (dst_size <= s.size()) { - return ENOBUFS; - } +static int copyStringIfFits(char* dst, size_t dst_size, const std::string& s) { + if (dst_size <= s.size()) { + return ENOBUFS; + } - strcpy(dst, s.c_str()); - return 0; + strcpy(dst, s.c_str()); + return 0; } -extern "C" int GameActivity_getLocaleLanguage(char *dst, size_t dst_size, - GameActivity *activity, +extern "C" int GameActivity_getLocaleLanguage(char* dst, size_t dst_size, GameActivity* activity, size_t localeIdx) { - const std::lock_guard lock(gConfigMutex); + const std::lock_guard lock(gConfigMutex); - if (localeIdx >= gConfiguration.locales.size()) { - return EINVAL; - } + if (localeIdx >= gConfiguration.locales.size()) { + return EINVAL; + } - return copyStringIfFits(dst, dst_size, - gConfiguration.locales[localeIdx].language); + return copyStringIfFits(dst, dst_size, gConfiguration.locales[localeIdx].language); } -extern "C" int GameActivity_getLocaleScript(char *dst, size_t dst_size, - GameActivity *activity, +extern "C" int GameActivity_getLocaleScript(char* dst, size_t dst_size, GameActivity* activity, size_t localeIdx) { - const std::lock_guard lock(gConfigMutex); + const std::lock_guard lock(gConfigMutex); - if (localeIdx >= gConfiguration.locales.size()) { - return EINVAL; - } + if (localeIdx >= gConfiguration.locales.size()) { + return EINVAL; + } - return copyStringIfFits(dst, dst_size, - gConfiguration.locales[localeIdx].script); + return copyStringIfFits(dst, dst_size, gConfiguration.locales[localeIdx].script); } -extern "C" int GameActivity_getLocaleCountry(char *dst, size_t dst_size, - GameActivity *activity, +extern "C" int GameActivity_getLocaleCountry(char* dst, size_t dst_size, GameActivity* activity, size_t localeIdx) { - const std::lock_guard lock(gConfigMutex); + const std::lock_guard lock(gConfigMutex); - if (localeIdx >= gConfiguration.locales.size()) { - return EINVAL; - } + if (localeIdx >= gConfiguration.locales.size()) { + return EINVAL; + } - return copyStringIfFits(dst, dst_size, - gConfiguration.locales[localeIdx].country); + return copyStringIfFits(dst, dst_size, gConfiguration.locales[localeIdx].country); } -extern "C" int GameActivity_getLocaleVariant(char *dst, size_t dst_size, - GameActivity *activity, +extern "C" int GameActivity_getLocaleVariant(char* dst, size_t dst_size, GameActivity* activity, size_t localeIdx) { - const std::lock_guard lock(gConfigMutex); + const std::lock_guard lock(gConfigMutex); - if (localeIdx >= gConfiguration.locales.size()) { - return EINVAL; - } + if (localeIdx >= gConfiguration.locales.size()) { + return EINVAL; + } - return copyStringIfFits(dst, dst_size, - gConfiguration.locales[localeIdx].variant); + return copyStringIfFits(dst, dst_size, gConfiguration.locales[localeIdx].variant); } -static bool onTouchEvent_native(JNIEnv *env, jobject javaGameActivity, - jlong handle, jobject motionEvent, - int pointerCount, int historySize, int deviceId, - int source, int action, int64_t eventTime, - int64_t downTime, int flags, int metaState, - int actionButton, int buttonState, - int classification, int edgeFlags, +static bool onTouchEvent_native(JNIEnv* env, jobject javaGameActivity, jlong handle, + jobject motionEvent, int pointerCount, int historySize, + int deviceId, int source, int action, int64_t eventTime, + int64_t downTime, int flags, int metaState, int actionButton, + int buttonState, int classification, int edgeFlags, float precisionX, float precisionY) { - if (handle == 0) return false; - NativeCode *code = (NativeCode *)handle; - if (code->callbacks.onTouchEvent == nullptr) return false; + if (handle == 0) return false; + NativeCode* code = (NativeCode*)handle; + if (code->callbacks.onTouchEvent == nullptr) return false; - static GameActivityMotionEvent c_event; + static GameActivityMotionEvent c_event; - c_event.deviceId = deviceId; - c_event.source = source; - c_event.action = action; + c_event.deviceId = deviceId; + c_event.source = source; + c_event.action = action; - c_event.eventTime = eventTime; - c_event.downTime = downTime; + c_event.eventTime = eventTime; + c_event.downTime = downTime; - c_event.flags = flags; - c_event.metaState = metaState; + c_event.flags = flags; + c_event.metaState = metaState; - c_event.actionButton = actionButton; - c_event.buttonState = buttonState; - c_event.classification = classification; - c_event.edgeFlags = edgeFlags; + c_event.actionButton = actionButton; + c_event.buttonState = buttonState; + c_event.classification = classification; + c_event.edgeFlags = edgeFlags; - c_event.precisionX = precisionX; - c_event.precisionY = precisionY; + c_event.precisionX = precisionX; + c_event.precisionY = precisionY; - GameActivityMotionEvent_fromJava(env, motionEvent, &c_event, pointerCount, - historySize); - return code->callbacks.onTouchEvent(code, &c_event); + GameActivityMotionEvent_fromJava(env, motionEvent, &c_event, pointerCount, historySize); + 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 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 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 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 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, +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); + 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); + 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 void onSoftwareKeyboardVisibilityChangedNative_native(JNIEnv *env, - jobject activity, - jlong handle, - bool visible) { - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; - code->softwareKeyboardVisible = visible; +static void onSoftwareKeyboardVisibilityChangedNative_native(JNIEnv* env, jobject activity, + jlong handle, bool visible) { + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + code->softwareKeyboardVisible = visible; - if (code->callbacks.onSoftwareKeyboardVisibilityChanged != nullptr) { - code->callbacks.onSoftwareKeyboardVisibilityChanged(code, visible); + if (code->callbacks.onSoftwareKeyboardVisibilityChanged != nullptr) { + code->callbacks.onSoftwareKeyboardVisibilityChanged(code, visible); + } } - } } -static void onEditorActionNative_native(JNIEnv *env, jobject activity, - jlong handle, int action) { - if (handle != 0) { - NativeCode *code = (NativeCode *)handle; +static void onEditorActionNative_native(JNIEnv* env, jobject activity, jlong handle, int action) { + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; - if (code->callbacks.onEditorAction != nullptr) { - code->callbacks.onEditorAction(code, action); + if (code->callbacks.onEditorAction != nullptr) { + code->callbacks.onEditorAction(code, action); + } } - } } 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;IIIIIJJIIIIIIFF)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}, - {"onSoftwareKeyboardVisibilityChangedNative", "(JZ)V", - (void *)onSoftwareKeyboardVisibilityChangedNative_native}, - {"onEditorActionNative", "(JI)V", (void *)onEditorActionNative_native}, + {"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;IIIIIJJIIIIIIFF)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}, + {"onSoftwareKeyboardVisibilityChangedNative", "(JZ)V", + (void*)onSoftwareKeyboardVisibilityChangedNative_native}, + {"onEditorActionNative", "(JI)V", (void*)onEditorActionNative_native}, }; -static const char *const kGameActivityPathName = - "com/google/androidgamesdk/GameActivity"; +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"; -static const char *const kLocaleListPathName = "android/os/LocaleList"; -static const char *const kLocalePathName = "java/util/Locale"; +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"; +static const char* const kLocaleListPathName = "android/os/LocaleList"; +static const char* const kLocalePathName = "java/util/Locale"; -#define FIND_CLASS(var, className) \ - var = env->FindClass(className); \ - LOG_FATAL_IF(!var, "Unable to find class %s", className); +#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_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"); - - GET_METHOD_ID(gConfigurationClassInfo.getLocales, configuration_class, - "getLocales", "()Landroid/os/LocaleList;"); - - jclass localeListClass; - FIND_CLASS(localeListClass, kLocaleListPathName); - GET_METHOD_ID(gLocaleListClassInfo.size, localeListClass, "size", "()I"); - GET_METHOD_ID(gLocaleListClassInfo.get, localeListClass, "get", - "(I)Ljava/util/Locale;"); - - jclass localeClass; - FIND_CLASS(localeClass, kLocalePathName); - GET_METHOD_ID(gLocaleClassInfo.getLanguage, localeClass, "getLanguage", - "()Ljava/lang/String;"); - GET_METHOD_ID(gLocaleClassInfo.getScript, localeClass, "getScript", - "()Ljava/lang/String;"); - GET_METHOD_ID(gLocaleClassInfo.getCountry, localeClass, "getCountry", - "()Ljava/lang/String;"); - GET_METHOD_ID(gLocaleClassInfo.getVariant, localeClass, "getVariant", - "()Ljava/lang/String;"); - - 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"); - } - - GameActivityEventsInit(env); - - return jniRegisterNativeMethods(env, kGameActivityPathName, g_methods, - NELEM(g_methods)); + 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) { + if (g_methodsRegistered.exchange(true)) { + // Already registered, do nothing. + return JNI_OK; + } + + 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"); + + GET_METHOD_ID(gConfigurationClassInfo.getLocales, configuration_class, "getLocales", + "()Landroid/os/LocaleList;"); + + jclass localeListClass; + FIND_CLASS(localeListClass, kLocaleListPathName); + GET_METHOD_ID(gLocaleListClassInfo.size, localeListClass, "size", "()I"); + GET_METHOD_ID(gLocaleListClassInfo.get, localeListClass, "get", "(I)Ljava/util/Locale;"); + + jclass localeClass; + FIND_CLASS(localeClass, kLocalePathName); + GET_METHOD_ID(gLocaleClassInfo.getLanguage, localeClass, "getLanguage", "()Ljava/lang/String;"); + GET_METHOD_ID(gLocaleClassInfo.getScript, localeClass, "getScript", "()Ljava/lang/String;"); + GET_METHOD_ID(gLocaleClassInfo.getCountry, localeClass, "getCountry", "()Ljava/lang/String;"); + GET_METHOD_ID(gLocaleClassInfo.getVariant, localeClass, "getVariant", "()Ljava/lang/String;"); + + 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"); + } + + GameActivityEventsInit(env); + + return jniRegisterNativeMethods(env, kGameActivityPathName, g_methods, NELEM(g_methods)); } // XXX: This symbol is renamed with a _C suffix and then re-exported from @@ -1388,12 +1304,22 @@ extern "C" int GameActivity_register(JNIEnv *env) { // 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; + 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; +} + +extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) __attribute__((weak)); + +extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + return JNI_ERR; + } + int result = GameActivity_register(env); + return (result == 0) ? JNI_VERSION_1_6 : -1; } diff --git a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivityEvents.cpp b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivityEvents.cpp index 5e238c6c..f88a64e2 100644 --- a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivityEvents.cpp +++ b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivityEvents.cpp @@ -24,312 +24,281 @@ #include "system_utils.h" 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}; + /* 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; - } + if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) { + return; + } - enabledAxes[axis] = true; + enabledAxes[axis] = true; } -float GameActivityPointerAxes_getAxisValue( - const GameActivityPointerAxes *pointerInfo, int32_t axis) { - if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) { - return 0; - } +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; - } + if (!enabledAxes[axis]) { + ALOGW("Axis %d must be enabled before it can be accessed.", axis); + return 0; + } - return pointerInfo->axisValues[axis]; + return pointerInfo->axisValues[axis]; } extern "C" void GameActivityPointerAxes_disableAxis(int32_t axis) { - if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) { - return; - } + if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) { + return; + } - enabledAxes[axis] = false; + 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]; +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 getDeviceId; + jmethodID getSource; + jmethodID getAction; - jmethodID getEventTime; - jmethodID getDownTime; + jmethodID getEventTime; + jmethodID getDownTime; - jmethodID getFlags; - jmethodID getMetaState; + jmethodID getFlags; + jmethodID getMetaState; - jmethodID getActionButton; - jmethodID getButtonState; - jmethodID getClassification; - jmethodID getEdgeFlags; + jmethodID getActionButton; + jmethodID getButtonState; + jmethodID getClassification; + jmethodID getEdgeFlags; - jmethodID getHistorySize; - jmethodID getHistoricalEventTime; + jmethodID getHistorySize; + jmethodID getHistoricalEventTime; - jmethodID getPointerCount; - jmethodID getPointerId; + jmethodID getPointerCount; + jmethodID getPointerId; - jmethodID getToolType; + jmethodID getToolType; - jmethodID getRawX; - jmethodID getRawY; - jmethodID getXPrecision; - jmethodID getYPrecision; - jmethodID getAxisValue; + jmethodID getRawX; + jmethodID getRawY; + jmethodID getXPrecision; + jmethodID getYPrecision; + jmethodID getAxisValue; - jmethodID getHistoricalAxisValue; + 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_destroy(GameActivityMotionEvent* c_event) { + delete c_event->historicalAxisValues; + delete c_event->historicalEventTimesMillis; + delete c_event->historicalEventTimesNanos; } -static void initMotionEvents(JNIEnv *env) { - int sdkVersion = gamesdk::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"); +static void initMotionEvents(JNIEnv* env) { + int sdkVersion = gamesdk::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"); } -extern "C" void GameActivityMotionEvent_fromJava( - JNIEnv *env, jobject motionEvent, GameActivityMotionEvent *out_event, - int pointerCount, int historySize) { - 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); - } - } - } - - 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; +extern "C" void GameActivityMotionEvent_fromJava(JNIEnv* env, jobject motionEvent, + GameActivityMotionEvent* out_event, + int pointerCount, int historySize) { + pointerCount = std::min(pointerCount, GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT); + out_event->pointerCount = pointerCount; 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->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); + } + } + } + + 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); + } + } } - } } - } } static struct { - jmethodID getDeviceId; - jmethodID getSource; - jmethodID getAction; + jmethodID getDeviceId; + jmethodID getSource; + jmethodID getAction; - jmethodID getEventTime; - jmethodID getDownTime; + jmethodID getEventTime; + jmethodID getDownTime; - jmethodID getFlags; - jmethodID getMetaState; + jmethodID getFlags; + jmethodID getMetaState; - jmethodID getModifiers; - jmethodID getRepeatCount; - jmethodID getKeyCode; - jmethodID getScanCode; - // jmethodID getUnicodeChar; + jmethodID getModifiers; + jmethodID getRepeatCount; + jmethodID getKeyCode; + jmethodID getScanCode; + // jmethodID getUnicodeChar; } gKeyEventClassInfo; -static void initKeyEvents(JNIEnv *env) { - int sdkVersion = gamesdk::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"); +static void initKeyEvents(JNIEnv* env) { + int sdkVersion = gamesdk::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"); } -extern "C" void GameActivityKeyEvent_fromJava(JNIEnv *env, jobject keyEvent, - GameActivityKeyEvent *out_event) { - *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) - }; +extern "C" void GameActivityKeyEvent_fromJava(JNIEnv* env, jobject keyEvent, + GameActivityKeyEvent* out_event) { + *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) + }; } -extern "C" void GameActivityEventsInit(JNIEnv *env) { - initMotionEvents(env); - initKeyEvents(env); +extern "C" void GameActivityEventsInit(JNIEnv* env) { + initMotionEvents(env); + initKeyEvents(env); } diff --git a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivityEvents_internal.h b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivityEvents_internal.h index 202be8b1..bcd0d537 100644 --- a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivityEvents_internal.h +++ b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivityEvents_internal.h @@ -53,8 +53,8 @@ void GameActivityEventsInit(JNIEnv* env); * to avoid extra JNI calls. */ void GameActivityMotionEvent_fromJava(JNIEnv* env, jobject motionEvent, - GameActivityMotionEvent* out_event, - int pointerCount, int historySize); + GameActivityMotionEvent* out_event, int pointerCount, + int historySize); /** * \brief Convert a Java `KeyEvent` to a `GameActivityKeyEvent`. @@ -74,4 +74,4 @@ void GameActivityKeyEvent_fromJava(JNIEnv* env, jobject motionEvent, /** @} */ -#endif // ANDROID_GAME_SDK_GAME_ACTIVITY_EVENTS_INTERNAL_H +#endif // ANDROID_GAME_SDK_GAME_ACTIVITY_EVENTS_INTERNAL_H diff --git a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c index 988287f5..27a68f4a 100644 --- a/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c +++ b/android-activity/android-games-sdk/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c @@ -27,217 +27,208 @@ #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) +#define NATIVE_APP_GLUE_CMD_WAIT_TIMEOUT_SECONDS 2 + +#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__)) +#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); + 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; + 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)); + 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; - } + 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; - } + 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); + 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. + 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); +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); - } + 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; + 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; + 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); + 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); + _rust_glue_entry(android_app); - android_app_destroy(android_app); - return NULL; + android_app_destroy(android_app); + return NULL; } // Codes from https://developer.android.com/reference/android/view/KeyEvent @@ -250,13 +241,13 @@ static void* android_app_entry(void* param) { // 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); + // 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 @@ -264,526 +255,518 @@ static bool default_key_filter(const GameActivityKeyEvent* event) { #define SOURCE_TOUCHSCREEN 0x00001002 static bool default_motion_filter(const GameActivityMotionEvent* event) { - // Ignore any non-touch events. - return (event->source & SOURCE_TOUCHSCREEN) != 0; + // Ignore any non-touch events. + return (event->source & SOURCE_TOUCHSCREEN) != 0; } // -------------------------------------------------------------------- // Native activity interaction (called from main thread) // -------------------------------------------------------------------- -static struct android_app* android_app_create(GameActivity* activity, - void* savedState, +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); - } - - android_app->mainLooper = ALooper_forThread(); - if (android_app->mainLooper == NULL) { - LOGE("Failed to get main looper"); - return NULL; - } - ALooper_acquire(android_app->mainLooper); - - 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; + // 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); + } - 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); + android_app->mainLooper = ALooper_forThread(); + if (android_app->mainLooper == NULL) { + LOGE("Failed to get main looper"); + return NULL; + } + ALooper_acquire(android_app->mainLooper); - // 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); + 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; + return android_app; } -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)); - } +bool 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)); + return false; + } + return true; } -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); +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) { + // 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) { + pthread_mutex_unlock(&android_app->mutex); + return; + } + if (android_app_write_cmd(android_app, cmd)) { + struct timespec timeout; + clock_gettime(CLOCK_REALTIME, &timeout); + timeout.tv_sec += NATIVE_APP_GLUE_CMD_WAIT_TIMEOUT_SECONDS; + + int wait_result = 0; + while (android_app->activityState != cmd) { + wait_result = pthread_cond_timedwait(&android_app->cond, &android_app->mutex, &timeout); + if (wait_result == ETIMEDOUT) { + LOGE("android_app_set_activity_state timed out waiting for cmd %d", cmd); + break; + } + } + } 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; + int input_buf_idx = 0; - pthread_mutex_lock(&android_app->mutex); + 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 + // 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); + 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]; + 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); - } + android_app_clear_motion_events(buf); + free(buf->motionEvents); + free(buf->keyEvents); + } - close(android_app->msgread); - close(android_app->msgwrite); + close(android_app->msgread); + close(android_app->msgwrite); - pthread_cond_destroy(&android_app->cond); - pthread_mutex_destroy(&android_app->mutex); + pthread_cond_destroy(&android_app->cond); + pthread_mutex_destroy(&android_app->mutex); - if (android_app->mainLooper != NULL) { - ALooper_release(android_app->mainLooper); - } + if (android_app->mainLooper != NULL) { + ALooper_release(android_app->mainLooper); + } - free(android_app); + free(android_app); } static inline struct android_app* ToApp(GameActivity* activity) { - return (struct android_app*)activity->instance; + return (struct android_app*)activity->instance; } static void onDestroy(GameActivity* activity) { - LOGV("Destroy: %p", activity); - android_app_free(ToApp(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); + 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); + LOGV("Resume: %p", activity); + android_app_set_activity_state(ToApp(activity), APP_CMD_RESUME); } -static void onSaveInstanceState(GameActivity* activity, - SaveInstanceStateRecallback recallback, +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; - } + 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); - } + 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; - } + 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); + 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); + 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); + 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); + 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); + 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); + 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 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 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 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); +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); + 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; + 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; - } + // Don't spam the mainloop with wake ups if we've already sent one + if (android_app->inputSwapPending) { + return; + } - if (android_app->motionEventFilter != NULL && - !android_app->motionEventFilter(event)) { - pthread_mutex_unlock(&android_app->mutex); - return false; - } + 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); + } +} - struct android_input_buffer* inputBuffer = - &android_app->inputBuffers[android_app->currentInputBuffer]; +static bool onTouchEvent(GameActivity* activity, const GameActivityMotionEvent* event) { + struct android_app* android_app = ToApp(activity); + pthread_mutex_lock(&android_app->mutex); - // 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); + // 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 (inputBuffer->motionEvents == NULL) { - LOGE("onTouchEvent: out of memory"); - abort(); + if (android_app->motionEventFilter != NULL && !android_app->motionEventFilter(event)) { + pthread_mutex_unlock(&android_app->mutex); + return false; } - } - int new_ix = inputBuffer->motionEventsCount; - memcpy(&inputBuffer->motionEvents[new_ix], event, - sizeof(GameActivityMotionEvent)); - ++inputBuffer->motionEventsCount; - notifyInput(android_app); + 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); - //android_app_write_cmd(android_app, APP_CMD_TOUCH_EVENT); - pthread_mutex_unlock(&android_app->mutex); - return true; + // android_app_write_cmd(android_app, APP_CMD_TOUCH_EVENT); + 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* 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]; + 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; - } + 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; + android_app->inputSwapPending = false; + android_app->inputAvailableWakeUp = false; - pthread_mutex_unlock(&android_app->mutex); + pthread_mutex_unlock(&android_app->mutex); - return inputBuffer; + 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]); + // 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); + 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); +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]; + 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; + } - // 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 (android_app->keyEventFilter != NULL && !android_app->keyEventFilter(event)) { + pthread_mutex_unlock(&android_app->mutex); + return false; + } - if (inputBuffer->keyEvents == NULL) { - LOGE("onKey: out of memory"); - abort(); + 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); + int new_ix = inputBuffer->keyEventsCount; + memcpy(&inputBuffer->keyEvents[new_ix], event, sizeof(GameActivityKeyEvent)); + ++inputBuffer->keyEventsCount; + notifyInput(android_app); - //android_app_write_cmd(android_app, APP_CMD_KEY_EVENT); - pthread_mutex_unlock(&android_app->mutex); - return true; + // android_app_write_cmd(android_app, APP_CMD_KEY_EVENT); + pthread_mutex_unlock(&android_app->mutex); + return true; } void android_app_clear_key_events(struct android_input_buffer* inputBuffer) { - inputBuffer->keyEventsCount = 0; + 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 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); + 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); + LOGV("ContentRectChanged: %p -- (%d %d) (%d %d)", activity, rect->left, rect->top, rect->right, + rect->bottom); - struct android_app* android_app = ToApp(activity); + struct android_app* android_app = ToApp(activity); - pthread_mutex_lock(&android_app->mutex); - android_app->contentRect = *rect; + 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); + android_app_write_cmd(android_app, APP_CMD_CONTENT_RECT_CHANGED); + pthread_mutex_unlock(&android_app->mutex); } -static void onSoftwareKeyboardVisibilityChanged(GameActivity* activity, - bool visible) { - LOGV("SoftwareKeyboardVisibilityChanged: %p -- %d", activity, (int)visible); +static void onSoftwareKeyboardVisibilityChanged(GameActivity* activity, bool visible) { + LOGV("SoftwareKeyboardVisibilityChanged: %p -- %d", activity, (int)visible); - struct android_app* android_app = ToApp(activity); + struct android_app* android_app = ToApp(activity); - pthread_mutex_lock(&android_app->mutex); - android_app->softwareKeyboardVisible = visible; + pthread_mutex_lock(&android_app->mutex); + android_app->softwareKeyboardVisible = visible; - android_app_write_cmd(android_app, APP_CMD_SOFTWARE_KB_VIS_CHANGED); - pthread_mutex_unlock(&android_app->mutex); + android_app_write_cmd(android_app, APP_CMD_SOFTWARE_KB_VIS_CHANGED); + pthread_mutex_unlock(&android_app->mutex); } static bool onEditorAction(GameActivity* activity, int action) { - LOGV("EditorAction: %p -- %d", activity, action); + LOGV("EditorAction: %p -- %d", activity, action); - struct android_app* android_app = ToApp(activity); + struct android_app* android_app = ToApp(activity); - pthread_mutex_lock(&android_app->mutex); + pthread_mutex_lock(&android_app->mutex); - // XXX: this is a racy design that could lose InputConnection actions if the - // application doesn't manage to look at app->editorAction before another - // action is delivered. - if (android_app->pendingEditorAction) { - LOGW("Dropping editor action %d because previous action %d not yet " - "handled", - action, android_app->editorAction); - } - android_app->editorAction = action; - android_app->pendingEditorAction = true; - notifyInput(android_app); - // TODO: buffer IME text events and editor actions like other input events + // XXX: this is a racy design that could lose InputConnection actions if the + // application doesn't manage to look at app->editorAction before another + // action is delivered. + if (android_app->pendingEditorAction) { + LOGW("Dropping editor action %d because previous action %d not yet " + "handled", + action, android_app->editorAction); + } + android_app->editorAction = action; + android_app->pendingEditorAction = true; + notifyInput(android_app); + // TODO: buffer IME text events and editor actions like other input events - //android_app_write_cmd(android_app, APP_CMD_EDITOR_ACTION); - pthread_mutex_unlock(&android_app->mutex); - return true; + // android_app_write_cmd(android_app, APP_CMD_EDITOR_ACTION); + pthread_mutex_unlock(&android_app->mutex); + return true; } // XXX: This symbol is renamed with a _C suffix so we can implement // `GameActivity_onCreate` as a wrapper in Rust that does some additional setup // before calling this function, 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; - activity->callbacks->onSoftwareKeyboardVisibilityChanged = - onSoftwareKeyboardVisibilityChanged; - activity->callbacks->onEditorAction = onEditorAction; - LOGV("Callbacks set: %p", activity->callbacks); - - activity->instance = android_app_create(activity, savedState, savedStateSize); +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; + activity->callbacks->onSoftwareKeyboardVisibilityChanged = onSoftwareKeyboardVisibilityChanged; + activity->callbacks->onEditorAction = onEditorAction; + LOGV("Callbacks set: %p", activity->callbacks); + + activity->instance = android_app_create(activity, savedState, savedStateSize); } diff --git a/android-activity/android-games-sdk/game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h b/android-activity/android-games-sdk/game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h index 28645446..e54bc22a 100644 --- a/android-activity/android-games-sdk/game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h +++ b/android-activity/android-games-sdk/game-text-input/prefab-src/modules/game-text-input/include/game-text-input/gametextinput.h @@ -32,13 +32,13 @@ extern "C" { #endif +#define GAMETEXTINPUT_VERSION_REVISION 7cd950d0022d01f1e7e2b470aba5a7b1abacdfaa #define GAMETEXTINPUT_MAJOR_VERSION 4 -#define GAMETEXTINPUT_MINOR_VERSION 0 +#define GAMETEXTINPUT_MINOR_VERSION 3 #define GAMETEXTINPUT_BUGFIX_VERSION 0 -#define GAMETEXTINPUT_PACKED_VERSION \ - ANDROID_GAMESDK_PACKED_VERSION(GAMETEXTINPUT_MAJOR_VERSION, \ - GAMETEXTINPUT_MINOR_VERSION, \ - GAMETEXTINPUT_BUGFIX_VERSION) +#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 @@ -46,10 +46,10 @@ extern "C" { * 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; + /** The start of the region (inclusive). */ + int32_t start; + /** The end of the region (exclusive). */ + int32_t end; } GameTextInputSpan; /** @@ -65,23 +65,23 @@ enum GameTextInputSpanFlag : int32_t { SPAN_UNDEFINED = -1 }; * 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; + /** + * 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; /** @@ -90,8 +90,8 @@ typedef struct GameTextInputState { * @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); +typedef void (*GameTextInputGetStateCallback)(void* context, + const struct GameTextInputState* state); /** * Opaque handle to the GameTextInput API. @@ -102,13 +102,14 @@ 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 env A JNI env valid on the calling thread. All other calls to the resulting GameTextInput + * object must be done on the same 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); +GameTextInput* GameTextInput_init(JNIEnv* env, uint32_t max_string_size); /** * When using GameTextInput, you need to create a gametextinput.InputConnection @@ -118,8 +119,7 @@ GameTextInput *GameTextInput_init(JNIEnv *env, uint32_t max_string_size); * @param input A valid GameTextInput library handle. * @param inputConnection A gametextinput.InputConnection object. */ -void GameTextInput_setInputConnection(GameTextInput *input, - jobject inputConnection); +void GameTextInput_setInputConnection(GameTextInput* input, jobject inputConnection); /** * Unless using GameActivity, it is required to call this function from your @@ -129,7 +129,7 @@ void GameTextInput_setInputConnection(GameTextInput *input, * @param input A valid GameTextInput library handle. * @param eventState A Java gametextinput.State object. */ -void GameTextInput_processEvent(GameTextInput *input, jobject eventState); +void GameTextInput_processEvent(GameTextInput* input, jobject eventState); /** * Free any resources owned by the GameTextInput library. @@ -137,19 +137,18 @@ void GameTextInput_processEvent(GameTextInput *input, jobject eventState); * called again. * @param input A valid GameTextInput library handle. */ -void GameTextInput_destroy(GameTextInput *input); +void GameTextInput_destroy(GameTextInput* input); /** * Flags to be passed to GameTextInput_showIme. */ enum ShowImeFlags : uint32_t { - 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_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. }; /** @@ -158,19 +157,17 @@ enum ShowImeFlags : uint32_t { * @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); +void GameTextInput_showIme(GameTextInput* input, uint32_t flags); /** * Flags to be passed to GameTextInput_hideIme. */ enum HideImeFlags : uint32_t { - 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. + 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. }; /** @@ -179,13 +176,13 @@ enum HideImeFlags : uint32_t { * @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); +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); +void GameTextInput_restartInput(GameTextInput* input); /** * Call a callback with the current GameTextInput state, which may have been @@ -197,9 +194,8 @@ void GameTextInput_restartInput(GameTextInput *input); * @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); +void GameTextInput_getState(GameTextInput* input, GameTextInputGetStateCallback callback, + void* context); /** * Set the current GameTextInput state. This state is reflected to any active @@ -208,8 +204,7 @@ void GameTextInput_getState(GameTextInput *input, * @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); +void GameTextInput_setState(GameTextInput* input, const GameTextInputState* state); /** * Type of the callback needed by GameTextInput_setEventCallback that will be @@ -218,8 +213,7 @@ void GameTextInput_setState(GameTextInput *input, * @param current_state Current IME state, owned by the library and valid during * the callback. */ -typedef void (*GameTextInputEventCallback)( - void *context, const GameTextInputState *current_state); +typedef void (*GameTextInputEventCallback)(void* context, const GameTextInputState* current_state); /** * Optionally set a callback to be called whenever the IME state changes. @@ -232,9 +226,8 @@ typedef void (*GameTextInputEventCallback)( * the callback other than copying the state variable. Using any synchronization * primitives inside this callback may cause a deadlock. */ -void GameTextInput_setEventCallback(GameTextInput *input, - GameTextInputEventCallback callback, - void *context); +void GameTextInput_setEventCallback(GameTextInput* input, GameTextInputEventCallback callback, + void* context); /** * Type of the callback needed by GameTextInput_setImeInsetsCallback that will @@ -244,8 +237,7 @@ void GameTextInput_setEventCallback(GameTextInput *input, * @param current_insets Current IME insets, owned by the library and valid * during the callback. */ -typedef void (*GameTextInputImeInsetsCallback)(void *context, - const ARect *current_insets); +typedef void (*GameTextInputImeInsetsCallback)(void* context, const ARect* current_insets); /** * Optionally set a callback to be called whenever the IME insets change. @@ -255,16 +247,15 @@ typedef void (*GameTextInputImeInsetsCallback)(void *context, * @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); +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); +void GameTextInput_getImeInsets(const GameTextInput* input, ARect* insets); /** * Unless using GameActivity, it is required to call this function from your @@ -274,7 +265,7 @@ void GameTextInput_getImeInsets(const GameTextInput *input, ARect *insets); * @param input A valid GameTextInput library handle. * @param eventState A Java gametextinput.State object. */ -void GameTextInput_processImeInsets(GameTextInput *input, const ARect *insets); +void GameTextInput_processImeInsets(GameTextInput* input, const ARect* insets); /** * Convert a GameTextInputState struct to a Java gametextinput.State object. @@ -284,8 +275,7 @@ void GameTextInput_processImeInsets(GameTextInput *input, const ARect *insets); * @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); +jobject GameTextInputState_toJava(const GameTextInput* input, const GameTextInputState* state); /** * Convert from a Java gametextinput.State object into a C GameTextInputState @@ -296,9 +286,8 @@ jobject GameTextInputState_toJava(const GameTextInput *input, * of the call. * @param context Context passed to the callback. */ -void GameTextInputState_fromJava(const GameTextInput *input, jobject state, - GameTextInputGetStateCallback callback, - void *context); +void GameTextInputState_fromJava(const GameTextInput* input, jobject state, + GameTextInputGetStateCallback callback, void* context); /** * Definitions for inputType argument of GameActivity_setImeEditorInfo() @@ -350,354 +339,354 @@ void GameTextInputState_fromJava(const GameTextInput *input, jobject state, */ enum GameTextInputType : uint32_t { - /** - * Mask of bits that determine the overall class - * of text being given. Currently supported classes are: - * {@link #TYPE_CLASS_TEXT}, {@link #TYPE_CLASS_NUMBER}, - * {@link #TYPE_CLASS_PHONE}, {@link #TYPE_CLASS_DATETIME}. - *

IME authors: If the class is not one you - * understand, assume {@link #TYPE_CLASS_TEXT} with NO variation - * or flags.

- */ - TYPE_MASK_CLASS = 0x0000000f, - - /** - * Mask of bits that determine the variation of - * the base content class. - */ - TYPE_MASK_VARIATION = 0x00000ff0, - - /** - * Mask of bits that provide addition bit flags - * of options. - */ - TYPE_MASK_FLAGS = 0x00fff000, - - /** - * Special content type for when no explicit type has been specified. - * This should be interpreted to mean that the target input connection - * is not rich, it can not process and show things like candidate text nor - * retrieve the current text, so the input method will need to run in a - * limited "generate key events" mode, if it supports it. Note that some - * input methods may not support it, for example a voice-based input - * method will likely not be able to generate key events even if this - * flag is set. - */ - TYPE_NULL = 0x00000000, - - // ---------------------------------------------------------------------- - - /** - * Class for normal text. This class supports the following flags (only - * one of which should be set): - * {@link #TYPE_TEXT_FLAG_CAP_CHARACTERS}, - * {@link #TYPE_TEXT_FLAG_CAP_WORDS}, and. - * {@link #TYPE_TEXT_FLAG_CAP_SENTENCES}. It also supports the - * following variations: - * {@link #TYPE_TEXT_VARIATION_NORMAL}, and - * {@link #TYPE_TEXT_VARIATION_URI}. If you do not recognize the - * variation, normal should be assumed. - */ - TYPE_CLASS_TEXT = 0x00000001, - - /** - * Flag for {@link #TYPE_CLASS_TEXT}: capitalize all characters. Overrides - * {@link #TYPE_TEXT_FLAG_CAP_WORDS} and - * {@link #TYPE_TEXT_FLAG_CAP_SENTENCES}. This value is explicitly defined - * to be the same as {@link TextUtils#CAP_MODE_CHARACTERS}. Of course, - * this only affects languages where there are upper-case and lower-case - * letters. - */ - TYPE_TEXT_FLAG_CAP_CHARACTERS = 0x00001000, - - /** - * Flag for {@link #TYPE_CLASS_TEXT}: capitalize the first character of - * every word. Overrides {@link #TYPE_TEXT_FLAG_CAP_SENTENCES}. This - * value is explicitly defined - * to be the same as {@link TextUtils#CAP_MODE_WORDS}. Of course, - * this only affects languages where there are upper-case and lower-case - * letters. - */ - TYPE_TEXT_FLAG_CAP_WORDS = 0x00002000, - - /** - * Flag for {@link #TYPE_CLASS_TEXT}: capitalize the first character of - * each sentence. This value is explicitly defined - * to be the same as {@link TextUtils#CAP_MODE_SENTENCES}. For example - * in English it means to capitalize after a period and a space (note that - * other languages may have different characters for period, or not use - * spaces, or use different grammatical rules). Of course, this only affects - * languages where there are upper-case and lower-case letters. - */ - TYPE_TEXT_FLAG_CAP_SENTENCES = 0x00004000, - - /** - * Flag for {@link #TYPE_CLASS_TEXT}: the user is entering free-form - * text that should have auto-correction applied to it. Without this flag, - * the IME will not try to correct typos. You should always set this flag - * unless you really expect users to type non-words in this field, for - * example to choose a name for a character in a game. - * Contrast this with {@link #TYPE_TEXT_FLAG_AUTO_COMPLETE} and - * {@link #TYPE_TEXT_FLAG_NO_SUGGESTIONS}: - * {@code TYPE_TEXT_FLAG_AUTO_CORRECT} means that the IME will try to - * auto-correct typos as the user is typing, but does not define whether - * the IME offers an interface to show suggestions. - */ - TYPE_TEXT_FLAG_AUTO_CORRECT = 0x00008000, - - /** - * Flag for {@link #TYPE_CLASS_TEXT}: the text editor (which means - * the application) is performing auto-completion of the text being entered - * based on its own semantics, which it will present to the user as they type. - * This generally means that the input method should not be showing - * candidates itself, but can expect the editor to supply its own - * completions/candidates from - * {@link android.view.inputmethod.InputMethodSession#displayCompletions - * InputMethodSession.displayCompletions()} as a result of the editor calling - * {@link android.view.inputmethod.InputMethodManager#displayCompletions - * InputMethodManager.displayCompletions()}. - * Note the contrast with {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} and - * {@link #TYPE_TEXT_FLAG_NO_SUGGESTIONS}: - * {@code TYPE_TEXT_FLAG_AUTO_COMPLETE} means the editor should show an - * interface for displaying suggestions, but instead of supplying its own - * it will rely on the Editor to pass completions/corrections. - */ - TYPE_TEXT_FLAG_AUTO_COMPLETE = 0x00010000, - - /** - * Flag for {@link #TYPE_CLASS_TEXT}: multiple lines of text can be - * entered into the field. If this flag is not set, the text field - * will be constrained to a single line. The IME may also choose not to - * display an enter key when this flag is not set, as there should be no - * need to create new lines. - */ - TYPE_TEXT_FLAG_MULTI_LINE = 0x00020000, - - /** - * Flag for {@link #TYPE_CLASS_TEXT}: the regular text view associated - * with this should not be multi-line, but when a fullscreen input method - * is providing text it should use multiple lines if it can. - */ - TYPE_TEXT_FLAG_IME_MULTI_LINE = 0x00040000, - - /** - * Flag for {@link #TYPE_CLASS_TEXT}: the input method does not need to - * display any dictionary-based candidates. This is useful for text views that - * do not contain words from the language and do not benefit from any - * dictionary-based completions or corrections. It overrides the - * {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} value when set. - * Please avoid using this unless you are certain this is what you want. - * Many input methods need suggestions to work well, for example the ones - * based on gesture typing. Consider clearing - * {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} instead if you just do not - * want the IME to correct typos. - * Note the contrast with {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} and - * {@link #TYPE_TEXT_FLAG_AUTO_COMPLETE}: - * {@code TYPE_TEXT_FLAG_NO_SUGGESTIONS} means the IME does not need to - * show an interface to display suggestions. Most IMEs will also take this to - * mean they do not need to try to auto-correct what the user is typing. - */ - TYPE_TEXT_FLAG_NO_SUGGESTIONS = 0x00080000, - - /** - * Flag for {@link #TYPE_CLASS_TEXT}: Let the IME know the text conversion - * suggestions are required by the application. Text conversion suggestion is - * for the transliteration languages which has pronunciation characters and - * target characters. When the user is typing the pronunciation charactes, the - * IME could provide the possible target characters to the user. When this - * flag is set, the IME should insert the text conversion suggestions through - * {@link Builder#setTextConversionSuggestions(List)} and - * the {@link TextAttribute} with initialized with the text conversion - * suggestions is provided by the IME to the application. To receive the - * additional information, the application needs to implement {@link - * InputConnection#setComposingText(CharSequence, int, TextAttribute)}, - * {@link InputConnection#setComposingRegion(int, int, TextAttribute)}, and - * {@link InputConnection#commitText(CharSequence, int, TextAttribute)}. - */ - TYPE_TEXT_FLAG_ENABLE_TEXT_CONVERSION_SUGGESTIONS = 0x00100000, - - // ---------------------------------------------------------------------- - - /** - * Default variation of {@link #TYPE_CLASS_TEXT}: plain old normal text. - */ - TYPE_TEXT_VARIATION_NORMAL = 0x00000000, - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering a URI. - */ - TYPE_TEXT_VARIATION_URI = 0x00000010, - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering an e-mail address. - */ - TYPE_TEXT_VARIATION_EMAIL_ADDRESS = 0x00000020, - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering the subject line of - * an e-mail. - */ - TYPE_TEXT_VARIATION_EMAIL_SUBJECT = 0x00000030, - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering a short, possibly informal - * message such as an instant message or a text message. - */ - TYPE_TEXT_VARIATION_SHORT_MESSAGE = 0x00000040, - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering the content of a long, - * possibly formal message such as the body of an e-mail. - */ - TYPE_TEXT_VARIATION_LONG_MESSAGE = 0x00000050, - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering the name of a person. - */ - TYPE_TEXT_VARIATION_PERSON_NAME = 0x00000060, - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering a postal mailing address. - */ - TYPE_TEXT_VARIATION_POSTAL_ADDRESS = 0x00000070, - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering a password. - */ - TYPE_TEXT_VARIATION_PASSWORD = 0x00000080, - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering a password, which should - * be visible to the user. - */ - TYPE_TEXT_VARIATION_VISIBLE_PASSWORD = 0x00000090, - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering text inside of a web form. - */ - TYPE_TEXT_VARIATION_WEB_EDIT_TEXT = 0x000000a0, - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering text to filter contents - * of a list etc. - */ - TYPE_TEXT_VARIATION_FILTER = 0x000000b0, - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering text for phonetic - * pronunciation, such as a phonetic name field in contacts. This is mostly - * useful for languages where one spelling may have several phonetic - * readings, like Japanese. - */ - TYPE_TEXT_VARIATION_PHONETIC = 0x000000c0, - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering e-mail address inside - * of a web form. This was added in - * {@link android.os.Build.VERSION_CODES#HONEYCOMB}. An IME must target - * this API version or later to see this input type; if it doesn't, a request - * for this type will be seen as {@link #TYPE_TEXT_VARIATION_EMAIL_ADDRESS} - * when passed through {@link - * android.view.inputmethod.EditorInfo#makeCompatible(int) - * EditorInfo.makeCompatible(int)}. - */ - TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS = 0x000000d0, - - /** - * Variation of {@link #TYPE_CLASS_TEXT}: entering password inside - * of a web form. This was added in - * {@link android.os.Build.VERSION_CODES#HONEYCOMB}. An IME must target - * this API version or later to see this input type; if it doesn't, a request - * for this type will be seen as {@link #TYPE_TEXT_VARIATION_PASSWORD} - * when passed through {@link - * android.view.inputmethod.EditorInfo#makeCompatible(int) - * EditorInfo.makeCompatible(int)}. - */ - TYPE_TEXT_VARIATION_WEB_PASSWORD = 0x000000e0, - - // ---------------------------------------------------------------------- - - /** - * Class for numeric text. This class supports the following flags: - * {@link #TYPE_NUMBER_FLAG_SIGNED} and - * {@link #TYPE_NUMBER_FLAG_DECIMAL}. It also supports the following - * variations: {@link #TYPE_NUMBER_VARIATION_NORMAL} and - * {@link #TYPE_NUMBER_VARIATION_PASSWORD}. - *

IME authors: If you do not recognize - * the variation, normal should be assumed.

- */ - TYPE_CLASS_NUMBER = 0x00000002, - - /** - * Flag of {@link #TYPE_CLASS_NUMBER}: the number is signed, allowing - * a positive or negative sign at the start. - */ - TYPE_NUMBER_FLAG_SIGNED = 0x00001000, - - /** - * Flag of {@link #TYPE_CLASS_NUMBER}: the number is decimal, allowing - * a decimal point to provide fractional values. - */ - TYPE_NUMBER_FLAG_DECIMAL = 0x00002000, - - // ---------------------------------------------------------------------- - - /** - * Default variation of {@link #TYPE_CLASS_NUMBER}: plain normal - * numeric text. This was added in - * {@link android.os.Build.VERSION_CODES#HONEYCOMB}. An IME must target - * this API version or later to see this input type; if it doesn't, a request - * for this type will be dropped when passed through - * {@link android.view.inputmethod.EditorInfo#makeCompatible(int) - * EditorInfo.makeCompatible(int)}. - */ - TYPE_NUMBER_VARIATION_NORMAL = 0x00000000, - - /** - * Variation of {@link #TYPE_CLASS_NUMBER}: entering a numeric password. - * This was added in {@link android.os.Build.VERSION_CODES#HONEYCOMB}. An - * IME must target this API version or later to see this input type; if it - * doesn't, a request for this type will be dropped when passed - * through {@link android.view.inputmethod.EditorInfo#makeCompatible(int) - * EditorInfo.makeCompatible(int)}. - */ - TYPE_NUMBER_VARIATION_PASSWORD = 0x00000010, - - // ---------------------------------------------------------------------- - /** - * Class for a phone number. This class currently supports no variations - * or flags. - */ - TYPE_CLASS_PHONE = 0x00000003, - - // ---------------------------------------------------------------------- - - /** - * Class for dates and times. It supports the - * following variations: - * {@link #TYPE_DATETIME_VARIATION_NORMAL} - * {@link #TYPE_DATETIME_VARIATION_DATE}, and - * {@link #TYPE_DATETIME_VARIATION_TIME}. - */ - TYPE_CLASS_DATETIME = 0x00000004, - - /** - * Default variation of {@link #TYPE_CLASS_DATETIME}: allows entering - * both a date and time. - */ - TYPE_DATETIME_VARIATION_NORMAL = 0x00000000, - - /** - * Default variation of {@link #TYPE_CLASS_DATETIME}: allows entering - * only a date. - */ - TYPE_DATETIME_VARIATION_DATE = 0x00000010, - - /** - * Default variation of {@link #TYPE_CLASS_DATETIME}: allows entering - * only a time. - */ - TYPE_DATETIME_VARIATION_TIME = 0x00000020, + /** + * Mask of bits that determine the overall class + * of text being given. Currently supported classes are: + * {@link #TYPE_CLASS_TEXT}, {@link #TYPE_CLASS_NUMBER}, + * {@link #TYPE_CLASS_PHONE}, {@link #TYPE_CLASS_DATETIME}. + *

IME authors: If the class is not one you + * understand, assume {@link #TYPE_CLASS_TEXT} with NO variation + * or flags.

+ */ + TYPE_MASK_CLASS = 0x0000000f, + + /** + * Mask of bits that determine the variation of + * the base content class. + */ + TYPE_MASK_VARIATION = 0x00000ff0, + + /** + * Mask of bits that provide addition bit flags + * of options. + */ + TYPE_MASK_FLAGS = 0x00fff000, + + /** + * Special content type for when no explicit type has been specified. + * This should be interpreted to mean that the target input connection + * is not rich, it can not process and show things like candidate text nor + * retrieve the current text, so the input method will need to run in a + * limited "generate key events" mode, if it supports it. Note that some + * input methods may not support it, for example a voice-based input + * method will likely not be able to generate key events even if this + * flag is set. + */ + TYPE_NULL = 0x00000000, + + // ---------------------------------------------------------------------- + + /** + * Class for normal text. This class supports the following flags (only + * one of which should be set): + * {@link #TYPE_TEXT_FLAG_CAP_CHARACTERS}, + * {@link #TYPE_TEXT_FLAG_CAP_WORDS}, and. + * {@link #TYPE_TEXT_FLAG_CAP_SENTENCES}. It also supports the + * following variations: + * {@link #TYPE_TEXT_VARIATION_NORMAL}, and + * {@link #TYPE_TEXT_VARIATION_URI}. If you do not recognize the + * variation, normal should be assumed. + */ + TYPE_CLASS_TEXT = 0x00000001, + + /** + * Flag for {@link #TYPE_CLASS_TEXT}: capitalize all characters. Overrides + * {@link #TYPE_TEXT_FLAG_CAP_WORDS} and + * {@link #TYPE_TEXT_FLAG_CAP_SENTENCES}. This value is explicitly defined + * to be the same as {@link TextUtils#CAP_MODE_CHARACTERS}. Of course, + * this only affects languages where there are upper-case and lower-case + * letters. + */ + TYPE_TEXT_FLAG_CAP_CHARACTERS = 0x00001000, + + /** + * Flag for {@link #TYPE_CLASS_TEXT}: capitalize the first character of + * every word. Overrides {@link #TYPE_TEXT_FLAG_CAP_SENTENCES}. This + * value is explicitly defined + * to be the same as {@link TextUtils#CAP_MODE_WORDS}. Of course, + * this only affects languages where there are upper-case and lower-case + * letters. + */ + TYPE_TEXT_FLAG_CAP_WORDS = 0x00002000, + + /** + * Flag for {@link #TYPE_CLASS_TEXT}: capitalize the first character of + * each sentence. This value is explicitly defined + * to be the same as {@link TextUtils#CAP_MODE_SENTENCES}. For example + * in English it means to capitalize after a period and a space (note that + * other languages may have different characters for period, or not use + * spaces, or use different grammatical rules). Of course, this only affects + * languages where there are upper-case and lower-case letters. + */ + TYPE_TEXT_FLAG_CAP_SENTENCES = 0x00004000, + + /** + * Flag for {@link #TYPE_CLASS_TEXT}: the user is entering free-form + * text that should have auto-correction applied to it. Without this flag, + * the IME will not try to correct typos. You should always set this flag + * unless you really expect users to type non-words in this field, for + * example to choose a name for a character in a game. + * Contrast this with {@link #TYPE_TEXT_FLAG_AUTO_COMPLETE} and + * {@link #TYPE_TEXT_FLAG_NO_SUGGESTIONS}: + * {@code TYPE_TEXT_FLAG_AUTO_CORRECT} means that the IME will try to + * auto-correct typos as the user is typing, but does not define whether + * the IME offers an interface to show suggestions. + */ + TYPE_TEXT_FLAG_AUTO_CORRECT = 0x00008000, + + /** + * Flag for {@link #TYPE_CLASS_TEXT}: the text editor (which means + * the application) is performing auto-completion of the text being entered + * based on its own semantics, which it will present to the user as they type. + * This generally means that the input method should not be showing + * candidates itself, but can expect the editor to supply its own + * completions/candidates from + * {@link android.view.inputmethod.InputMethodSession#displayCompletions + * InputMethodSession.displayCompletions()} as a result of the editor calling + * {@link android.view.inputmethod.InputMethodManager#displayCompletions + * InputMethodManager.displayCompletions()}. + * Note the contrast with {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} and + * {@link #TYPE_TEXT_FLAG_NO_SUGGESTIONS}: + * {@code TYPE_TEXT_FLAG_AUTO_COMPLETE} means the editor should show an + * interface for displaying suggestions, but instead of supplying its own + * it will rely on the Editor to pass completions/corrections. + */ + TYPE_TEXT_FLAG_AUTO_COMPLETE = 0x00010000, + + /** + * Flag for {@link #TYPE_CLASS_TEXT}: multiple lines of text can be + * entered into the field. If this flag is not set, the text field + * will be constrained to a single line. The IME may also choose not to + * display an enter key when this flag is not set, as there should be no + * need to create new lines. + */ + TYPE_TEXT_FLAG_MULTI_LINE = 0x00020000, + + /** + * Flag for {@link #TYPE_CLASS_TEXT}: the regular text view associated + * with this should not be multi-line, but when a fullscreen input method + * is providing text it should use multiple lines if it can. + */ + TYPE_TEXT_FLAG_IME_MULTI_LINE = 0x00040000, + + /** + * Flag for {@link #TYPE_CLASS_TEXT}: the input method does not need to + * display any dictionary-based candidates. This is useful for text views that + * do not contain words from the language and do not benefit from any + * dictionary-based completions or corrections. It overrides the + * {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} value when set. + * Please avoid using this unless you are certain this is what you want. + * Many input methods need suggestions to work well, for example the ones + * based on gesture typing. Consider clearing + * {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} instead if you just do not + * want the IME to correct typos. + * Note the contrast with {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} and + * {@link #TYPE_TEXT_FLAG_AUTO_COMPLETE}: + * {@code TYPE_TEXT_FLAG_NO_SUGGESTIONS} means the IME does not need to + * show an interface to display suggestions. Most IMEs will also take this to + * mean they do not need to try to auto-correct what the user is typing. + */ + TYPE_TEXT_FLAG_NO_SUGGESTIONS = 0x00080000, + + /** + * Flag for {@link #TYPE_CLASS_TEXT}: Let the IME know the text conversion + * suggestions are required by the application. Text conversion suggestion is + * for the transliteration languages which has pronunciation characters and + * target characters. When the user is typing the pronunciation charactes, the + * IME could provide the possible target characters to the user. When this + * flag is set, the IME should insert the text conversion suggestions through + * {@link Builder#setTextConversionSuggestions(List)} and + * the {@link TextAttribute} with initialized with the text conversion + * suggestions is provided by the IME to the application. To receive the + * additional information, the application needs to implement {@link + * InputConnection#setComposingText(CharSequence, int, TextAttribute)}, + * {@link InputConnection#setComposingRegion(int, int, TextAttribute)}, and + * {@link InputConnection#commitText(CharSequence, int, TextAttribute)}. + */ + TYPE_TEXT_FLAG_ENABLE_TEXT_CONVERSION_SUGGESTIONS = 0x00100000, + + // ---------------------------------------------------------------------- + + /** + * Default variation of {@link #TYPE_CLASS_TEXT}: plain old normal text. + */ + TYPE_TEXT_VARIATION_NORMAL = 0x00000000, + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering a URI. + */ + TYPE_TEXT_VARIATION_URI = 0x00000010, + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering an e-mail address. + */ + TYPE_TEXT_VARIATION_EMAIL_ADDRESS = 0x00000020, + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering the subject line of + * an e-mail. + */ + TYPE_TEXT_VARIATION_EMAIL_SUBJECT = 0x00000030, + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering a short, possibly informal + * message such as an instant message or a text message. + */ + TYPE_TEXT_VARIATION_SHORT_MESSAGE = 0x00000040, + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering the content of a long, + * possibly formal message such as the body of an e-mail. + */ + TYPE_TEXT_VARIATION_LONG_MESSAGE = 0x00000050, + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering the name of a person. + */ + TYPE_TEXT_VARIATION_PERSON_NAME = 0x00000060, + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering a postal mailing address. + */ + TYPE_TEXT_VARIATION_POSTAL_ADDRESS = 0x00000070, + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering a password. + */ + TYPE_TEXT_VARIATION_PASSWORD = 0x00000080, + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering a password, which should + * be visible to the user. + */ + TYPE_TEXT_VARIATION_VISIBLE_PASSWORD = 0x00000090, + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering text inside of a web form. + */ + TYPE_TEXT_VARIATION_WEB_EDIT_TEXT = 0x000000a0, + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering text to filter contents + * of a list etc. + */ + TYPE_TEXT_VARIATION_FILTER = 0x000000b0, + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering text for phonetic + * pronunciation, such as a phonetic name field in contacts. This is mostly + * useful for languages where one spelling may have several phonetic + * readings, like Japanese. + */ + TYPE_TEXT_VARIATION_PHONETIC = 0x000000c0, + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering e-mail address inside + * of a web form. This was added in + * {@link android.os.Build.VERSION_CODES#HONEYCOMB}. An IME must target + * this API version or later to see this input type; if it doesn't, a request + * for this type will be seen as {@link #TYPE_TEXT_VARIATION_EMAIL_ADDRESS} + * when passed through {@link + * android.view.inputmethod.EditorInfo#makeCompatible(int) + * EditorInfo.makeCompatible(int)}. + */ + TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS = 0x000000d0, + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering password inside + * of a web form. This was added in + * {@link android.os.Build.VERSION_CODES#HONEYCOMB}. An IME must target + * this API version or later to see this input type; if it doesn't, a request + * for this type will be seen as {@link #TYPE_TEXT_VARIATION_PASSWORD} + * when passed through {@link + * android.view.inputmethod.EditorInfo#makeCompatible(int) + * EditorInfo.makeCompatible(int)}. + */ + TYPE_TEXT_VARIATION_WEB_PASSWORD = 0x000000e0, + + // ---------------------------------------------------------------------- + + /** + * Class for numeric text. This class supports the following flags: + * {@link #TYPE_NUMBER_FLAG_SIGNED} and + * {@link #TYPE_NUMBER_FLAG_DECIMAL}. It also supports the following + * variations: {@link #TYPE_NUMBER_VARIATION_NORMAL} and + * {@link #TYPE_NUMBER_VARIATION_PASSWORD}. + *

IME authors: If you do not recognize + * the variation, normal should be assumed.

+ */ + TYPE_CLASS_NUMBER = 0x00000002, + + /** + * Flag of {@link #TYPE_CLASS_NUMBER}: the number is signed, allowing + * a positive or negative sign at the start. + */ + TYPE_NUMBER_FLAG_SIGNED = 0x00001000, + + /** + * Flag of {@link #TYPE_CLASS_NUMBER}: the number is decimal, allowing + * a decimal point to provide fractional values. + */ + TYPE_NUMBER_FLAG_DECIMAL = 0x00002000, + + // ---------------------------------------------------------------------- + + /** + * Default variation of {@link #TYPE_CLASS_NUMBER}: plain normal + * numeric text. This was added in + * {@link android.os.Build.VERSION_CODES#HONEYCOMB}. An IME must target + * this API version or later to see this input type; if it doesn't, a request + * for this type will be dropped when passed through + * {@link android.view.inputmethod.EditorInfo#makeCompatible(int) + * EditorInfo.makeCompatible(int)}. + */ + TYPE_NUMBER_VARIATION_NORMAL = 0x00000000, + + /** + * Variation of {@link #TYPE_CLASS_NUMBER}: entering a numeric password. + * This was added in {@link android.os.Build.VERSION_CODES#HONEYCOMB}. An + * IME must target this API version or later to see this input type; if it + * doesn't, a request for this type will be dropped when passed + * through {@link android.view.inputmethod.EditorInfo#makeCompatible(int) + * EditorInfo.makeCompatible(int)}. + */ + TYPE_NUMBER_VARIATION_PASSWORD = 0x00000010, + + // ---------------------------------------------------------------------- + /** + * Class for a phone number. This class currently supports no variations + * or flags. + */ + TYPE_CLASS_PHONE = 0x00000003, + + // ---------------------------------------------------------------------- + + /** + * Class for dates and times. It supports the + * following variations: + * {@link #TYPE_DATETIME_VARIATION_NORMAL} + * {@link #TYPE_DATETIME_VARIATION_DATE}, and + * {@link #TYPE_DATETIME_VARIATION_TIME}. + */ + TYPE_CLASS_DATETIME = 0x00000004, + + /** + * Default variation of {@link #TYPE_CLASS_DATETIME}: allows entering + * both a date and time. + */ + TYPE_DATETIME_VARIATION_NORMAL = 0x00000000, + + /** + * Default variation of {@link #TYPE_CLASS_DATETIME}: allows entering + * only a date. + */ + TYPE_DATETIME_VARIATION_DATE = 0x00000010, + + /** + * Default variation of {@link #TYPE_CLASS_DATETIME}: allows entering + * only a time. + */ + TYPE_DATETIME_VARIATION_TIME = 0x00000020, }; /** @@ -727,199 +716,199 @@ enum GameTextInputType : uint32_t { */ enum GameTextInputActionType : uint32_t { - /** - * Set of bits in {@link #imeOptions} that provide alternative actions - * associated with the "enter" key. This both helps the IME provide - * better feedback about what the enter key will do, and also allows it - * to provide alternative mechanisms for providing that command. - */ - IME_MASK_ACTION = 0x000000ff, - - /** - * Bits of {@link #IME_MASK_ACTION}: no specific action has been - * associated with this editor, let the editor come up with its own if - * it can. - */ - IME_ACTION_UNSPECIFIED = 0x00000000, - - /** - * Bits of {@link #IME_MASK_ACTION}: there is no available action. - */ - IME_ACTION_NONE = 0x00000001, - - /** - * Bits of {@link #IME_MASK_ACTION}: the action key performs a "go" - * operation to take the user to the target of the text they typed. - * Typically used, for example, when entering a URL. - */ - IME_ACTION_GO = 0x00000002, - - /** - * Bits of {@link #IME_MASK_ACTION}: the action key performs a "search" - * operation, taking the user to the results of searching for the text - * they have typed (in whatever context is appropriate). - */ - IME_ACTION_SEARCH = 0x00000003, - - /** - * Bits of {@link #IME_MASK_ACTION}: the action key performs a "send" - * operation, delivering the text to its target. This is typically used - * when composing a message in IM or SMS where sending is immediate. - */ - IME_ACTION_SEND = 0x00000004, - - /** - * Bits of {@link #IME_MASK_ACTION}: the action key performs a "next" - * operation, taking the user to the next field that will accept text. - */ - IME_ACTION_NEXT = 0x00000005, - - /** - * Bits of {@link #IME_MASK_ACTION}: the action key performs a "done" - * operation, typically meaning there is nothing more to input and the - * IME will be closed. - */ - IME_ACTION_DONE = 0x00000006, - - /** - * Bits of {@link #IME_MASK_ACTION}: like {@link #IME_ACTION_NEXT}, but - * for moving to the previous field. This will normally not be used to - * specify an action (since it precludes {@link #IME_ACTION_NEXT}), but - * can be returned to the app if it sets {@link #IME_FLAG_NAVIGATE_PREVIOUS}. - */ - IME_ACTION_PREVIOUS = 0x00000007, + /** + * Set of bits in {@link #imeOptions} that provide alternative actions + * associated with the "enter" key. This both helps the IME provide + * better feedback about what the enter key will do, and also allows it + * to provide alternative mechanisms for providing that command. + */ + IME_MASK_ACTION = 0x000000ff, + + /** + * Bits of {@link #IME_MASK_ACTION}: no specific action has been + * associated with this editor, let the editor come up with its own if + * it can. + */ + IME_ACTION_UNSPECIFIED = 0x00000000, + + /** + * Bits of {@link #IME_MASK_ACTION}: there is no available action. + */ + IME_ACTION_NONE = 0x00000001, + + /** + * Bits of {@link #IME_MASK_ACTION}: the action key performs a "go" + * operation to take the user to the target of the text they typed. + * Typically used, for example, when entering a URL. + */ + IME_ACTION_GO = 0x00000002, + + /** + * Bits of {@link #IME_MASK_ACTION}: the action key performs a "search" + * operation, taking the user to the results of searching for the text + * they have typed (in whatever context is appropriate). + */ + IME_ACTION_SEARCH = 0x00000003, + + /** + * Bits of {@link #IME_MASK_ACTION}: the action key performs a "send" + * operation, delivering the text to its target. This is typically used + * when composing a message in IM or SMS where sending is immediate. + */ + IME_ACTION_SEND = 0x00000004, + + /** + * Bits of {@link #IME_MASK_ACTION}: the action key performs a "next" + * operation, taking the user to the next field that will accept text. + */ + IME_ACTION_NEXT = 0x00000005, + + /** + * Bits of {@link #IME_MASK_ACTION}: the action key performs a "done" + * operation, typically meaning there is nothing more to input and the + * IME will be closed. + */ + IME_ACTION_DONE = 0x00000006, + + /** + * Bits of {@link #IME_MASK_ACTION}: like {@link #IME_ACTION_NEXT}, but + * for moving to the previous field. This will normally not be used to + * specify an action (since it precludes {@link #IME_ACTION_NEXT}), but + * can be returned to the app if it sets {@link #IME_FLAG_NAVIGATE_PREVIOUS}. + */ + IME_ACTION_PREVIOUS = 0x00000007, }; enum GameTextInputImeOptions : uint32_t { - /** - * Flag of {@link #imeOptions}: used to request that the IME should not update - * any personalized data such as typing history and personalized language - * model based on what the user typed on this text editing object. Typical - * use cases are:
  • When the application is in a special mode, where - * user's activities are expected to be not recorded in the application's - * history. Some web browsers and chat applications may have this kind of - * modes.
  • When storing typing history does not make much sense. - * Specifying this flag in typing games may help to avoid typing history from - * being filled up with words that the user is less likely to type in their - * daily life. Another example is that when the application already knows - * that the expected input is not a valid word (e.g. a promotion code that is - * not a valid word in any natural language).
  • - *
- * - *

Applications need to be aware that the flag is not a guarantee, and some - * IMEs may not respect it.

- */ - IME_FLAG_NO_PERSONALIZED_LEARNING = 0x1000000, - - /** - * Flag of {@link #imeOptions}: used to request that the IME never go - * into fullscreen mode. - * By default, IMEs may go into full screen mode when they think - * it's appropriate, for example on small screens in landscape - * orientation where displaying a software keyboard may occlude - * such a large portion of the screen that the remaining part is - * too small to meaningfully display the application UI. - * If this flag is set, compliant IMEs will never go into full screen mode, - * and always leave some space to display the application UI. - * Applications need to be aware that the flag is not a guarantee, and - * some IMEs may ignore it. - */ - IME_FLAG_NO_FULLSCREEN = 0x2000000, - - /** - * Flag of {@link #imeOptions}: like {@link #IME_FLAG_NAVIGATE_NEXT}, but - * specifies there is something interesting that a backward navigation - * can focus on. If the user selects the IME's facility to backward - * navigate, this will show up in the application as an {@link - * #IME_ACTION_PREVIOUS} at {@link InputConnection#performEditorAction(int) - * InputConnection.performEditorAction(int)}. - */ - IME_FLAG_NAVIGATE_PREVIOUS = 0x4000000, - - /** - * Flag of {@link #imeOptions}: used to specify that there is something - * interesting that a forward navigation can focus on. This is like using - * {@link #IME_ACTION_NEXT}, except allows the IME to be multiline (with - * an enter key) as well as provide forward navigation. Note that some - * IMEs may not be able to do this, especially when running on a small - * screen where there is little space. In that case it does not need to - * present a UI for this option. Like {@link #IME_ACTION_NEXT}, if the - * user selects the IME's facility to forward navigate, this will show up - * in the application at {@link InputConnection#performEditorAction(int) - * InputConnection.performEditorAction(int)}. - */ - IME_FLAG_NAVIGATE_NEXT = 0x8000000, - - /** - * Flag of {@link #imeOptions}: used to specify that the IME does not need - * to show its extracted text UI. For input methods that may be fullscreen, - * often when in landscape mode, this allows them to be smaller and let part - * of the application be shown behind, through transparent UI parts in the - * fullscreen IME. The part of the UI visible to the user may not be - * responsive to touch because the IME will receive touch events, which may - * confuse the user; use {@link #IME_FLAG_NO_FULLSCREEN} instead for a better - * experience. Using this flag is discouraged and it may become deprecated in - * the future. Its meaning is unclear in some situations and it may not work - * appropriately on older versions of the platform. - */ - IME_FLAG_NO_EXTRACT_UI = 0x10000000, - - /** - * Flag of {@link #imeOptions}: used in conjunction with one of the actions - * masked by {@link #IME_MASK_ACTION}, this indicates that the action - * should not be available as an accessory button on the right of the - * extracted text when the input method is full-screen. Note that by setting - * this flag, there can be cases where the action is simply never available to - * the user. Setting this generally means that you think that in fullscreen - * mode, where there is little space to show the text, it's not worth taking - * some screen real estate to display the action and it should be used instead - * to show more text. - */ - IME_FLAG_NO_ACCESSORY_ACTION = 0x20000000, - - /** - * Flag of {@link #imeOptions}: used in conjunction with one of the actions - * masked by {@link #IME_MASK_ACTION}. If this flag is not set, IMEs will - * normally replace the "enter" key with the action supplied. This flag - * indicates that the action should not be available in-line as a replacement - * for the "enter" key. Typically this is because the action has such a - * significant impact or is not recoverable enough that accidentally hitting - * it should be avoided, such as sending a message. Note that - * {@link android.widget.TextView} will automatically set this flag for you - * on multi-line text views. - */ - IME_FLAG_NO_ENTER_ACTION = 0x40000000, - - /** - * Flag of {@link #imeOptions}: used to request an IME that is capable of - * inputting ASCII characters. The intention of this flag is to ensure that - * the user can type Roman alphabet characters in a {@link - * android.widget.TextView}. It is typically used for an account ID or - * password input. A lot of the time, IMEs are already able to input ASCII - * even without being told so (such IMEs already respect this flag in a - * sense), but there are cases when this is not the default. For instance, - * users of languages using a different script like Arabic, Greek, Hebrew or - * Russian typically have a keyboard that can't input ASCII characters by - * default. Applications need to be aware that the flag is not a guarantee, - * and some IMEs may not respect it. However, it is strongly recommended for - * IME authors to respect this flag especially when their IME could end up - * with a state where only languages using non-ASCII are enabled. - */ - IME_FLAG_FORCE_ASCII = 0x80000000, - - /** - * Flag of {@link #internalImeOptions}: flag is set when app window containing - * this - * {@link EditorInfo} is using {@link Configuration#ORIENTATION_PORTRAIT} - * mode. - * @hide - */ - IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT = 0x00000001, - - /** - * Generic unspecified type for {@link #imeOptions}. - */ - IME_NULL = 0x00000000, + /** + * Flag of {@link #imeOptions}: used to request that the IME should not update + * any personalized data such as typing history and personalized language + * model based on what the user typed on this text editing object. Typical + * use cases are:
  • When the application is in a special mode, where + * user's activities are expected to be not recorded in the application's + * history. Some web browsers and chat applications may have this kind of + * modes.
  • When storing typing history does not make much sense. + * Specifying this flag in typing games may help to avoid typing history from + * being filled up with words that the user is less likely to type in their + * daily life. Another example is that when the application already knows + * that the expected input is not a valid word (e.g. a promotion code that is + * not a valid word in any natural language).
  • + *
+ * + *

Applications need to be aware that the flag is not a guarantee, and some + * IMEs may not respect it.

+ */ + IME_FLAG_NO_PERSONALIZED_LEARNING = 0x1000000, + + /** + * Flag of {@link #imeOptions}: used to request that the IME never go + * into fullscreen mode. + * By default, IMEs may go into full screen mode when they think + * it's appropriate, for example on small screens in landscape + * orientation where displaying a software keyboard may occlude + * such a large portion of the screen that the remaining part is + * too small to meaningfully display the application UI. + * If this flag is set, compliant IMEs will never go into full screen mode, + * and always leave some space to display the application UI. + * Applications need to be aware that the flag is not a guarantee, and + * some IMEs may ignore it. + */ + IME_FLAG_NO_FULLSCREEN = 0x2000000, + + /** + * Flag of {@link #imeOptions}: like {@link #IME_FLAG_NAVIGATE_NEXT}, but + * specifies there is something interesting that a backward navigation + * can focus on. If the user selects the IME's facility to backward + * navigate, this will show up in the application as an {@link + * #IME_ACTION_PREVIOUS} at {@link InputConnection#performEditorAction(int) + * InputConnection.performEditorAction(int)}. + */ + IME_FLAG_NAVIGATE_PREVIOUS = 0x4000000, + + /** + * Flag of {@link #imeOptions}: used to specify that there is something + * interesting that a forward navigation can focus on. This is like using + * {@link #IME_ACTION_NEXT}, except allows the IME to be multiline (with + * an enter key) as well as provide forward navigation. Note that some + * IMEs may not be able to do this, especially when running on a small + * screen where there is little space. In that case it does not need to + * present a UI for this option. Like {@link #IME_ACTION_NEXT}, if the + * user selects the IME's facility to forward navigate, this will show up + * in the application at {@link InputConnection#performEditorAction(int) + * InputConnection.performEditorAction(int)}. + */ + IME_FLAG_NAVIGATE_NEXT = 0x8000000, + + /** + * Flag of {@link #imeOptions}: used to specify that the IME does not need + * to show its extracted text UI. For input methods that may be fullscreen, + * often when in landscape mode, this allows them to be smaller and let part + * of the application be shown behind, through transparent UI parts in the + * fullscreen IME. The part of the UI visible to the user may not be + * responsive to touch because the IME will receive touch events, which may + * confuse the user; use {@link #IME_FLAG_NO_FULLSCREEN} instead for a better + * experience. Using this flag is discouraged and it may become deprecated in + * the future. Its meaning is unclear in some situations and it may not work + * appropriately on older versions of the platform. + */ + IME_FLAG_NO_EXTRACT_UI = 0x10000000, + + /** + * Flag of {@link #imeOptions}: used in conjunction with one of the actions + * masked by {@link #IME_MASK_ACTION}, this indicates that the action + * should not be available as an accessory button on the right of the + * extracted text when the input method is full-screen. Note that by setting + * this flag, there can be cases where the action is simply never available to + * the user. Setting this generally means that you think that in fullscreen + * mode, where there is little space to show the text, it's not worth taking + * some screen real estate to display the action and it should be used instead + * to show more text. + */ + IME_FLAG_NO_ACCESSORY_ACTION = 0x20000000, + + /** + * Flag of {@link #imeOptions}: used in conjunction with one of the actions + * masked by {@link #IME_MASK_ACTION}. If this flag is not set, IMEs will + * normally replace the "enter" key with the action supplied. This flag + * indicates that the action should not be available in-line as a replacement + * for the "enter" key. Typically this is because the action has such a + * significant impact or is not recoverable enough that accidentally hitting + * it should be avoided, such as sending a message. Note that + * {@link android.widget.TextView} will automatically set this flag for you + * on multi-line text views. + */ + IME_FLAG_NO_ENTER_ACTION = 0x40000000, + + /** + * Flag of {@link #imeOptions}: used to request an IME that is capable of + * inputting ASCII characters. The intention of this flag is to ensure that + * the user can type Roman alphabet characters in a {@link + * android.widget.TextView}. It is typically used for an account ID or + * password input. A lot of the time, IMEs are already able to input ASCII + * even without being told so (such IMEs already respect this flag in a + * sense), but there are cases when this is not the default. For instance, + * users of languages using a different script like Arabic, Greek, Hebrew or + * Russian typically have a keyboard that can't input ASCII characters by + * default. Applications need to be aware that the flag is not a guarantee, + * and some IMEs may not respect it. However, it is strongly recommended for + * IME authors to respect this flag especially when their IME could end up + * with a state where only languages using non-ASCII are enabled. + */ + IME_FLAG_FORCE_ASCII = 0x80000000, + + /** + * Flag of {@link #internalImeOptions}: flag is set when app window containing + * this + * {@link EditorInfo} is using {@link Configuration#ORIENTATION_PORTRAIT} + * mode. + * @hide + */ + IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT = 0x00000001, + + /** + * Generic unspecified type for {@link #imeOptions}. + */ + IME_NULL = 0x00000000, }; #ifdef __cplusplus diff --git a/android-activity/android-games-sdk/game-text-input/prefab-src/modules/game-text-input/src/game-text-input/gametextinput.cpp b/android-activity/android-games-sdk/game-text-input/prefab-src/modules/game-text-input/src/game-text-input/gametextinput.cpp index 70a59389..5db09800 100644 --- a/android-activity/android-games-sdk/game-text-input/prefab-src/modules/game-text-input/src/game-text-input/gametextinput.cpp +++ b/android-activity/android-games-sdk/game-text-input/prefab-src/modules/game-text-input/src/game-text-input/gametextinput.cpp @@ -31,65 +31,64 @@ 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; + 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); - GameTextInputState getState() const { - std::lock_guard lock(currentStateMutex_); - 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_ = {}; - // A mutex to protect currentState_. - mutable std::mutex currentStateMutex_; - // 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_; +public: + GameTextInput(JNIEnv* env, uint32_t max_string_size); + ~GameTextInput(); + void setState(const GameTextInputState& state); + GameTextInputState getState() const { + std::lock_guard lock(currentStateMutex_); + 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_ = {}; + // A mutex to protect currentState_. + mutable std::mutex currentStateMutex_; + // 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; @@ -101,286 +100,257 @@ extern "C" { /////////////////////////////////////////////////////////// // Convert to a Java structure. -jobject currentState_toJava(const GameTextInput *gameTextInput, - const GameTextInputState *state) { - if (state == nullptr) return NULL; - return gameTextInput->stateToJava(*state); +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); +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"); +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(); - } - // 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_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_setState(GameTextInput* input, const GameTextInputState* state) { + if (state == nullptr) return; + input->setState(*state); } -void GameTextInput_getState(GameTextInput *input, - GameTextInputGetStateCallback callback, - void *context) { - GameTextInputState state = input->getState(); - callback(context, &state); +void GameTextInput_getState(GameTextInput* input, GameTextInputGetStateCallback callback, + void* context) { + GameTextInputState state = input->getState(); + callback(context, &state); } -void GameTextInput_setInputConnection(GameTextInput *input, - jobject inputConnection) { - input->setInputConnection(inputConnection); +void GameTextInput_setInputConnection(GameTextInput* input, jobject inputConnection) { + input->setInputConnection(inputConnection); } -void GameTextInput_processEvent(GameTextInput *input, jobject textInputEvent) { - input->processEvent(textInputEvent); +void GameTextInput_processEvent(GameTextInput* input, jobject textInputEvent) { + input->processEvent(textInputEvent); } -void GameTextInput_processImeInsets(GameTextInput *input, const ARect *insets) { - input->processImeInsets(insets); +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_showIme(struct GameTextInput* input, uint32_t flags) { + input->showIme(flags); } -void GameTextInput_hideIme(struct GameTextInput *input, uint32_t flags) { - input->hideIme(flags); +void GameTextInput_hideIme(struct GameTextInput* input, uint32_t flags) { + input->hideIme(flags); } -void GameTextInput_restartInput(struct GameTextInput *input) { - input->restartInput(); +void GameTextInput_restartInput(struct GameTextInput* input) { + input->restartInput(); } -void GameTextInput_setEventCallback(struct GameTextInput *input, - GameTextInputEventCallback callback, - void *context) { - input->setEventCallback(callback, context); +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_setImeInsetsCallback(struct GameTextInput* input, + GameTextInputImeInsetsCallback callback, void* context) { + input->setImeInsetsCallback(callback, context); } -void GameTextInput_getImeInsets(const GameTextInput *input, ARect *insets) { - *insets = input->getImeInsets(); +void GameTextInput_getImeInsets(const GameTextInput* input, ARect* insets) { + *insets = input->getImeInsets(); } -} // extern "C" +} // 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(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; - } + 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::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) { - std::lock_guard lock(currentStateMutex_); - - // 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::setStateInner(const GameTextInputState& state) { + std::lock_guard lock(currentStateMutex_); + + // 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); + 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); +/*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_) { - std::lock_guard lock(currentStateMutex_); - eventCallback_(eventCallbackContext_, ¤tState_); - } + stateFromJava(textInputEvent, processCallback, this); + if (eventCallback_) { + std::lock_guard lock(currentStateMutex_); + eventCallback_(eventCallbackContext_, ¤tState_); + } } void GameTextInput::showIme(uint32_t flags) { - if (inputConnection_ == nullptr) return; - env_->CallVoidMethod(inputConnection_, setSoftKeyboardActiveMethod_, true, - static_cast(flags)); + if (inputConnection_ == nullptr) return; + env_->CallVoidMethod(inputConnection_, setSoftKeyboardActiveMethod_, true, + static_cast(flags)); } -void GameTextInput::setEventCallback(GameTextInputEventCallback callback, - void *context) { - eventCallback_ = callback; - eventCallbackContext_ = context; +void GameTextInput::setEventCallback(GameTextInputEventCallback callback, void* context) { + eventCallback_ = callback; + eventCallbackContext_ = context; } -void GameTextInput::setImeInsetsCallback( - GameTextInputImeInsetsCallback callback, void *context) { - insetsCallback_ = callback; - insetsCallbackContext_ = 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::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, - static_cast(flags)); + if (inputConnection_ == nullptr) return; + env_->CallVoidMethod(inputConnection_, setSoftKeyboardActiveMethod_, false, + static_cast(flags)); } void GameTextInput::restartInput() { - if (inputConnection_ == nullptr) return; - env_->CallVoidMethod(inputConnection_, restartInputMethod_); + if (inputConnection_ == nullptr) return; + env_->CallVoidMethod(inputConnection_, restartInputMethod_); } -jobject GameTextInput::stateToJava(const GameTextInputState &state) const { - static jmethodID constructor = nullptr; - if (constructor == nullptr) { - constructor = env_->GetMethodID(stateJavaClass_, "", - "(Ljava/lang/String;IIII)V"); +jobject GameTextInput::stateToJava(const GameTextInputState& state) const { + static jmethodID constructor = nullptr; if (constructor == nullptr) { - __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, - "Can't find gametextinput.State constructor"); - return 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; } - } - 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; + // 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); +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/android-games-sdk/include/common/gamesdk_common.h b/android-activity/android-games-sdk/include/common/gamesdk_common.h index 25ce8139..806351f5 100644 --- a/android-activity/android-games-sdk/include/common/gamesdk_common.h +++ b/android-activity/android-games-sdk/include/common/gamesdk_common.h @@ -31,7 +31,7 @@ // 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)) + ((MAJOR << 16) | (MINOR << 8) | (BUGFIX)) // Accessors #define ANDROID_GAMESDK_MAJOR_VERSION(PACKED) ((PACKED) >> 16) #define ANDROID_GAMESDK_MINOR_VERSION(PACKED) (((PACKED) >> 8) & 0xff) @@ -39,4 +39,4 @@ #define AGDK_STRINGIFY(NUMBER) #NUMBER #define AGDK_STRING_VERSION(MAJOR, MINOR, BUGFIX) \ - AGDK_STRINGIFY(MAJOR) "." AGDK_STRINGIFY(MINOR) "." AGDK_STRINGIFY(BUGFIX) + AGDK_STRINGIFY(MAJOR) "." AGDK_STRINGIFY(MINOR) "." AGDK_STRINGIFY(BUGFIX) diff --git a/android-activity/android-games-sdk/src/common/system_utils.cpp b/android-activity/android-games-sdk/src/common/system_utils.cpp index efc5b743..2bd09ea8 100644 --- a/android-activity/android-games-sdk/src/common/system_utils.cpp +++ b/android-activity/android-games-sdk/src/common/system_utils.cpp @@ -23,15 +23,13 @@ namespace gamesdk { #if __ANDROID_API__ >= 26 -std::string getSystemPropViaCallback(const char* key, - const char* default_value = "") { +std::string getSystemPropViaCallback(const char* key, const char* default_value = "") { const prop_info* prop = __system_property_find(key); if (prop == nullptr) { return default_value; } std::string return_value; - auto thunk = [](void* cookie, const char* /*name*/, const char* value, - uint32_t /*serial*/) { + auto thunk = [](void* cookie, const char* /*name*/, const char* value, uint32_t /*serial*/) { if (value != nullptr) { std::string* r = static_cast(cookie); *r = value; @@ -41,9 +39,8 @@ std::string getSystemPropViaCallback(const char* key, return return_value; } #else -std::string getSystemPropViaGet(const char* key, - const char* default_value = "") { - char buffer[PROP_VALUE_MAX + 1] = ""; // +1 for terminator +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; @@ -69,4 +66,4 @@ bool GetSystemPropAsBool(const char* key, bool default_value) { return GetSystemPropAsInt(key, default_value) != 0; } -} // namespace gamesdk \ No newline at end of file +} // namespace gamesdk \ No newline at end of file diff --git a/android-activity/android-games-sdk/src/common/system_utils.h b/android-activity/android-games-sdk/src/common/system_utils.h index 488374bc..ed3286fc 100644 --- a/android-activity/android-games-sdk/src/common/system_utils.h +++ b/android-activity/android-games-sdk/src/common/system_utils.h @@ -29,4 +29,4 @@ int GetSystemPropAsInt(const char* key, int default_value = 0); // Get the value of the given system property as a bool bool GetSystemPropAsBool(const char* key, bool default_value = false); -} // namespace gamesdk \ No newline at end of file +} // namespace gamesdk \ No newline at end of file diff --git a/android-activity/src/game_activity/ffi_aarch64.rs b/android-activity/src/game_activity/ffi_aarch64.rs index 323af260..50c2e939 100644 --- a/android-activity/src/game_activity/ffi_aarch64.rs +++ b/android-activity/src/game_activity/ffi_aarch64.rs @@ -293,10 +293,10 @@ pub const SCNxPTR: &[u8; 3] = b"lx\0"; pub const GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT: u32 = 48; pub const GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT: u32 = 8; pub const GAMETEXTINPUT_MAJOR_VERSION: u32 = 4; -pub const GAMETEXTINPUT_MINOR_VERSION: u32 = 0; +pub const GAMETEXTINPUT_MINOR_VERSION: u32 = 3; pub const GAMETEXTINPUT_BUGFIX_VERSION: u32 = 0; pub const GAMEACTIVITY_MAJOR_VERSION: u32 = 4; -pub const GAMEACTIVITY_MINOR_VERSION: u32 = 0; +pub const GAMEACTIVITY_MINOR_VERSION: u32 = 4; pub const GAMEACTIVITY_BUGFIX_VERSION: u32 = 0; pub const POLLIN: u32 = 1; pub const POLLPRI: u32 = 2; @@ -2364,7 +2364,7 @@ pub struct GameTextInput { _unused: [u8; 0], } unsafe extern "C" { - #[doc = " Initialize the GameTextInput library.\n If called twice without GameTextInput_destroy being called, the same pointer\n will be returned and a warning will be issued.\n @param env A JNI env valid on the calling thread.\n @param max_string_size The maximum length of a string that can be edited. If\n zero, the maximum defaults to 65536 bytes. A buffer of this size is allocated\n at initialization.\n @return A handle to the library."] + #[doc = " Initialize the GameTextInput library.\n If called twice without GameTextInput_destroy being called, the same pointer\n will be returned and a warning will be issued.\n @param env A JNI env valid on the calling thread. All other calls to the resulting GameTextInput\n object must be done on the same calling thread.\n @param max_string_size The maximum length of a string that can be edited. If\n zero, the maximum defaults to 65536 bytes. A buffer of this size is allocated\n at initialization.\n @return A handle to the library."] pub fn GameTextInput_init(env: *mut JNIEnv, max_string_size: u32) -> *mut GameTextInput; } unsafe extern "C" { @@ -4829,7 +4829,7 @@ pub const NativeAppGlueAppCmd_APP_CMD_WINDOW_INSETS_CHANGED: NativeAppGlueAppCmd #[doc = " Commands passed from the application's main Java thread to the game's thread.\n\n Values from 0 to 127 are reserved for this library; values from -128 to -1\n can be used for custom user's events."] pub type NativeAppGlueAppCmd = i8; unsafe extern "C" { - #[doc = " Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next\n app command message."] + #[doc = " Call when ALooper_pollOnce() returns LOOPER_ID_MAIN, reading the next\n app command message."] pub fn android_app_read_cmd(android_app: *mut android_app) -> i8; } unsafe extern "C" { @@ -4873,8 +4873,8 @@ unsafe extern "C" { ); } unsafe extern "C" { - #[doc = " You can send your custom events using the function below.\n\n Make sure your custom codes do not overlap with this library's ones.\n\n Values from 0 to 127 are reserved for this library; values from -128 to -1\n can be used for custom user's events."] - pub fn android_app_write_cmd(android_app: *mut android_app, cmd: i8); + #[doc = " You can send your custom events using the function below.\n\n Make sure your custom codes do not overlap with this library's ones.\n\n Values from 0 to 127 are reserved for this library; values from -128 to -1\n can be used for custom user's events.\n\n The function returns true if the write operation was successful."] + pub fn android_app_write_cmd(android_app: *mut android_app, cmd: i8) -> bool; } unsafe extern "C" { #[doc = " Determines if a looper wake up was due to new input becoming available"] diff --git a/android-activity/src/game_activity/ffi_arm.rs b/android-activity/src/game_activity/ffi_arm.rs index 79126740..1ee62e57 100644 --- a/android-activity/src/game_activity/ffi_arm.rs +++ b/android-activity/src/game_activity/ffi_arm.rs @@ -378,10 +378,10 @@ pub const SCNxMAX: &[u8; 3] = b"jx\0"; pub const GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT: u32 = 48; pub const GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT: u32 = 8; pub const GAMETEXTINPUT_MAJOR_VERSION: u32 = 4; -pub const GAMETEXTINPUT_MINOR_VERSION: u32 = 0; +pub const GAMETEXTINPUT_MINOR_VERSION: u32 = 3; pub const GAMETEXTINPUT_BUGFIX_VERSION: u32 = 0; pub const GAMEACTIVITY_MAJOR_VERSION: u32 = 4; -pub const GAMEACTIVITY_MINOR_VERSION: u32 = 0; +pub const GAMEACTIVITY_MINOR_VERSION: u32 = 4; pub const GAMEACTIVITY_BUGFIX_VERSION: u32 = 0; pub const POLLIN: u32 = 1; pub const POLLPRI: u32 = 2; @@ -2420,7 +2420,7 @@ pub struct GameTextInput { _unused: [u8; 0], } unsafe extern "C" { - #[doc = " Initialize the GameTextInput library.\n If called twice without GameTextInput_destroy being called, the same pointer\n will be returned and a warning will be issued.\n @param env A JNI env valid on the calling thread.\n @param max_string_size The maximum length of a string that can be edited. If\n zero, the maximum defaults to 65536 bytes. A buffer of this size is allocated\n at initialization.\n @return A handle to the library."] + #[doc = " Initialize the GameTextInput library.\n If called twice without GameTextInput_destroy being called, the same pointer\n will be returned and a warning will be issued.\n @param env A JNI env valid on the calling thread. All other calls to the resulting GameTextInput\n object must be done on the same calling thread.\n @param max_string_size The maximum length of a string that can be edited. If\n zero, the maximum defaults to 65536 bytes. A buffer of this size is allocated\n at initialization.\n @return A handle to the library."] pub fn GameTextInput_init(env: *mut JNIEnv, max_string_size: u32) -> *mut GameTextInput; } unsafe extern "C" { @@ -5241,7 +5241,7 @@ pub const NativeAppGlueAppCmd_APP_CMD_WINDOW_INSETS_CHANGED: NativeAppGlueAppCmd #[doc = " Commands passed from the application's main Java thread to the game's thread.\n\n Values from 0 to 127 are reserved for this library; values from -128 to -1\n can be used for custom user's events."] pub type NativeAppGlueAppCmd = i8; unsafe extern "C" { - #[doc = " Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next\n app command message."] + #[doc = " Call when ALooper_pollOnce() returns LOOPER_ID_MAIN, reading the next\n app command message."] pub fn android_app_read_cmd(android_app: *mut android_app) -> i8; } unsafe extern "C" { @@ -5285,8 +5285,8 @@ unsafe extern "C" { ); } unsafe extern "C" { - #[doc = " You can send your custom events using the function below.\n\n Make sure your custom codes do not overlap with this library's ones.\n\n Values from 0 to 127 are reserved for this library; values from -128 to -1\n can be used for custom user's events."] - pub fn android_app_write_cmd(android_app: *mut android_app, cmd: i8); + #[doc = " You can send your custom events using the function below.\n\n Make sure your custom codes do not overlap with this library's ones.\n\n Values from 0 to 127 are reserved for this library; values from -128 to -1\n can be used for custom user's events.\n\n The function returns true if the write operation was successful."] + pub fn android_app_write_cmd(android_app: *mut android_app, cmd: i8) -> bool; } unsafe extern "C" { #[doc = " Determines if a looper wake up was due to new input becoming available"] diff --git a/android-activity/src/game_activity/ffi_i686.rs b/android-activity/src/game_activity/ffi_i686.rs index 52ac4e4e..85b6d835 100644 --- a/android-activity/src/game_activity/ffi_i686.rs +++ b/android-activity/src/game_activity/ffi_i686.rs @@ -241,10 +241,10 @@ pub const SCNxMAX: &[u8; 3] = b"jx\0"; pub const GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT: u32 = 48; pub const GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT: u32 = 8; pub const GAMETEXTINPUT_MAJOR_VERSION: u32 = 4; -pub const GAMETEXTINPUT_MINOR_VERSION: u32 = 0; +pub const GAMETEXTINPUT_MINOR_VERSION: u32 = 3; pub const GAMETEXTINPUT_BUGFIX_VERSION: u32 = 0; pub const GAMEACTIVITY_MAJOR_VERSION: u32 = 4; -pub const GAMEACTIVITY_MINOR_VERSION: u32 = 0; +pub const GAMEACTIVITY_MINOR_VERSION: u32 = 4; pub const GAMEACTIVITY_BUGFIX_VERSION: u32 = 0; pub const POLLIN: u32 = 1; pub const POLLPRI: u32 = 2; @@ -2284,7 +2284,7 @@ pub struct GameTextInput { _unused: [u8; 0], } unsafe extern "C" { - #[doc = " Initialize the GameTextInput library.\n If called twice without GameTextInput_destroy being called, the same pointer\n will be returned and a warning will be issued.\n @param env A JNI env valid on the calling thread.\n @param max_string_size The maximum length of a string that can be edited. If\n zero, the maximum defaults to 65536 bytes. A buffer of this size is allocated\n at initialization.\n @return A handle to the library."] + #[doc = " Initialize the GameTextInput library.\n If called twice without GameTextInput_destroy being called, the same pointer\n will be returned and a warning will be issued.\n @param env A JNI env valid on the calling thread. All other calls to the resulting GameTextInput\n object must be done on the same calling thread.\n @param max_string_size The maximum length of a string that can be edited. If\n zero, the maximum defaults to 65536 bytes. A buffer of this size is allocated\n at initialization.\n @return A handle to the library."] pub fn GameTextInput_init(env: *mut JNIEnv, max_string_size: u32) -> *mut GameTextInput; } unsafe extern "C" { @@ -5281,7 +5281,7 @@ pub const NativeAppGlueAppCmd_APP_CMD_WINDOW_INSETS_CHANGED: NativeAppGlueAppCmd #[doc = " Commands passed from the application's main Java thread to the game's thread.\n\n Values from 0 to 127 are reserved for this library; values from -128 to -1\n can be used for custom user's events."] pub type NativeAppGlueAppCmd = i8; unsafe extern "C" { - #[doc = " Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next\n app command message."] + #[doc = " Call when ALooper_pollOnce() returns LOOPER_ID_MAIN, reading the next\n app command message."] pub fn android_app_read_cmd(android_app: *mut android_app) -> i8; } unsafe extern "C" { @@ -5325,8 +5325,8 @@ unsafe extern "C" { ); } unsafe extern "C" { - #[doc = " You can send your custom events using the function below.\n\n Make sure your custom codes do not overlap with this library's ones.\n\n Values from 0 to 127 are reserved for this library; values from -128 to -1\n can be used for custom user's events."] - pub fn android_app_write_cmd(android_app: *mut android_app, cmd: i8); + #[doc = " You can send your custom events using the function below.\n\n Make sure your custom codes do not overlap with this library's ones.\n\n Values from 0 to 127 are reserved for this library; values from -128 to -1\n can be used for custom user's events.\n\n The function returns true if the write operation was successful."] + pub fn android_app_write_cmd(android_app: *mut android_app, cmd: i8) -> bool; } unsafe extern "C" { #[doc = " Determines if a looper wake up was due to new input becoming available"] diff --git a/android-activity/src/game_activity/ffi_x86_64.rs b/android-activity/src/game_activity/ffi_x86_64.rs index 849863f9..ac793270 100644 --- a/android-activity/src/game_activity/ffi_x86_64.rs +++ b/android-activity/src/game_activity/ffi_x86_64.rs @@ -282,10 +282,10 @@ pub const SCNxPTR: &[u8; 3] = b"lx\0"; pub const GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT: u32 = 48; pub const GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT: u32 = 8; pub const GAMETEXTINPUT_MAJOR_VERSION: u32 = 4; -pub const GAMETEXTINPUT_MINOR_VERSION: u32 = 0; +pub const GAMETEXTINPUT_MINOR_VERSION: u32 = 3; pub const GAMETEXTINPUT_BUGFIX_VERSION: u32 = 0; pub const GAMEACTIVITY_MAJOR_VERSION: u32 = 4; -pub const GAMEACTIVITY_MINOR_VERSION: u32 = 0; +pub const GAMEACTIVITY_MINOR_VERSION: u32 = 4; pub const GAMEACTIVITY_BUGFIX_VERSION: u32 = 0; pub const POLLIN: u32 = 1; pub const POLLPRI: u32 = 2; @@ -2325,7 +2325,7 @@ pub struct GameTextInput { _unused: [u8; 0], } unsafe extern "C" { - #[doc = " Initialize the GameTextInput library.\n If called twice without GameTextInput_destroy being called, the same pointer\n will be returned and a warning will be issued.\n @param env A JNI env valid on the calling thread.\n @param max_string_size The maximum length of a string that can be edited. If\n zero, the maximum defaults to 65536 bytes. A buffer of this size is allocated\n at initialization.\n @return A handle to the library."] + #[doc = " Initialize the GameTextInput library.\n If called twice without GameTextInput_destroy being called, the same pointer\n will be returned and a warning will be issued.\n @param env A JNI env valid on the calling thread. All other calls to the resulting GameTextInput\n object must be done on the same calling thread.\n @param max_string_size The maximum length of a string that can be edited. If\n zero, the maximum defaults to 65536 bytes. A buffer of this size is allocated\n at initialization.\n @return A handle to the library."] pub fn GameTextInput_init(env: *mut JNIEnv, max_string_size: u32) -> *mut GameTextInput; } unsafe extern "C" { @@ -5310,7 +5310,7 @@ pub const NativeAppGlueAppCmd_APP_CMD_WINDOW_INSETS_CHANGED: NativeAppGlueAppCmd #[doc = " Commands passed from the application's main Java thread to the game's thread.\n\n Values from 0 to 127 are reserved for this library; values from -128 to -1\n can be used for custom user's events."] pub type NativeAppGlueAppCmd = i8; unsafe extern "C" { - #[doc = " Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next\n app command message."] + #[doc = " Call when ALooper_pollOnce() returns LOOPER_ID_MAIN, reading the next\n app command message."] pub fn android_app_read_cmd(android_app: *mut android_app) -> i8; } unsafe extern "C" { @@ -5354,8 +5354,8 @@ unsafe extern "C" { ); } unsafe extern "C" { - #[doc = " You can send your custom events using the function below.\n\n Make sure your custom codes do not overlap with this library's ones.\n\n Values from 0 to 127 are reserved for this library; values from -128 to -1\n can be used for custom user's events."] - pub fn android_app_write_cmd(android_app: *mut android_app, cmd: i8); + #[doc = " You can send your custom events using the function below.\n\n Make sure your custom codes do not overlap with this library's ones.\n\n Values from 0 to 127 are reserved for this library; values from -128 to -1\n can be used for custom user's events.\n\n The function returns true if the write operation was successful."] + pub fn android_app_write_cmd(android_app: *mut android_app, cmd: i8) -> bool; } unsafe extern "C" { #[doc = " Determines if a looper wake up was due to new input becoming available"]