integrate gamesdk source code (#248)
* integrate gamesdk source code * refine
This commit is contained in:
parent
12edb5dd27
commit
b0c2516d5b
|
|
@ -47,6 +47,21 @@ endif()
|
|||
# add dependent boost libs
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/boost-source/boost.cmake)
|
||||
|
||||
|
||||
if(ANDROID)
|
||||
set(CC_GAME_ACTIVITY_SOURCES
|
||||
${CMAKE_CURRENT_LIST_DIR}/android-gamesdk/GameActivity/game-activity/include/game-activity/GameActivity.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/android-gamesdk/GameActivity/game-activity/include/game-activity/native_app_glue/android_native_app_glue.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/android-gamesdk/GameActivity/game-activity/include/game-text-input/gametextinput.cpp
|
||||
)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/android-gamesdk/GameController/CMakeLists.txt)
|
||||
list(APPEND CC_EXTERNAL_SOURCES
|
||||
${CC_GAME_ACTIVITY_SOURCES}
|
||||
)
|
||||
include_directories(${CMAKE_CURRENT_LIST_DIR}/android-gamesdk/GameActivity/game-activity/include)
|
||||
endif()
|
||||
|
||||
if(ANDROID OR OHOS)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/pvmp3dec/CMakeLists.txt)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,769 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup GameActivity Game Activity
|
||||
* The interface to use GameActivity.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file GameActivity.h
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_GAME_SDK_GAME_ACTIVITY_H
|
||||
#define ANDROID_GAME_SDK_GAME_ACTIVITY_H
|
||||
|
||||
#include <android/asset_manager.h>
|
||||
#include <android/input.h>
|
||||
#include <android/native_window.h>
|
||||
#include <android/rect.h>
|
||||
#include <jni.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "game-text-input/gametextinput.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* {@link GameActivityCallbacks}
|
||||
*/
|
||||
struct GameActivityCallbacks;
|
||||
|
||||
/**
|
||||
* This structure defines the native side of an android.app.GameActivity.
|
||||
* It is created by the framework, and handed to the application's native
|
||||
* code as it is being launched.
|
||||
*/
|
||||
typedef struct GameActivity {
|
||||
/**
|
||||
* Pointer to the callback function table of the native application.
|
||||
* You can set the functions here to your own callbacks. The callbacks
|
||||
* pointer itself here should not be changed; it is allocated and managed
|
||||
* for you by the framework.
|
||||
*/
|
||||
struct GameActivityCallbacks* callbacks;
|
||||
|
||||
/**
|
||||
* The global handle on the process's Java VM.
|
||||
*/
|
||||
JavaVM* vm;
|
||||
|
||||
/**
|
||||
* JNI context for the main thread of the app. Note that this field
|
||||
* can ONLY be used from the main thread of the process; that is, the
|
||||
* thread that calls into the GameActivityCallbacks.
|
||||
*/
|
||||
JNIEnv* env;
|
||||
|
||||
/**
|
||||
* The GameActivity object handle.
|
||||
*/
|
||||
jobject javaGameActivity;
|
||||
|
||||
/**
|
||||
* Path to this application's internal data directory.
|
||||
*/
|
||||
const char* internalDataPath;
|
||||
|
||||
/**
|
||||
* Path to this application's external (removable/mountable) data directory.
|
||||
*/
|
||||
const char* externalDataPath;
|
||||
|
||||
/**
|
||||
* The platform's SDK version code.
|
||||
*/
|
||||
int32_t sdkVersion;
|
||||
|
||||
/**
|
||||
* This is the native instance of the application. It is not used by
|
||||
* the framework, but can be set by the application to its own instance
|
||||
* state.
|
||||
*/
|
||||
void* instance;
|
||||
|
||||
/**
|
||||
* Pointer to the Asset Manager instance for the application. The
|
||||
* application uses this to access binary assets bundled inside its own .apk
|
||||
* file.
|
||||
*/
|
||||
AAssetManager* assetManager;
|
||||
|
||||
/**
|
||||
* Available starting with Honeycomb: path to the directory containing
|
||||
* the application's OBB files (if any). If the app doesn't have any
|
||||
* OBB files, this directory may not exist.
|
||||
*/
|
||||
const char* obbPath;
|
||||
} GameActivity;
|
||||
|
||||
/**
|
||||
* The maximum number of axes supported in an Android MotionEvent.
|
||||
* See https://developer.android.com/ndk/reference/group/input.
|
||||
*/
|
||||
#define GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT 48
|
||||
|
||||
/**
|
||||
* \brief Describe information about a pointer, found in a
|
||||
* GameActivityMotionEvent.
|
||||
*
|
||||
* You can read values directly from this structure, or use helper functions
|
||||
* (`GameActivityPointerAxes_getX`, `GameActivityPointerAxes_getY` and
|
||||
* `GameActivityPointerAxes_getAxisValue`).
|
||||
*
|
||||
* The X axis and Y axis are enabled by default but any other axis that you want
|
||||
* to read **must** be enabled first, using
|
||||
* `GameActivityPointerAxes_enableAxis`.
|
||||
*
|
||||
* \see GameActivityMotionEvent
|
||||
*/
|
||||
typedef struct GameActivityPointerAxes {
|
||||
int32_t id;
|
||||
float axisValues[GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT];
|
||||
float rawX;
|
||||
float rawY;
|
||||
} GameActivityPointerAxes;
|
||||
|
||||
/** \brief Get the current X coordinate of the pointer. */
|
||||
inline float GameActivityPointerAxes_getX(
|
||||
const GameActivityPointerAxes* pointerInfo) {
|
||||
return pointerInfo->axisValues[AMOTION_EVENT_AXIS_X];
|
||||
}
|
||||
|
||||
/** \brief Get the current Y coordinate of the pointer. */
|
||||
inline float GameActivityPointerAxes_getY(
|
||||
const GameActivityPointerAxes* pointerInfo) {
|
||||
return pointerInfo->axisValues[AMOTION_EVENT_AXIS_Y];
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable the specified axis, so that its value is reported in the
|
||||
* GameActivityPointerAxes structures stored in a motion event.
|
||||
*
|
||||
* You must enable any axis that you want to read, apart from
|
||||
* `AMOTION_EVENT_AXIS_X` and `AMOTION_EVENT_AXIS_Y` that are enabled by
|
||||
* default.
|
||||
*
|
||||
* If the axis index is out of range, nothing is done.
|
||||
*/
|
||||
void GameActivityPointerAxes_enableAxis(int32_t axis);
|
||||
|
||||
/**
|
||||
* \brief Disable the specified axis. Its value won't be reported in the
|
||||
* GameActivityPointerAxes structures stored in a motion event anymore.
|
||||
*
|
||||
* Apart from X and Y, any axis that you want to read **must** be enabled first,
|
||||
* using `GameActivityPointerAxes_enableAxis`.
|
||||
*
|
||||
* If the axis index is out of range, nothing is done.
|
||||
*/
|
||||
void GameActivityPointerAxes_disableAxis(int32_t axis);
|
||||
|
||||
/**
|
||||
* \brief Get the value of the requested axis.
|
||||
*
|
||||
* Apart from X and Y, any axis that you want to read **must** be enabled first,
|
||||
* using `GameActivityPointerAxes_enableAxis`.
|
||||
*
|
||||
* Find the valid enums for the axis (`AMOTION_EVENT_AXIS_X`,
|
||||
* `AMOTION_EVENT_AXIS_Y`, `AMOTION_EVENT_AXIS_PRESSURE`...)
|
||||
* in https://developer.android.com/ndk/reference/group/input.
|
||||
*
|
||||
* @param pointerInfo The structure containing information about the pointer,
|
||||
* obtained from GameActivityMotionEvent.
|
||||
* @param axis The axis to get the value from
|
||||
* @return The value of the axis, or 0 if the axis is invalid or was not
|
||||
* enabled.
|
||||
*/
|
||||
inline float GameActivityPointerAxes_getAxisValue(
|
||||
GameActivityPointerAxes* pointerInfo, int32_t axis) {
|
||||
if (axis < 0 || axis >= GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pointerInfo->axisValues[axis];
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum number of pointers returned inside a motion event.
|
||||
*/
|
||||
#if (defined GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT_OVERRIDE)
|
||||
#define GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT \
|
||||
GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT_OVERRIDE
|
||||
#else
|
||||
#define GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT 8
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Describe a motion event that happened on the GameActivity SurfaceView.
|
||||
*
|
||||
* This is 1:1 mapping to the information contained in a Java `MotionEvent`
|
||||
* (see https://developer.android.com/reference/android/view/MotionEvent).
|
||||
*/
|
||||
typedef struct GameActivityMotionEvent {
|
||||
int32_t deviceId;
|
||||
int32_t source;
|
||||
int32_t action;
|
||||
|
||||
int64_t eventTime;
|
||||
int64_t downTime;
|
||||
|
||||
int32_t flags;
|
||||
int32_t metaState;
|
||||
|
||||
int32_t actionButton;
|
||||
int32_t buttonState;
|
||||
int32_t classification;
|
||||
int32_t edgeFlags;
|
||||
|
||||
uint32_t pointerCount;
|
||||
GameActivityPointerAxes
|
||||
pointers[GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT];
|
||||
|
||||
float precisionX;
|
||||
float precisionY;
|
||||
} GameActivityMotionEvent;
|
||||
|
||||
/**
|
||||
* \brief Describe a key event that happened on the GameActivity SurfaceView.
|
||||
*
|
||||
* This is 1:1 mapping to the information contained in a Java `KeyEvent`
|
||||
* (see https://developer.android.com/reference/android/view/KeyEvent).
|
||||
*/
|
||||
typedef struct GameActivityKeyEvent {
|
||||
int32_t deviceId;
|
||||
int32_t source;
|
||||
int32_t action;
|
||||
|
||||
int64_t eventTime;
|
||||
int64_t downTime;
|
||||
|
||||
int32_t flags;
|
||||
int32_t metaState;
|
||||
|
||||
int32_t modifiers;
|
||||
int32_t repeatCount;
|
||||
int32_t keyCode;
|
||||
} GameActivityKeyEvent;
|
||||
|
||||
/**
|
||||
* A function the user should call from their callback with the data, its length
|
||||
* and the library- supplied context.
|
||||
*/
|
||||
typedef void (*SaveInstanceStateRecallback)(const char* bytes, int len,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* These are the callbacks the framework makes into a native application.
|
||||
* All of these callbacks happen on the main thread of the application.
|
||||
* By default, all callbacks are NULL; set to a pointer to your own function
|
||||
* to have it called.
|
||||
*/
|
||||
typedef struct GameActivityCallbacks {
|
||||
/**
|
||||
* GameActivity has started. See Java documentation for Activity.onStart()
|
||||
* for more information.
|
||||
*/
|
||||
void (*onStart)(GameActivity* activity);
|
||||
|
||||
/**
|
||||
* GameActivity has resumed. See Java documentation for Activity.onResume()
|
||||
* for more information.
|
||||
*/
|
||||
void (*onResume)(GameActivity* activity);
|
||||
|
||||
/**
|
||||
* The framework is asking GameActivity to save its current instance state.
|
||||
* See the Java documentation for Activity.onSaveInstanceState() for more
|
||||
* information. The user should call the recallback with their data, its
|
||||
* length and the provided context; they retain ownership of the data. Note
|
||||
* that the saved state will be persisted, so it can not contain any active
|
||||
* entities (pointers to memory, file descriptors, etc).
|
||||
*/
|
||||
void (*onSaveInstanceState)(GameActivity* activity,
|
||||
SaveInstanceStateRecallback recallback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* GameActivity has paused. See Java documentation for Activity.onPause()
|
||||
* for more information.
|
||||
*/
|
||||
void (*onPause)(GameActivity* activity);
|
||||
|
||||
/**
|
||||
* GameActivity has stopped. See Java documentation for Activity.onStop()
|
||||
* for more information.
|
||||
*/
|
||||
void (*onStop)(GameActivity* activity);
|
||||
|
||||
/**
|
||||
* GameActivity is being destroyed. See Java documentation for
|
||||
* Activity.onDestroy() for more information.
|
||||
*/
|
||||
void (*onDestroy)(GameActivity* activity);
|
||||
|
||||
/**
|
||||
* Focus has changed in this GameActivity's window. This is often used,
|
||||
* for example, to pause a game when it loses input focus.
|
||||
*/
|
||||
void (*onWindowFocusChanged)(GameActivity* activity, bool hasFocus);
|
||||
|
||||
/**
|
||||
* The drawing window for this native activity has been created. You
|
||||
* can use the given native window object to start drawing.
|
||||
*/
|
||||
void (*onNativeWindowCreated)(GameActivity* activity,
|
||||
ANativeWindow* window);
|
||||
|
||||
/**
|
||||
* The drawing window for this native activity has been resized. You should
|
||||
* retrieve the new size from the window and ensure that your rendering in
|
||||
* it now matches.
|
||||
*/
|
||||
void (*onNativeWindowResized)(GameActivity* activity, ANativeWindow* window,
|
||||
int32_t newWidth, int32_t newHeight);
|
||||
|
||||
/**
|
||||
* The drawing window for this native activity needs to be redrawn. To
|
||||
* avoid transient artifacts during screen changes (such resizing after
|
||||
* rotation), applications should not return from this function until they
|
||||
* have finished drawing their window in its current state.
|
||||
*/
|
||||
void (*onNativeWindowRedrawNeeded)(GameActivity* activity,
|
||||
ANativeWindow* window);
|
||||
|
||||
/**
|
||||
* The drawing window for this native activity is going to be destroyed.
|
||||
* You MUST ensure that you do not touch the window object after returning
|
||||
* from this function: in the common case of drawing to the window from
|
||||
* another thread, that means the implementation of this callback must
|
||||
* properly synchronize with the other thread to stop its drawing before
|
||||
* returning from here.
|
||||
*/
|
||||
void (*onNativeWindowDestroyed)(GameActivity* activity,
|
||||
ANativeWindow* window);
|
||||
|
||||
/**
|
||||
* The current device AConfiguration has changed. The new configuration can
|
||||
* be retrieved from assetManager.
|
||||
*/
|
||||
void (*onConfigurationChanged)(GameActivity* activity);
|
||||
|
||||
/**
|
||||
* The system is running low on memory. Use this callback to release
|
||||
* resources you do not need, to help the system avoid killing more
|
||||
* important processes.
|
||||
*/
|
||||
void (*onTrimMemory)(GameActivity* activity, int level);
|
||||
|
||||
/**
|
||||
* Callback called for every MotionEvent done on the GameActivity
|
||||
* SurfaceView. Ownership of `event` is maintained by the library and it is
|
||||
* only valid during the callback.
|
||||
*/
|
||||
bool (*onTouchEvent)(GameActivity* activity,
|
||||
const GameActivityMotionEvent* event);
|
||||
|
||||
/**
|
||||
* Callback called for every key down event on the GameActivity SurfaceView.
|
||||
* Ownership of `event` is maintained by the library and it is only valid
|
||||
* during the callback.
|
||||
*/
|
||||
bool (*onKeyDown)(GameActivity* activity,
|
||||
const GameActivityKeyEvent* event);
|
||||
|
||||
/**
|
||||
* Callback called for every key up event on the GameActivity SurfaceView.
|
||||
* Ownership of `event` is maintained by the library and it is only valid
|
||||
* during the callback.
|
||||
*/
|
||||
bool (*onKeyUp)(GameActivity* activity, const GameActivityKeyEvent* event);
|
||||
|
||||
/**
|
||||
* Callback called for every soft-keyboard text input event.
|
||||
* Ownership of `state` is maintained by the library and it is only valid
|
||||
* during the callback.
|
||||
*/
|
||||
void (*onTextInputEvent)(GameActivity* activity,
|
||||
const GameTextInputState* state);
|
||||
|
||||
/**
|
||||
* Callback called when WindowInsets of the main app window have changed.
|
||||
* Call GameActivity_getWindowInsets to retrieve the insets themselves.
|
||||
*/
|
||||
void (*onWindowInsetsChanged)(GameActivity* activity);
|
||||
} GameActivityCallbacks;
|
||||
|
||||
/**
|
||||
* \brief Convert a Java `MotionEvent` to a `GameActivityMotionEvent`.
|
||||
*
|
||||
* This is done automatically by the GameActivity: see `onTouchEvent` to set
|
||||
* a callback to consume the received events.
|
||||
* This function can be used if you re-implement events handling in your own
|
||||
* activity.
|
||||
* Ownership of out_event is maintained by the caller.
|
||||
*/
|
||||
void GameActivityMotionEvent_fromJava(JNIEnv* env, jobject motionEvent,
|
||||
GameActivityMotionEvent* out_event);
|
||||
|
||||
/**
|
||||
* \brief Convert a Java `KeyEvent` to a `GameActivityKeyEvent`.
|
||||
*
|
||||
* This is done automatically by the GameActivity: see `onKeyUp` and `onKeyDown`
|
||||
* to set a callback to consume the received events.
|
||||
* This function can be used if you re-implement events handling in your own
|
||||
* activity.
|
||||
* Ownership of out_event is maintained by the caller.
|
||||
*/
|
||||
void GameActivityKeyEvent_fromJava(JNIEnv* env, jobject motionEvent,
|
||||
GameActivityKeyEvent* out_event);
|
||||
|
||||
/**
|
||||
* This is the function that must be in the native code to instantiate the
|
||||
* application's native activity. It is called with the activity instance (see
|
||||
* above); if the code is being instantiated from a previously saved instance,
|
||||
* the savedState will be non-NULL and point to the saved data. You must make
|
||||
* any copy of this data you need -- it will be released after you return from
|
||||
* this function.
|
||||
*/
|
||||
typedef void GameActivity_createFunc(GameActivity* activity, void* savedState,
|
||||
size_t savedStateSize);
|
||||
|
||||
/**
|
||||
* The name of the function that NativeInstance looks for when launching its
|
||||
* native code. This is the default function that is used, you can specify
|
||||
* "android.app.func_name" string meta-data in your manifest to use a different
|
||||
* function.
|
||||
*/
|
||||
extern GameActivity_createFunc GameActivity_onCreate;
|
||||
|
||||
/**
|
||||
* Finish the given activity. Its finish() method will be called, causing it
|
||||
* to be stopped and destroyed. Note that this method can be called from
|
||||
* *any* thread; it will send a message to the main thread of the process
|
||||
* where the Java finish call will take place.
|
||||
*/
|
||||
void GameActivity_finish(GameActivity* activity);
|
||||
|
||||
/**
|
||||
* Flags for GameActivity_setWindowFlags,
|
||||
* as per the Java API at android.view.WindowManager.LayoutParams.
|
||||
*/
|
||||
enum GameActivitySetWindowFlags {
|
||||
/**
|
||||
* As long as this window is visible to the user, allow the lock
|
||||
* screen to activate while the screen is on. This can be used
|
||||
* independently, or in combination with {@link
|
||||
* GAMEACTIVITY_FLAG_KEEP_SCREEN_ON} and/or {@link
|
||||
* GAMEACTIVITY_FLAG_SHOW_WHEN_LOCKED}
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
|
||||
/** Everything behind this window will be dimmed. */
|
||||
GAMEACTIVITY_FLAG_DIM_BEHIND = 0x00000002,
|
||||
/**
|
||||
* Blur everything behind this window.
|
||||
* @deprecated Blurring is no longer supported.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_BLUR_BEHIND = 0x00000004,
|
||||
/**
|
||||
* This window won't ever get key input focus, so the
|
||||
* user can not send key or other button events to it. Those will
|
||||
* instead go to whatever focusable window is behind it. This flag
|
||||
* will also enable {@link GAMEACTIVITY_FLAG_NOT_TOUCH_MODAL} whether or not
|
||||
* that is explicitly set.
|
||||
*
|
||||
* Setting this flag also implies that the window will not need to
|
||||
* interact with
|
||||
* a soft input method, so it will be Z-ordered and positioned
|
||||
* independently of any active input method (typically this means it
|
||||
* gets Z-ordered on top of the input method, so it can use the full
|
||||
* screen for its content and cover the input method if needed. You
|
||||
* can use {@link GAMEACTIVITY_FLAG_ALT_FOCUSABLE_IM} to modify this
|
||||
* behavior.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_NOT_FOCUSABLE = 0x00000008,
|
||||
/** This window can never receive touch events. */
|
||||
GAMEACTIVITY_FLAG_NOT_TOUCHABLE = 0x00000010,
|
||||
/**
|
||||
* Even when this window is focusable (its
|
||||
* {@link GAMEACTIVITY_FLAG_NOT_FOCUSABLE} is not set), allow any pointer
|
||||
* events outside of the window to be sent to the windows behind it.
|
||||
* Otherwise it will consume all pointer events itself, regardless of
|
||||
* whether they are inside of the window.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_NOT_TOUCH_MODAL = 0x00000020,
|
||||
/**
|
||||
* When set, if the device is asleep when the touch
|
||||
* screen is pressed, you will receive this first touch event. Usually
|
||||
* the first touch event is consumed by the system since the user can
|
||||
* not see what they are pressing on.
|
||||
*
|
||||
* @deprecated This flag has no effect.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
|
||||
/**
|
||||
* As long as this window is visible to the user, keep
|
||||
* the device's screen turned on and bright.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_KEEP_SCREEN_ON = 0x00000080,
|
||||
/**
|
||||
* Place the window within the entire screen, ignoring
|
||||
* decorations around the border (such as the status bar). The
|
||||
* window must correctly position its contents to take the screen
|
||||
* decoration into account.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_LAYOUT_IN_SCREEN = 0x00000100,
|
||||
/** Allows the window to extend outside of the screen. */
|
||||
GAMEACTIVITY_FLAG_LAYOUT_NO_LIMITS = 0x00000200,
|
||||
/**
|
||||
* Hide all screen decorations (such as the status
|
||||
* bar) while this window is displayed. This allows the window to
|
||||
* use the entire display space for itself -- the status bar will
|
||||
* be hidden when an app window with this flag set is on the top
|
||||
* layer. A fullscreen window will ignore a value of {@link
|
||||
* GAMEACTIVITY_SOFT_INPUT_ADJUST_RESIZE}; the window will stay
|
||||
* fullscreen and will not resize.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_FULLSCREEN = 0x00000400,
|
||||
/**
|
||||
* Override {@link GAMEACTIVITY_FLAG_FULLSCREEN} and force the
|
||||
* screen decorations (such as the status bar) to be shown.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
|
||||
/**
|
||||
* Turn on dithering when compositing this window to
|
||||
* the screen.
|
||||
* @deprecated This flag is no longer used.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_DITHER = 0x00001000,
|
||||
/**
|
||||
* Treat the content of the window as secure, preventing
|
||||
* it from appearing in screenshots or from being viewed on non-secure
|
||||
* displays.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_SECURE = 0x00002000,
|
||||
/**
|
||||
* A special mode where the layout parameters are used
|
||||
* to perform scaling of the surface when it is composited to the
|
||||
* screen.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_SCALED = 0x00004000,
|
||||
/**
|
||||
* Intended for windows that will often be used when the user is
|
||||
* holding the screen against their face, it will aggressively
|
||||
* filter the event stream to prevent unintended presses in this
|
||||
* situation that may not be desired for a particular window, when
|
||||
* such an event stream is detected, the application will receive
|
||||
* a {@link AMOTION_EVENT_ACTION_CANCEL} to indicate this so
|
||||
* applications can handle this accordingly by taking no action on
|
||||
* the event until the finger is released.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
|
||||
/**
|
||||
* A special option only for use in combination with
|
||||
* {@link GAMEACTIVITY_FLAG_LAYOUT_IN_SCREEN}. When requesting layout in
|
||||
* the screen your window may appear on top of or behind screen decorations
|
||||
* such as the status bar. By also including this flag, the window
|
||||
* manager will report the inset rectangle needed to ensure your
|
||||
* content is not covered by screen decorations.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_LAYOUT_INSET_DECOR = 0x00010000,
|
||||
/**
|
||||
* Invert the state of {@link GAMEACTIVITY_FLAG_NOT_FOCUSABLE} with
|
||||
* respect to how this window interacts with the current method.
|
||||
* That is, if FLAG_NOT_FOCUSABLE is set and this flag is set,
|
||||
* then the window will behave as if it needs to interact with the
|
||||
* input method and thus be placed behind/away from it; if {@link
|
||||
* GAMEACTIVITY_FLAG_NOT_FOCUSABLE} is not set and this flag is set,
|
||||
* then the window will behave as if it doesn't need to interact
|
||||
* with the input method and can be placed to use more space and
|
||||
* cover the input method.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_ALT_FOCUSABLE_IM = 0x00020000,
|
||||
/**
|
||||
* If you have set {@link GAMEACTIVITY_FLAG_NOT_TOUCH_MODAL}, you
|
||||
* can set this flag to receive a single special MotionEvent with
|
||||
* the action
|
||||
* {@link AMOTION_EVENT_ACTION_OUTSIDE} for
|
||||
* touches that occur outside of your window. Note that you will not
|
||||
* receive the full down/move/up gesture, only the location of the
|
||||
* first down as an {@link AMOTION_EVENT_ACTION_OUTSIDE}.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
|
||||
/**
|
||||
* Special flag to let windows be shown when the screen
|
||||
* is locked. This will let application windows take precedence over
|
||||
* key guard or any other lock screens. Can be used with
|
||||
* {@link GAMEACTIVITY_FLAG_KEEP_SCREEN_ON} to turn screen on and display
|
||||
* windows directly before showing the key guard window. Can be used with
|
||||
* {@link GAMEACTIVITY_FLAG_DISMISS_KEYGUARD} to automatically fully
|
||||
* dismisss non-secure keyguards. This flag only applies to the top-most
|
||||
* full-screen window.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_SHOW_WHEN_LOCKED = 0x00080000,
|
||||
/**
|
||||
* Ask that the system wallpaper be shown behind
|
||||
* your window. The window surface must be translucent to be able
|
||||
* to actually see the wallpaper behind it; this flag just ensures
|
||||
* that the wallpaper surface will be there if this window actually
|
||||
* has translucent regions.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_SHOW_WALLPAPER = 0x00100000,
|
||||
/**
|
||||
* When set as a window is being added or made
|
||||
* visible, once the window has been shown then the system will
|
||||
* poke the power manager's user activity (as if the user had woken
|
||||
* up the device) to turn the screen on.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_TURN_SCREEN_ON = 0x00200000,
|
||||
/**
|
||||
* When set the window will cause the keyguard to
|
||||
* be dismissed, only if it is not a secure lock keyguard. Because such
|
||||
* a keyguard is not needed for security, it will never re-appear if
|
||||
* the user navigates to another window (in contrast to
|
||||
* {@link GAMEACTIVITY_FLAG_SHOW_WHEN_LOCKED}, which will only temporarily
|
||||
* hide both secure and non-secure keyguards but ensure they reappear
|
||||
* when the user moves to another UI that doesn't hide them).
|
||||
* If the keyguard is currently active and is secure (requires an
|
||||
* unlock pattern) than the user will still need to confirm it before
|
||||
* seeing this window, unless {@link GAMEACTIVITY_FLAG_SHOW_WHEN_LOCKED} has
|
||||
* also been set.
|
||||
*/
|
||||
GAMEACTIVITY_FLAG_DISMISS_KEYGUARD = 0x00400000,
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the window flags of the given activity. Calls getWindow().setFlags()
|
||||
* of the given activity.
|
||||
* Note that some flags must be set before the window decoration is created,
|
||||
* see
|
||||
* https://developer.android.com/reference/android/view/Window#setFlags(int,%20int).
|
||||
* Note also that this method can be called from
|
||||
* *any* thread; it will send a message to the main thread of the process
|
||||
* where the Java finish call will take place.
|
||||
*/
|
||||
void GameActivity_setWindowFlags(GameActivity* activity, uint32_t addFlags,
|
||||
uint32_t removeFlags);
|
||||
|
||||
/**
|
||||
* Flags for GameActivity_showSoftInput; see the Java InputMethodManager
|
||||
* API for documentation.
|
||||
*/
|
||||
enum GameActivityShowSoftInputFlags {
|
||||
/**
|
||||
* Implicit request to show the input window, not as the result
|
||||
* of a direct request by the user.
|
||||
*/
|
||||
GAMEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT = 0x0001,
|
||||
|
||||
/**
|
||||
* The user has forced the input method open (such as by
|
||||
* long-pressing menu) so it should not be closed until they
|
||||
* explicitly do so.
|
||||
*/
|
||||
GAMEACTIVITY_SHOW_SOFT_INPUT_FORCED = 0x0002,
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the IME while in the given activity. Calls
|
||||
* InputMethodManager.showSoftInput() for the given activity. Note that this
|
||||
* method can be called from *any* thread; it will send a message to the main
|
||||
* thread of the process where the Java call will take place.
|
||||
*/
|
||||
void GameActivity_showSoftInput(GameActivity* activity, uint32_t flags);
|
||||
|
||||
/**
|
||||
* Set the text entry state (see documentation of the GameTextInputState struct
|
||||
* in the Game Text Input library reference).
|
||||
*
|
||||
* Ownership of the state is maintained by the caller.
|
||||
*/
|
||||
void GameActivity_setTextInputState(GameActivity* activity,
|
||||
const GameTextInputState* state);
|
||||
|
||||
/**
|
||||
* Get the last-received text entry state (see documentation of the
|
||||
* GameTextInputState struct in the Game Text Input library reference).
|
||||
*
|
||||
*/
|
||||
void GameActivity_getTextInputState(GameActivity* activity,
|
||||
GameTextInputGetStateCallback callback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* Get a pointer to the GameTextInput library instance.
|
||||
*/
|
||||
GameTextInput* GameActivity_getTextInput(const GameActivity* activity);
|
||||
|
||||
/**
|
||||
* Flags for GameActivity_hideSoftInput; see the Java InputMethodManager
|
||||
* API for documentation.
|
||||
*/
|
||||
enum GameActivityHideSoftInputFlags {
|
||||
/**
|
||||
* The soft input window should only be hidden if it was not
|
||||
* explicitly shown by the user.
|
||||
*/
|
||||
GAMEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY = 0x0001,
|
||||
/**
|
||||
* The soft input window should normally be hidden, unless it was
|
||||
* originally shown with {@link GAMEACTIVITY_SHOW_SOFT_INPUT_FORCED}.
|
||||
*/
|
||||
GAMEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS = 0x0002,
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the IME while in the given activity. Calls
|
||||
* InputMethodManager.hideSoftInput() for the given activity. Note that this
|
||||
* method can be called from *any* thread; it will send a message to the main
|
||||
* thread of the process where the Java finish call will take place.
|
||||
*/
|
||||
void GameActivity_hideSoftInput(GameActivity* activity, uint32_t flags);
|
||||
|
||||
/**
|
||||
* Get the current window insets of the particular component. See
|
||||
* https://developer.android.com/reference/androidx/core/view/WindowInsetsCompat.Type
|
||||
* for more details.
|
||||
* You can use these insets to influence what you show on the screen.
|
||||
*/
|
||||
void GameActivity_getWindowInsets(GameActivity* activity,
|
||||
GameCommonInsetsType type, ARect* insets);
|
||||
|
||||
/**
|
||||
* Set options on how the IME behaves when it is requested for text input.
|
||||
* See
|
||||
* https://developer.android.com/reference/android/view/inputmethod/EditorInfo
|
||||
* for the meaning of inputType, actionId and imeOptions.
|
||||
*
|
||||
* Note that this function will attach the current thread to the JVM if it is
|
||||
* not already attached, so the caller must detach the thread from the JVM
|
||||
* before the thread is destroyed using DetachCurrentThread.
|
||||
*/
|
||||
void GameActivity_setImeEditorInfo(GameActivity* activity, int inputType,
|
||||
int actionId, int imeOptions);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif // ANDROID_GAME_SDK_GAME_ACTIVITY_H
|
||||
|
|
@ -0,0 +1,571 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android_native_app_glue.h"
|
||||
|
||||
#include <android/log.h>
|
||||
#include <errno.h>
|
||||
#include <jni.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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__))
|
||||
|
||||
/* For debug builds, always enable the debug traces in this library */
|
||||
#ifndef NDEBUG
|
||||
#define LOGV(...) \
|
||||
((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", \
|
||||
__VA_ARGS__))
|
||||
#else
|
||||
#define LOGV(...) ((void)0)
|
||||
#endif
|
||||
|
||||
static void free_saved_state(struct android_app* android_app) {
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
if (android_app->savedState != NULL) {
|
||||
free(android_app->savedState);
|
||||
android_app->savedState = NULL;
|
||||
android_app->savedStateSize = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
}
|
||||
|
||||
int8_t android_app_read_cmd(struct android_app* android_app) {
|
||||
int8_t cmd;
|
||||
if (read(android_app->msgread, &cmd, sizeof(cmd)) != sizeof(cmd)) {
|
||||
LOGE("No data on command pipe!");
|
||||
return -1;
|
||||
}
|
||||
if (cmd == APP_CMD_SAVE_STATE) free_saved_state(android_app);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
static void print_cur_config(struct android_app* android_app) {
|
||||
char lang[2], country[2];
|
||||
AConfiguration_getLanguage(android_app->config, lang);
|
||||
AConfiguration_getCountry(android_app->config, country);
|
||||
|
||||
LOGV(
|
||||
"Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
|
||||
"keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
|
||||
"modetype=%d modenight=%d",
|
||||
AConfiguration_getMcc(android_app->config),
|
||||
AConfiguration_getMnc(android_app->config), lang[0], lang[1],
|
||||
country[0], country[1],
|
||||
AConfiguration_getOrientation(android_app->config),
|
||||
AConfiguration_getTouchscreen(android_app->config),
|
||||
AConfiguration_getDensity(android_app->config),
|
||||
AConfiguration_getKeyboard(android_app->config),
|
||||
AConfiguration_getNavigation(android_app->config),
|
||||
AConfiguration_getKeysHidden(android_app->config),
|
||||
AConfiguration_getNavHidden(android_app->config),
|
||||
AConfiguration_getSdkVersion(android_app->config),
|
||||
AConfiguration_getScreenSize(android_app->config),
|
||||
AConfiguration_getScreenLong(android_app->config),
|
||||
AConfiguration_getUiModeType(android_app->config),
|
||||
AConfiguration_getUiModeNight(android_app->config));
|
||||
}
|
||||
|
||||
void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) {
|
||||
switch (cmd) {
|
||||
case UNUSED_APP_CMD_INPUT_CHANGED:
|
||||
LOGV("UNUSED_APP_CMD_INPUT_CHANGED");
|
||||
// Do nothing. This can be used in the future to handle AInputQueue
|
||||
// natively, like done in NativeActivity.
|
||||
break;
|
||||
|
||||
case APP_CMD_INIT_WINDOW:
|
||||
LOGV("APP_CMD_INIT_WINDOW");
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
android_app->window = android_app->pendingWindow;
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
break;
|
||||
|
||||
case APP_CMD_TERM_WINDOW:
|
||||
LOGV("APP_CMD_TERM_WINDOW");
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
break;
|
||||
|
||||
case APP_CMD_RESUME:
|
||||
case APP_CMD_START:
|
||||
case APP_CMD_PAUSE:
|
||||
case APP_CMD_STOP:
|
||||
LOGV("activityState=%d", cmd);
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
android_app->activityState = cmd;
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
break;
|
||||
|
||||
case APP_CMD_CONFIG_CHANGED:
|
||||
LOGV("APP_CMD_CONFIG_CHANGED");
|
||||
AConfiguration_fromAssetManager(
|
||||
android_app->config, android_app->activity->assetManager);
|
||||
print_cur_config(android_app);
|
||||
break;
|
||||
|
||||
case APP_CMD_DESTROY:
|
||||
LOGV("APP_CMD_DESTROY");
|
||||
android_app->destroyRequested = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) {
|
||||
switch (cmd) {
|
||||
case APP_CMD_TERM_WINDOW:
|
||||
LOGV("APP_CMD_TERM_WINDOW");
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
android_app->window = NULL;
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
break;
|
||||
|
||||
case APP_CMD_SAVE_STATE:
|
||||
LOGV("APP_CMD_SAVE_STATE");
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
android_app->stateSaved = 1;
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
break;
|
||||
|
||||
case APP_CMD_RESUME:
|
||||
free_saved_state(android_app);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void app_dummy() {}
|
||||
|
||||
static void android_app_destroy(struct android_app* android_app) {
|
||||
LOGV("android_app_destroy!");
|
||||
free_saved_state(android_app);
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
|
||||
AConfiguration_delete(android_app->config);
|
||||
android_app->destroyed = 1;
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
// Can't touch android_app object after this.
|
||||
}
|
||||
|
||||
static void process_cmd(struct android_app* app,
|
||||
struct android_poll_source* source) {
|
||||
int8_t cmd = android_app_read_cmd(app);
|
||||
android_app_pre_exec_cmd(app, cmd);
|
||||
if (app->onAppCmd != NULL) app->onAppCmd(app, cmd);
|
||||
android_app_post_exec_cmd(app, cmd);
|
||||
}
|
||||
|
||||
// This is run on a separate thread (i.e: not the main thread).
|
||||
static void* android_app_entry(void* param) {
|
||||
struct android_app* android_app = (struct android_app*)param;
|
||||
|
||||
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);
|
||||
|
||||
android_app->cmdPollSource.id = LOOPER_ID_MAIN;
|
||||
android_app->cmdPollSource.app = android_app;
|
||||
android_app->cmdPollSource.process = process_cmd;
|
||||
|
||||
ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
|
||||
ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN,
|
||||
ALOOPER_EVENT_INPUT, NULL, &android_app->cmdPollSource);
|
||||
android_app->looper = looper;
|
||||
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
android_app->running = 1;
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
|
||||
android_main(android_app);
|
||||
|
||||
android_app_destroy(android_app);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Codes from https://developer.android.com/reference/android/view/KeyEvent
|
||||
#define KEY_EVENT_KEYCODE_VOLUME_DOWN 25
|
||||
#define KEY_EVENT_KEYCODE_VOLUME_MUTE 164
|
||||
#define KEY_EVENT_KEYCODE_VOLUME_UP 24
|
||||
#define KEY_EVENT_KEYCODE_CAMERA 27
|
||||
#define KEY_EVENT_KEYCODE_ZOOM_IN 168
|
||||
#define KEY_EVENT_KEYCODE_ZOOM_OUT 169
|
||||
|
||||
// Double-buffer the key event filter to avoid race condition.
|
||||
static bool default_key_filter(const GameActivityKeyEvent* event) {
|
||||
// Ignore camera, volume, etc. buttons
|
||||
return !(event->keyCode == KEY_EVENT_KEYCODE_VOLUME_DOWN ||
|
||||
event->keyCode == KEY_EVENT_KEYCODE_VOLUME_MUTE ||
|
||||
event->keyCode == KEY_EVENT_KEYCODE_VOLUME_UP ||
|
||||
event->keyCode == KEY_EVENT_KEYCODE_CAMERA ||
|
||||
event->keyCode == KEY_EVENT_KEYCODE_ZOOM_IN ||
|
||||
event->keyCode == KEY_EVENT_KEYCODE_ZOOM_OUT);
|
||||
}
|
||||
|
||||
// See
|
||||
// https://developer.android.com/reference/android/view/InputDevice#SOURCE_TOUCHSCREEN
|
||||
#define SOURCE_TOUCHSCREEN 0x00001002
|
||||
|
||||
static bool default_motion_filter(const GameActivityMotionEvent* event) {
|
||||
// Ignore any non-touch events.
|
||||
return event->source == SOURCE_TOUCHSCREEN;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Native activity interaction (called from main thread)
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
static struct android_app* android_app_create(GameActivity* activity,
|
||||
void* savedState,
|
||||
size_t savedStateSize) {
|
||||
// struct android_app* android_app = calloc(1, sizeof(struct android_app));
|
||||
struct android_app* android_app =
|
||||
(struct android_app*)malloc(sizeof(struct android_app));
|
||||
memset(android_app, 0, sizeof(struct android_app));
|
||||
android_app->activity = activity;
|
||||
|
||||
pthread_mutex_init(&android_app->mutex, NULL);
|
||||
pthread_cond_init(&android_app->cond, NULL);
|
||||
|
||||
if (savedState != NULL) {
|
||||
android_app->savedState = malloc(savedStateSize);
|
||||
android_app->savedStateSize = savedStateSize;
|
||||
memcpy(android_app->savedState, savedState, savedStateSize);
|
||||
}
|
||||
|
||||
int msgpipe[2];
|
||||
if (pipe(msgpipe)) {
|
||||
LOGE("could not create pipe: %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
android_app->msgread = msgpipe[0];
|
||||
android_app->msgwrite = msgpipe[1];
|
||||
|
||||
android_app->keyEventFilter = default_key_filter;
|
||||
android_app->motionEventFilter = default_motion_filter;
|
||||
|
||||
LOGV("Launching android_app_entry in a thread");
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
|
||||
|
||||
// Wait for thread to start.
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
while (!android_app->running) {
|
||||
pthread_cond_wait(&android_app->cond, &android_app->mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
|
||||
return android_app;
|
||||
}
|
||||
|
||||
static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) {
|
||||
if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) {
|
||||
LOGE("Failure writing android_app cmd: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void android_app_set_window(struct android_app* android_app,
|
||||
ANativeWindow* window) {
|
||||
LOGV("android_app_set_window called");
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
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);
|
||||
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) {
|
||||
pthread_mutex_lock(&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);
|
||||
|
||||
close(android_app->msgread);
|
||||
close(android_app->msgwrite);
|
||||
pthread_cond_destroy(&android_app->cond);
|
||||
pthread_mutex_destroy(&android_app->mutex);
|
||||
free(android_app);
|
||||
}
|
||||
|
||||
static inline struct android_app* ToApp(GameActivity* activity) {
|
||||
return (struct android_app*)activity->instance;
|
||||
}
|
||||
|
||||
static void onDestroy(GameActivity* activity) {
|
||||
LOGV("Destroy: %p", activity);
|
||||
android_app_free(ToApp(activity));
|
||||
}
|
||||
|
||||
static void onStart(GameActivity* activity) {
|
||||
LOGV("Start: %p", activity);
|
||||
android_app_set_activity_state(ToApp(activity), APP_CMD_START);
|
||||
}
|
||||
|
||||
static void onResume(GameActivity* activity) {
|
||||
LOGV("Resume: %p", activity);
|
||||
android_app_set_activity_state(ToApp(activity), APP_CMD_RESUME);
|
||||
}
|
||||
|
||||
static void onSaveInstanceState(GameActivity* activity,
|
||||
SaveInstanceStateRecallback recallback,
|
||||
void* context) {
|
||||
LOGV("SaveInstanceState: %p", activity);
|
||||
|
||||
struct android_app* android_app = ToApp(activity);
|
||||
void* savedState = NULL;
|
||||
pthread_mutex_lock(&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;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
}
|
||||
|
||||
static void onPause(GameActivity* activity) {
|
||||
LOGV("Pause: %p", activity);
|
||||
android_app_set_activity_state(ToApp(activity), APP_CMD_PAUSE);
|
||||
}
|
||||
|
||||
static void onStop(GameActivity* activity) {
|
||||
LOGV("Stop: %p", activity);
|
||||
android_app_set_activity_state(ToApp(activity), APP_CMD_STOP);
|
||||
}
|
||||
|
||||
static void onConfigurationChanged(GameActivity* activity) {
|
||||
LOGV("ConfigurationChanged: %p", activity);
|
||||
android_app_write_cmd(ToApp(activity), APP_CMD_CONFIG_CHANGED);
|
||||
}
|
||||
|
||||
static void onTrimMemory(GameActivity* activity, int level) {
|
||||
LOGV("TrimMemory: %p %d", activity, level);
|
||||
android_app_write_cmd(ToApp(activity), APP_CMD_LOW_MEMORY);
|
||||
}
|
||||
|
||||
static void onWindowFocusChanged(GameActivity* activity, bool focused) {
|
||||
LOGV("WindowFocusChanged: %p -- %d", activity, focused);
|
||||
android_app_write_cmd(ToApp(activity),
|
||||
focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
|
||||
}
|
||||
|
||||
static void onNativeWindowCreated(GameActivity* activity,
|
||||
ANativeWindow* window) {
|
||||
LOGV("NativeWindowCreated: %p -- %p", activity, window);
|
||||
android_app_set_window(ToApp(activity), window);
|
||||
}
|
||||
|
||||
static void onNativeWindowDestroyed(GameActivity* activity,
|
||||
ANativeWindow* window) {
|
||||
LOGV("NativeWindowDestroyed: %p -- %p", activity, window);
|
||||
android_app_set_window(ToApp(activity), NULL);
|
||||
}
|
||||
|
||||
static void onNativeWindowRedrawNeeded(GameActivity* activity,
|
||||
ANativeWindow* window) {
|
||||
LOGV("NativeWindowRedrawNeeded: %p -- %p", activity, window);
|
||||
android_app_write_cmd(ToApp(activity), APP_CMD_WINDOW_REDRAW_NEEDED);
|
||||
}
|
||||
|
||||
static void onNativeWindowResized(GameActivity* activity, ANativeWindow* window,
|
||||
int32_t width, int32_t height) {
|
||||
LOGV("NativeWindowResized: %p -- %p ( %d x %d )", activity, window, width,
|
||||
height);
|
||||
android_app_write_cmd(ToApp(activity), APP_CMD_WINDOW_RESIZED);
|
||||
}
|
||||
|
||||
void android_app_set_motion_event_filter(struct android_app* app,
|
||||
android_motion_event_filter filter) {
|
||||
pthread_mutex_lock(&app->mutex);
|
||||
app->motionEventFilter = filter;
|
||||
pthread_mutex_unlock(&app->mutex);
|
||||
}
|
||||
|
||||
static bool onTouchEvent(GameActivity* activity,
|
||||
const GameActivityMotionEvent* event) {
|
||||
struct android_app* android_app = ToApp(activity);
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
|
||||
if (android_app->motionEventFilter != NULL &&
|
||||
!android_app->motionEventFilter(event)) {
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct android_input_buffer* inputBuffer =
|
||||
&android_app->inputBuffers[android_app->currentInputBuffer];
|
||||
|
||||
// Add to the list of active motion events
|
||||
if (inputBuffer->motionEventsCount <
|
||||
NATIVE_APP_GLUE_MAX_NUM_MOTION_EVENTS) {
|
||||
int new_ix = inputBuffer->motionEventsCount;
|
||||
memcpy(&inputBuffer->motionEvents[new_ix], event,
|
||||
sizeof(GameActivityMotionEvent));
|
||||
++inputBuffer->motionEventsCount;
|
||||
}
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct android_input_buffer* android_app_swap_input_buffers(
|
||||
struct android_app* android_app) {
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
|
||||
struct android_input_buffer* inputBuffer =
|
||||
&android_app->inputBuffers[android_app->currentInputBuffer];
|
||||
|
||||
if (inputBuffer->motionEventsCount == 0 &&
|
||||
inputBuffer->keyEventsCount == 0) {
|
||||
inputBuffer = NULL;
|
||||
} else {
|
||||
android_app->currentInputBuffer =
|
||||
(android_app->currentInputBuffer + 1) %
|
||||
NATIVE_APP_GLUE_MAX_INPUT_BUFFERS;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
|
||||
return inputBuffer;
|
||||
}
|
||||
|
||||
void android_app_clear_motion_events(struct android_input_buffer* inputBuffer) {
|
||||
inputBuffer->motionEventsCount = 0;
|
||||
}
|
||||
|
||||
void android_app_set_key_event_filter(struct android_app* app,
|
||||
android_key_event_filter filter) {
|
||||
pthread_mutex_lock(&app->mutex);
|
||||
app->keyEventFilter = filter;
|
||||
pthread_mutex_unlock(&app->mutex);
|
||||
}
|
||||
|
||||
static bool onKey(GameActivity* activity, const GameActivityKeyEvent* event) {
|
||||
struct android_app* android_app = ToApp(activity);
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
|
||||
if (android_app->keyEventFilter != NULL &&
|
||||
!android_app->keyEventFilter(event)) {
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct android_input_buffer* inputBuffer =
|
||||
&android_app->inputBuffers[android_app->currentInputBuffer];
|
||||
|
||||
// Add to the list of active key down events
|
||||
if (inputBuffer->keyEventsCount < NATIVE_APP_GLUE_MAX_NUM_KEY_EVENTS) {
|
||||
int new_ix = inputBuffer->keyEventsCount;
|
||||
memcpy(&inputBuffer->keyEvents[new_ix], event,
|
||||
sizeof(GameActivityKeyEvent));
|
||||
++inputBuffer->keyEventsCount;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
void android_app_clear_key_events(struct android_input_buffer* inputBuffer) {
|
||||
inputBuffer->keyEventsCount = 0;
|
||||
}
|
||||
|
||||
static void onTextInputEvent(GameActivity* activity,
|
||||
const GameTextInputState* state) {
|
||||
struct android_app* android_app = ToApp(activity);
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
|
||||
android_app->textInputState = 1;
|
||||
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);
|
||||
}
|
||||
|
||||
JNIEXPORT
|
||||
void GameActivity_onCreate(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;
|
||||
LOGV("Callbacks set: %p", activity->callbacks);
|
||||
|
||||
activity->instance =
|
||||
android_app_create(activity, savedState, savedStateSize);
|
||||
}
|
||||
|
|
@ -0,0 +1,486 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @addtogroup android_native_app_glue Native App Glue library
|
||||
* The glue library to interface your game loop with GameActivity.
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <android/configuration.h>
|
||||
#include <android/looper.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include "game-activity/GameActivity.h"
|
||||
|
||||
#if (defined NATIVE_APP_GLUE_MAX_NUM_MOTION_EVENTS_OVERRIDE)
|
||||
#define NATIVE_APP_GLUE_MAX_NUM_MOTION_EVENTS \
|
||||
NATIVE_APP_GLUE_MAX_NUM_MOTION_EVENTS_OVERRIDE
|
||||
#else
|
||||
#define NATIVE_APP_GLUE_MAX_NUM_MOTION_EVENTS 16
|
||||
#endif
|
||||
|
||||
#if (defined NATIVE_APP_GLUE_MAX_NUM_KEY_EVENTS_OVERRIDE)
|
||||
#define NATIVE_APP_GLUE_MAX_NUM_KEY_EVENTS \
|
||||
NATIVE_APP_GLUE_MAX_NUM_KEY_EVENTS_OVERRIDE
|
||||
#else
|
||||
#define NATIVE_APP_GLUE_MAX_NUM_KEY_EVENTS 4
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The GameActivity interface provided by <game-activity/GameActivity.h>
|
||||
* is based on a set of application-provided callbacks that will be called
|
||||
* by the Activity's main thread when certain events occur.
|
||||
*
|
||||
* This means that each one of this callbacks _should_ _not_ block, or they
|
||||
* risk having the system force-close the application. This programming
|
||||
* model is direct, lightweight, but constraining.
|
||||
*
|
||||
* The 'android_native_app_glue' static library is used to provide a different
|
||||
* execution model where the application can implement its own main event
|
||||
* loop in a different thread instead. Here's how it works:
|
||||
*
|
||||
* 1/ The application must provide a function named "android_main()" that
|
||||
* will be called when the activity is created, in a new thread that is
|
||||
* distinct from the activity's main thread.
|
||||
*
|
||||
* 2/ android_main() receives a pointer to a valid "android_app" structure
|
||||
* that contains references to other important objects, e.g. the
|
||||
* GameActivity obejct instance the application is running in.
|
||||
*
|
||||
* 3/ the "android_app" object holds an ALooper instance that already
|
||||
* listens to activity lifecycle events (e.g. "pause", "resume").
|
||||
* See APP_CMD_XXX declarations below.
|
||||
*
|
||||
* This corresponds to an ALooper identifier returned by
|
||||
* ALooper_pollOnce with value LOOPER_ID_MAIN.
|
||||
*
|
||||
* Your application can use the same ALooper to listen to additional
|
||||
* file-descriptors. They can either be callback based, or with return
|
||||
* identifiers starting with LOOPER_ID_USER.
|
||||
*
|
||||
* 4/ Whenever you receive a LOOPER_ID_MAIN event,
|
||||
* the returned data will point to an android_poll_source structure. You
|
||||
* can call the process() function on it, and fill in android_app->onAppCmd
|
||||
* to be called for your own processing of the event.
|
||||
*
|
||||
* Alternatively, you can call the low-level functions to read and process
|
||||
* the data directly... look at the process_cmd() and process_input()
|
||||
* implementations in the glue to see how to do this.
|
||||
*
|
||||
* See the sample named "native-activity" that comes with the NDK with a
|
||||
* full usage example. Also look at the documentation of GameActivity.
|
||||
*/
|
||||
|
||||
struct android_app;
|
||||
|
||||
/**
|
||||
* Data associated with an ALooper fd that will be returned as the "outData"
|
||||
* when that source has data ready.
|
||||
*/
|
||||
struct android_poll_source {
|
||||
/**
|
||||
* The identifier of this source. May be LOOPER_ID_MAIN or
|
||||
* LOOPER_ID_INPUT.
|
||||
*/
|
||||
int32_t id;
|
||||
|
||||
/** The android_app this ident is associated with. */
|
||||
struct android_app* app;
|
||||
|
||||
/**
|
||||
* Function to call to perform the standard processing of data from
|
||||
* this source.
|
||||
*/
|
||||
void (*process)(struct android_app* app,
|
||||
struct android_poll_source* source);
|
||||
};
|
||||
|
||||
struct android_input_buffer {
|
||||
/**
|
||||
* Pointer to a read-only array of pointers to GameActivityMotionEvent.
|
||||
* Only the first motionEventsCount events are valid.
|
||||
*/
|
||||
GameActivityMotionEvent motionEvents[NATIVE_APP_GLUE_MAX_NUM_MOTION_EVENTS];
|
||||
|
||||
/**
|
||||
* The number of valid motion events in `motionEvents`.
|
||||
*/
|
||||
uint64_t motionEventsCount;
|
||||
|
||||
/**
|
||||
* Pointer to a read-only array of pointers to GameActivityKeyEvent.
|
||||
* Only the first keyEventsCount events are valid.
|
||||
*/
|
||||
GameActivityKeyEvent keyEvents[NATIVE_APP_GLUE_MAX_NUM_KEY_EVENTS];
|
||||
|
||||
/**
|
||||
* The number of valid "Key" events in `keyEvents`.
|
||||
*/
|
||||
uint64_t keyEventsCount;
|
||||
};
|
||||
|
||||
/**
|
||||
* Function pointer declaration for the filtering of key events.
|
||||
* A function with this signature should be passed to
|
||||
* android_app_set_key_event_filter and return false for any events that should
|
||||
* not be handled by android_native_app_glue. These events will be handled by
|
||||
* the system instead.
|
||||
*/
|
||||
typedef bool (*android_key_event_filter)(const GameActivityKeyEvent*);
|
||||
|
||||
/**
|
||||
* Function pointer definition for the filtering of motion events.
|
||||
* A function with this signature should be passed to
|
||||
* android_app_set_motion_event_filter and return false for any events that
|
||||
* should not be handled by android_native_app_glue. These events will be
|
||||
* handled by the system instead.
|
||||
*/
|
||||
typedef bool (*android_motion_event_filter)(const GameActivityMotionEvent*);
|
||||
|
||||
/**
|
||||
* This is the interface for the standard glue code of a threaded
|
||||
* application. In this model, the application's code is running
|
||||
* in its own thread separate from the main thread of the process.
|
||||
* It is not required that this thread be associated with the Java
|
||||
* VM, although it will need to be in order to make JNI calls any
|
||||
* Java objects.
|
||||
*/
|
||||
struct android_app {
|
||||
/**
|
||||
* An optional pointer to application-defined state.
|
||||
*/
|
||||
void* userData;
|
||||
|
||||
/**
|
||||
* A required callback for processing main app commands (`APP_CMD_*`).
|
||||
* This is called each frame if there are app commands that need processing.
|
||||
*/
|
||||
void (*onAppCmd)(struct android_app* app, int32_t cmd);
|
||||
|
||||
/** The GameActivity object instance that this app is running in. */
|
||||
GameActivity* activity;
|
||||
|
||||
/** The current configuration the app is running in. */
|
||||
AConfiguration* config;
|
||||
|
||||
/**
|
||||
* The last activity saved state, as provided at creation time.
|
||||
* It is NULL if there was no state. You can use this as you need; the
|
||||
* memory will remain around until you call android_app_exec_cmd() for
|
||||
* APP_CMD_RESUME, at which point it will be freed and savedState set to
|
||||
* NULL. These variables should only be changed when processing a
|
||||
* APP_CMD_SAVE_STATE, at which point they will be initialized to NULL and
|
||||
* you can malloc your state and place the information here. In that case
|
||||
* the memory will be freed for you later.
|
||||
*/
|
||||
void* savedState;
|
||||
|
||||
/**
|
||||
* The size of the activity saved state. It is 0 if `savedState` is NULL.
|
||||
*/
|
||||
size_t savedStateSize;
|
||||
|
||||
/** The ALooper associated with the app's thread. */
|
||||
ALooper* looper;
|
||||
|
||||
/** When non-NULL, this is the window surface that the app can draw in. */
|
||||
ANativeWindow* window;
|
||||
|
||||
/**
|
||||
* Current content rectangle of the window; this is the area where the
|
||||
* window's content should be placed to be seen by the user.
|
||||
*/
|
||||
ARect contentRect;
|
||||
|
||||
/**
|
||||
* Current state of the app's activity. May be either APP_CMD_START,
|
||||
* APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP.
|
||||
*/
|
||||
int activityState;
|
||||
|
||||
/**
|
||||
* This is non-zero when the application's GameActivity is being
|
||||
* destroyed and waiting for the app thread to complete.
|
||||
*/
|
||||
int destroyRequested;
|
||||
|
||||
#define NATIVE_APP_GLUE_MAX_INPUT_BUFFERS 2
|
||||
|
||||
/**
|
||||
* This is used for buffering input from GameActivity. Once ready, the
|
||||
* application thread switches the buffers and processes what was
|
||||
* accumulated.
|
||||
*/
|
||||
struct android_input_buffer inputBuffers[NATIVE_APP_GLUE_MAX_INPUT_BUFFERS];
|
||||
|
||||
int currentInputBuffer;
|
||||
|
||||
/**
|
||||
* 0 if no text input event is outstanding, 1 if it is.
|
||||
* Use `GameActivity_getTextInputState` to get information
|
||||
* about the text entered by the user.
|
||||
*/
|
||||
int textInputState;
|
||||
|
||||
// Below are "private" implementation of the glue code.
|
||||
/** @cond INTERNAL */
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
|
||||
int msgread;
|
||||
int msgwrite;
|
||||
|
||||
pthread_t thread;
|
||||
|
||||
struct android_poll_source cmdPollSource;
|
||||
|
||||
int running;
|
||||
int stateSaved;
|
||||
int destroyed;
|
||||
int redrawNeeded;
|
||||
ANativeWindow* pendingWindow;
|
||||
ARect pendingContentRect;
|
||||
|
||||
android_key_event_filter keyEventFilter;
|
||||
android_motion_event_filter motionEventFilter;
|
||||
|
||||
/** @endcond */
|
||||
};
|
||||
|
||||
/**
|
||||
* Looper ID of commands coming from the app's main thread, an AInputQueue or
|
||||
* user-defined sources.
|
||||
*/
|
||||
enum NativeAppGlueLooperId {
|
||||
/**
|
||||
* Looper data ID of commands coming from the app's main thread, which
|
||||
* is returned as an identifier from ALooper_pollOnce(). The data for this
|
||||
* identifier is a pointer to an android_poll_source structure.
|
||||
* These can be retrieved and processed with android_app_read_cmd()
|
||||
* and android_app_exec_cmd().
|
||||
*/
|
||||
LOOPER_ID_MAIN = 1,
|
||||
|
||||
/**
|
||||
* Unused. Reserved for future use when usage of AInputQueue will be
|
||||
* supported.
|
||||
*/
|
||||
LOOPER_ID_INPUT = 2,
|
||||
|
||||
/**
|
||||
* Start of user-defined ALooper identifiers.
|
||||
*/
|
||||
LOOPER_ID_USER = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* Commands passed from the application's main Java thread to the game's thread.
|
||||
*/
|
||||
enum NativeAppGlueAppCmd {
|
||||
/**
|
||||
* Unused. Reserved for future use when usage of AInputQueue will be
|
||||
* supported.
|
||||
*/
|
||||
UNUSED_APP_CMD_INPUT_CHANGED,
|
||||
|
||||
/**
|
||||
* Command from main thread: a new ANativeWindow is ready for use. Upon
|
||||
* receiving this command, android_app->window will contain the new window
|
||||
* surface.
|
||||
*/
|
||||
APP_CMD_INIT_WINDOW,
|
||||
|
||||
/**
|
||||
* Command from main thread: the existing ANativeWindow needs to be
|
||||
* terminated. Upon receiving this command, android_app->window still
|
||||
* contains the existing window; after calling android_app_exec_cmd
|
||||
* it will be set to NULL.
|
||||
*/
|
||||
APP_CMD_TERM_WINDOW,
|
||||
|
||||
/**
|
||||
* Command from main thread: the current ANativeWindow has been resized.
|
||||
* Please redraw with its new size.
|
||||
*/
|
||||
APP_CMD_WINDOW_RESIZED,
|
||||
|
||||
/**
|
||||
* Command from main thread: the system needs that the current ANativeWindow
|
||||
* be redrawn. You should redraw the window before handing this to
|
||||
* android_app_exec_cmd() in order to avoid transient drawing glitches.
|
||||
*/
|
||||
APP_CMD_WINDOW_REDRAW_NEEDED,
|
||||
|
||||
/**
|
||||
* Command from main thread: the content area of the window has changed,
|
||||
* such as from the soft input window being shown or hidden. You can
|
||||
* find the new content rect in android_app::contentRect.
|
||||
*/
|
||||
APP_CMD_CONTENT_RECT_CHANGED,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity window has gained
|
||||
* input focus.
|
||||
*/
|
||||
APP_CMD_GAINED_FOCUS,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity window has lost
|
||||
* input focus.
|
||||
*/
|
||||
APP_CMD_LOST_FOCUS,
|
||||
|
||||
/**
|
||||
* Command from main thread: the current device configuration has changed.
|
||||
*/
|
||||
APP_CMD_CONFIG_CHANGED,
|
||||
|
||||
/**
|
||||
* Command from main thread: the system is running low on memory.
|
||||
* Try to reduce your memory use.
|
||||
*/
|
||||
APP_CMD_LOW_MEMORY,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity has been started.
|
||||
*/
|
||||
APP_CMD_START,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity has been resumed.
|
||||
*/
|
||||
APP_CMD_RESUME,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app should generate a new saved state
|
||||
* for itself, to restore from later if needed. If you have saved state,
|
||||
* allocate it with malloc and place it in android_app.savedState with
|
||||
* the size in android_app.savedStateSize. The will be freed for you
|
||||
* later.
|
||||
*/
|
||||
APP_CMD_SAVE_STATE,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity has been paused.
|
||||
*/
|
||||
APP_CMD_PAUSE,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity has been stopped.
|
||||
*/
|
||||
APP_CMD_STOP,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity is being destroyed,
|
||||
* and waiting for the app thread to clean up and exit before proceeding.
|
||||
*/
|
||||
APP_CMD_DESTROY,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's insets have changed.
|
||||
*/
|
||||
APP_CMD_WINDOW_INSETS_CHANGED,
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next
|
||||
* app command message.
|
||||
*/
|
||||
int8_t android_app_read_cmd(struct android_app* android_app);
|
||||
|
||||
/**
|
||||
* Call with the command returned by android_app_read_cmd() to do the
|
||||
* initial pre-processing of the given command. You can perform your own
|
||||
* actions for the command after calling this function.
|
||||
*/
|
||||
void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd);
|
||||
|
||||
/**
|
||||
* Call with the command returned by android_app_read_cmd() to do the
|
||||
* final post-processing of the given command. You must have done your own
|
||||
* actions for the command before calling this function.
|
||||
*/
|
||||
void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd);
|
||||
|
||||
/**
|
||||
* Call this before processing input events to get the events buffer.
|
||||
* The function returns NULL if there are no events to process.
|
||||
*/
|
||||
struct android_input_buffer* android_app_swap_input_buffers(
|
||||
struct android_app* android_app);
|
||||
|
||||
/**
|
||||
* Clear the array of motion events that were waiting to be handled, and release
|
||||
* each of them.
|
||||
*
|
||||
* This method should be called after you have processed the motion events in
|
||||
* your game loop. You should handle events at each iteration of your game loop.
|
||||
*/
|
||||
void android_app_clear_motion_events(struct android_input_buffer* inputBuffer);
|
||||
|
||||
/**
|
||||
* Clear the array of key events that were waiting to be handled, and release
|
||||
* each of them.
|
||||
*
|
||||
* This method should be called after you have processed the key up events in
|
||||
* your game loop. You should handle events at each iteration of your game loop.
|
||||
*/
|
||||
void android_app_clear_key_events(struct android_input_buffer* inputBuffer);
|
||||
|
||||
/**
|
||||
* This is the function that application code must implement, representing
|
||||
* the main entry to the app.
|
||||
*/
|
||||
extern void android_main(struct android_app* app);
|
||||
|
||||
/**
|
||||
* Set the filter to use when processing key events.
|
||||
* Any events for which the filter returns false will be ignored by
|
||||
* android_native_app_glue. If filter is set to NULL, no filtering is done.
|
||||
*
|
||||
* The default key filter will filter out volume and camera button presses.
|
||||
*/
|
||||
void android_app_set_key_event_filter(struct android_app* app,
|
||||
android_key_event_filter filter);
|
||||
|
||||
/**
|
||||
* Set the filter to use when processing touch and motion events.
|
||||
* Any events for which the filter returns false will be ignored by
|
||||
* android_native_app_glue. If filter is set to NULL, no filtering is done.
|
||||
*
|
||||
* Note that the default motion event filter will only allow touchscreen events
|
||||
* through, in order to mimic NativeActivity's behaviour, so for controller
|
||||
* events to be passed to the app, set the filter to NULL.
|
||||
*/
|
||||
void android_app_set_motion_event_filter(struct android_app* app,
|
||||
android_motion_event_filter filter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup game_common Game Common
|
||||
* Common structures and functions used within AGDK
|
||||
* @{
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* The type of a component for which to retrieve insets. See
|
||||
* https://developer.android.com/reference/androidx/core/view/WindowInsetsCompat.Type
|
||||
*/
|
||||
typedef enum GameCommonInsetsType {
|
||||
GAMECOMMON_INSETS_TYPE_CAPTION_BAR = 0,
|
||||
GAMECOMMON_INSETS_TYPE_DISPLAY_CUTOUT,
|
||||
GAMECOMMON_INSETS_TYPE_IME,
|
||||
GAMECOMMON_INSETS_TYPE_MANDATORY_SYSTEM_GESTURES,
|
||||
GAMECOMMON_INSETS_TYPE_NAVIGATION_BARS,
|
||||
GAMECOMMON_INSETS_TYPE_STATUS_BARS,
|
||||
GAMECOMMON_INSETS_TYPE_SYSTEM_BARS,
|
||||
GAMECOMMON_INSETS_TYPE_SYSTEM_GESTURES,
|
||||
GAMECOMMON_INSETS_TYPE_TAPABLE_ELEMENT,
|
||||
GAMECOMMON_INSETS_TYPE_WATERFALL,
|
||||
GAMECOMMON_INSETS_TYPE_COUNT
|
||||
} GameCommonInsetsType;
|
||||
|
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "game-text-input/gametextinput.h"
|
||||
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#define LOG_TAG "GameTextInput"
|
||||
|
||||
static constexpr int32_t DEFAULT_MAX_STRING_SIZE = 1 << 16;
|
||||
|
||||
// Cache of field ids in the Java GameTextInputState class
|
||||
struct StateClassInfo {
|
||||
jfieldID text;
|
||||
jfieldID selectionStart;
|
||||
jfieldID selectionEnd;
|
||||
jfieldID composingRegionStart;
|
||||
jfieldID composingRegionEnd;
|
||||
};
|
||||
|
||||
// Main GameTextInput object.
|
||||
struct GameTextInput {
|
||||
public:
|
||||
GameTextInput(JNIEnv *env, uint32_t max_string_size);
|
||||
~GameTextInput();
|
||||
void setState(const GameTextInputState &state);
|
||||
const GameTextInputState &getState() const { return currentState_; }
|
||||
void setInputConnection(jobject inputConnection);
|
||||
void processEvent(jobject textInputEvent);
|
||||
void showIme(uint32_t flags);
|
||||
void hideIme(uint32_t flags);
|
||||
void setEventCallback(GameTextInputEventCallback callback, void *context);
|
||||
jobject stateToJava(const GameTextInputState &state) const;
|
||||
void stateFromJava(jobject textInputEvent,
|
||||
GameTextInputGetStateCallback callback,
|
||||
void *context) const;
|
||||
void setImeInsetsCallback(GameTextInputImeInsetsCallback callback,
|
||||
void *context);
|
||||
void processImeInsets(const ARect *insets);
|
||||
const ARect &getImeInsets() const { return currentInsets_; }
|
||||
|
||||
private:
|
||||
// Copy string and set other fields
|
||||
void setStateInner(const GameTextInputState &state);
|
||||
static void processCallback(void *context, const GameTextInputState *state);
|
||||
JNIEnv *env_ = nullptr;
|
||||
// Cached at initialization from
|
||||
// com/google/androidgamesdk/gametextinput/State.
|
||||
jclass stateJavaClass_ = nullptr;
|
||||
// The latest text input update.
|
||||
GameTextInputState currentState_ = {};
|
||||
// An instance of gametextinput.InputConnection.
|
||||
jclass inputConnectionClass_ = nullptr;
|
||||
jobject inputConnection_ = nullptr;
|
||||
jmethodID inputConnectionSetStateMethod_;
|
||||
jmethodID setSoftKeyboardActiveMethod_;
|
||||
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<char> stateStringBuffer_;
|
||||
};
|
||||
|
||||
std::unique_ptr<GameTextInput> s_gameTextInput;
|
||||
|
||||
extern "C" {
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
/// GameTextInputState C Functions
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
// Convert to a Java structure.
|
||||
jobject currentState_toJava(const GameTextInput *gameTextInput,
|
||||
const GameTextInputState *state) {
|
||||
if (state == nullptr) return NULL;
|
||||
return gameTextInput->stateToJava(*state);
|
||||
}
|
||||
|
||||
// Convert from Java structure.
|
||||
void currentState_fromJava(const GameTextInput *gameTextInput,
|
||||
jobject textInputEvent,
|
||||
GameTextInputGetStateCallback callback,
|
||||
void *context) {
|
||||
gameTextInput->stateFromJava(textInputEvent, callback, context);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
/// GameTextInput C Functions
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
struct GameTextInput *GameTextInput_init(JNIEnv *env,
|
||||
uint32_t max_string_size) {
|
||||
if (s_gameTextInput.get() != nullptr) {
|
||||
__android_log_print(ANDROID_LOG_WARN, LOG_TAG,
|
||||
"Warning: called GameTextInput_init twice without "
|
||||
"calling GameTextInput_destroy");
|
||||
return s_gameTextInput.get();
|
||||
}
|
||||
// Don't use make_unique, for C++11 compatibility
|
||||
s_gameTextInput =
|
||||
std::unique_ptr<GameTextInput>(new GameTextInput(env, max_string_size));
|
||||
return s_gameTextInput.get();
|
||||
}
|
||||
|
||||
void GameTextInput_destroy(GameTextInput *input) {
|
||||
if (input == nullptr || s_gameTextInput.get() == nullptr) return;
|
||||
s_gameTextInput.reset();
|
||||
}
|
||||
|
||||
void GameTextInput_setState(GameTextInput *input,
|
||||
const GameTextInputState *state) {
|
||||
if (state == nullptr) return;
|
||||
input->setState(*state);
|
||||
}
|
||||
|
||||
void GameTextInput_getState(GameTextInput *input,
|
||||
GameTextInputGetStateCallback callback,
|
||||
void *context) {
|
||||
callback(context, &input->getState());
|
||||
}
|
||||
|
||||
void GameTextInput_setInputConnection(GameTextInput *input,
|
||||
jobject inputConnection) {
|
||||
input->setInputConnection(inputConnection);
|
||||
}
|
||||
|
||||
void GameTextInput_processEvent(GameTextInput *input, jobject textInputEvent) {
|
||||
input->processEvent(textInputEvent);
|
||||
}
|
||||
|
||||
void GameTextInput_processImeInsets(GameTextInput *input, const ARect *insets) {
|
||||
input->processImeInsets(insets);
|
||||
}
|
||||
|
||||
void GameTextInput_showIme(struct GameTextInput *input, uint32_t flags) {
|
||||
input->showIme(flags);
|
||||
}
|
||||
|
||||
void GameTextInput_hideIme(struct GameTextInput *input, uint32_t flags) {
|
||||
input->hideIme(flags);
|
||||
}
|
||||
|
||||
void GameTextInput_setEventCallback(struct GameTextInput *input,
|
||||
GameTextInputEventCallback callback,
|
||||
void *context) {
|
||||
input->setEventCallback(callback, context);
|
||||
}
|
||||
|
||||
void GameTextInput_setImeInsetsCallback(struct GameTextInput *input,
|
||||
GameTextInputImeInsetsCallback callback,
|
||||
void *context) {
|
||||
input->setImeInsetsCallback(callback, context);
|
||||
}
|
||||
|
||||
void GameTextInput_getImeInsets(const GameTextInput *input, ARect *insets) {
|
||||
*insets = input->getImeInsets();
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
/// GameTextInput C++ class Implementation
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
GameTextInput::GameTextInput(JNIEnv *env, uint32_t max_string_size)
|
||||
: env_(env),
|
||||
stateStringBuffer_(max_string_size == 0 ? DEFAULT_MAX_STRING_SIZE
|
||||
: max_string_size) {
|
||||
stateJavaClass_ = (jclass)env_->NewGlobalRef(
|
||||
env_->FindClass("com/google/androidgamesdk/gametextinput/State"));
|
||||
inputConnectionClass_ = (jclass)env_->NewGlobalRef(env_->FindClass(
|
||||
"com/google/androidgamesdk/gametextinput/InputConnection"));
|
||||
inputConnectionSetStateMethod_ =
|
||||
env_->GetMethodID(inputConnectionClass_, "setState",
|
||||
"(Lcom/google/androidgamesdk/gametextinput/State;)V");
|
||||
setSoftKeyboardActiveMethod_ = env_->GetMethodID(
|
||||
inputConnectionClass_, "setSoftKeyboardActive", "(ZI)V");
|
||||
|
||||
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");
|
||||
|
||||
s_gameTextInput.get();
|
||||
}
|
||||
|
||||
GameTextInput::~GameTextInput() {
|
||||
if (stateJavaClass_ != NULL) {
|
||||
env_->DeleteGlobalRef(stateJavaClass_);
|
||||
stateJavaClass_ = NULL;
|
||||
}
|
||||
if (inputConnectionClass_ != NULL) {
|
||||
env_->DeleteGlobalRef(inputConnectionClass_);
|
||||
inputConnectionClass_ = NULL;
|
||||
}
|
||||
if (inputConnection_ != NULL) {
|
||||
env_->DeleteGlobalRef(inputConnection_);
|
||||
inputConnection_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void GameTextInput::setState(const GameTextInputState &state) {
|
||||
if (inputConnection_ == nullptr) return;
|
||||
jobject jstate = stateToJava(state);
|
||||
env_->CallVoidMethod(inputConnection_, inputConnectionSetStateMethod_,
|
||||
jstate);
|
||||
env_->DeleteLocalRef(jstate);
|
||||
setStateInner(state);
|
||||
}
|
||||
|
||||
void GameTextInput::setStateInner(const GameTextInputState &state) {
|
||||
// Check if we're setting using our own string (other parts may be
|
||||
// different)
|
||||
if (state.text_UTF8 == currentState_.text_UTF8) {
|
||||
currentState_ = state;
|
||||
return;
|
||||
}
|
||||
// Otherwise, copy across the string.
|
||||
auto bytes_needed =
|
||||
std::min(static_cast<uint32_t>(state.text_length + 1),
|
||||
static_cast<uint32_t>(stateStringBuffer_.size()));
|
||||
currentState_.text_UTF8 = stateStringBuffer_.data();
|
||||
std::copy(state.text_UTF8, state.text_UTF8 + bytes_needed - 1,
|
||||
stateStringBuffer_.data());
|
||||
currentState_.text_length = state.text_length;
|
||||
currentState_.selection = state.selection;
|
||||
currentState_.composingRegion = state.composingRegion;
|
||||
stateStringBuffer_[bytes_needed - 1] = 0;
|
||||
}
|
||||
|
||||
void GameTextInput::setInputConnection(jobject inputConnection) {
|
||||
if (inputConnection_ != NULL) {
|
||||
env_->DeleteGlobalRef(inputConnection_);
|
||||
}
|
||||
inputConnection_ = env_->NewGlobalRef(inputConnection);
|
||||
}
|
||||
|
||||
/*static*/ void GameTextInput::processCallback(
|
||||
void *context, const GameTextInputState *state) {
|
||||
auto thiz = static_cast<GameTextInput *>(context);
|
||||
if (state != nullptr) thiz->setStateInner(*state);
|
||||
}
|
||||
|
||||
void GameTextInput::processEvent(jobject textInputEvent) {
|
||||
stateFromJava(textInputEvent, processCallback, this);
|
||||
if (eventCallback_) {
|
||||
eventCallback_(eventCallbackContext_, ¤tState_);
|
||||
}
|
||||
}
|
||||
|
||||
void GameTextInput::showIme(uint32_t flags) {
|
||||
if (inputConnection_ == nullptr) return;
|
||||
env_->CallVoidMethod(inputConnection_, setSoftKeyboardActiveMethod_, true,
|
||||
flags);
|
||||
}
|
||||
|
||||
void GameTextInput::setEventCallback(GameTextInputEventCallback callback,
|
||||
void *context) {
|
||||
eventCallback_ = callback;
|
||||
eventCallbackContext_ = context;
|
||||
}
|
||||
|
||||
void GameTextInput::setImeInsetsCallback(
|
||||
GameTextInputImeInsetsCallback callback, void *context) {
|
||||
insetsCallback_ = callback;
|
||||
insetsCallbackContext_ = context;
|
||||
}
|
||||
|
||||
void GameTextInput::processImeInsets(const ARect *insets) {
|
||||
currentInsets_ = *insets;
|
||||
if (insetsCallback_) {
|
||||
insetsCallback_(insetsCallbackContext_, ¤tInsets_);
|
||||
}
|
||||
}
|
||||
|
||||
void GameTextInput::hideIme(uint32_t flags) {
|
||||
if (inputConnection_ == nullptr) return;
|
||||
env_->CallVoidMethod(inputConnection_, setSoftKeyboardActiveMethod_, false,
|
||||
flags);
|
||||
}
|
||||
|
||||
jobject GameTextInput::stateToJava(const GameTextInputState &state) const {
|
||||
static jmethodID constructor = nullptr;
|
||||
if (constructor == nullptr) {
|
||||
constructor = env_->GetMethodID(stateJavaClass_, "<init>",
|
||||
"(Ljava/lang/String;IIII)V");
|
||||
if (constructor == nullptr) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
|
||||
"Can't find gametextinput.State constructor");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
const char *text = state.text_UTF8;
|
||||
if (text == nullptr) {
|
||||
static char empty_string[] = "";
|
||||
text = empty_string;
|
||||
}
|
||||
// Note that this expects 'modified' UTF-8 which is not the same as UTF-8
|
||||
// https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8
|
||||
jstring jtext = env_->NewStringUTF(text);
|
||||
jobject jobj =
|
||||
env_->NewObject(stateJavaClass_, constructor, jtext,
|
||||
state.selection.start, state.selection.end,
|
||||
state.composingRegion.start, state.composingRegion.end);
|
||||
env_->DeleteLocalRef(jtext);
|
||||
return jobj;
|
||||
}
|
||||
|
||||
void GameTextInput::stateFromJava(jobject textInputEvent,
|
||||
GameTextInputGetStateCallback callback,
|
||||
void *context) const {
|
||||
jstring text =
|
||||
(jstring)env_->GetObjectField(textInputEvent, stateClassInfo_.text);
|
||||
// Note this is 'modified' UTF-8, not true UTF-8. It has no NULLs in it,
|
||||
// except at the end. It's actually not specified whether the value returned
|
||||
// by GetStringUTFChars includes a null at the end, but it *seems to* on
|
||||
// Android.
|
||||
const char *text_chars = env_->GetStringUTFChars(text, NULL);
|
||||
int text_len = env_->GetStringUTFLength(
|
||||
text); // Length in bytes, *not* including the null.
|
||||
int selectionStart =
|
||||
env_->GetIntField(textInputEvent, stateClassInfo_.selectionStart);
|
||||
int selectionEnd =
|
||||
env_->GetIntField(textInputEvent, stateClassInfo_.selectionEnd);
|
||||
int composingRegionStart =
|
||||
env_->GetIntField(textInputEvent, stateClassInfo_.composingRegionStart);
|
||||
int composingRegionEnd =
|
||||
env_->GetIntField(textInputEvent, stateClassInfo_.composingRegionEnd);
|
||||
GameTextInputState state{text_chars,
|
||||
text_len,
|
||||
{selectionStart, selectionEnd},
|
||||
{composingRegionStart, composingRegionEnd}};
|
||||
callback(context, &state);
|
||||
env_->ReleaseStringUTFChars(text, text_chars);
|
||||
env_->DeleteLocalRef(text);
|
||||
}
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup game_text_input Game Text Input
|
||||
* The interface to use GameTextInput.
|
||||
* @{
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/rect.h>
|
||||
#include <jni.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "gamecommon.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This struct holds a span within a region of text from start (inclusive) to
|
||||
* end (exclusive). An empty span or cursor position is specified with
|
||||
* start==end. An undefined span is specified with start = end = SPAN_UNDEFINED.
|
||||
*/
|
||||
typedef struct GameTextInputSpan {
|
||||
/** The start of the region (inclusive). */
|
||||
int32_t start;
|
||||
/** The end of the region (exclusive). */
|
||||
int32_t end;
|
||||
} GameTextInputSpan;
|
||||
|
||||
/**
|
||||
* Values with special meaning in a GameTextInputSpan.
|
||||
*/
|
||||
enum GameTextInputSpanFlag { SPAN_UNDEFINED = -1 };
|
||||
|
||||
/**
|
||||
* This struct holds the state of an editable section of text.
|
||||
* The text can have a selection and a composing region defined on it.
|
||||
* A composing region is used by IMEs that allow input using multiple steps to
|
||||
* compose a glyph or word. Use functions GameTextInput_getState and
|
||||
* GameTextInput_setState to read and modify the state that an IME is editing.
|
||||
*/
|
||||
typedef struct GameTextInputState {
|
||||
/**
|
||||
* Text owned by the state, as a modified UTF-8 string. Null-terminated.
|
||||
* https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8
|
||||
*/
|
||||
const char *text_UTF8;
|
||||
/**
|
||||
* Length in bytes of text_UTF8, *not* including the null at end.
|
||||
*/
|
||||
int32_t text_length;
|
||||
/**
|
||||
* A selection defined on the text.
|
||||
*/
|
||||
GameTextInputSpan selection;
|
||||
/**
|
||||
* A composing region defined on the text.
|
||||
*/
|
||||
GameTextInputSpan composingRegion;
|
||||
} GameTextInputState;
|
||||
|
||||
/**
|
||||
* A callback called by GameTextInput_getState.
|
||||
* @param context User-defined context.
|
||||
* @param state State, owned by the library, that will be valid for the duration
|
||||
* of the callback.
|
||||
*/
|
||||
typedef void (*GameTextInputGetStateCallback)(
|
||||
void *context, const struct GameTextInputState *state);
|
||||
|
||||
/**
|
||||
* Opaque handle to the GameTextInput API.
|
||||
*/
|
||||
typedef struct GameTextInput GameTextInput;
|
||||
|
||||
/**
|
||||
* Initialize the GameTextInput library.
|
||||
* If called twice without GameTextInput_destroy being called, the same pointer
|
||||
* will be returned and a warning will be issued.
|
||||
* @param env A JNI env valid on the calling thread.
|
||||
* @param max_string_size The maximum length of a string that can be edited. If
|
||||
* zero, the maximum defaults to 65536 bytes. A buffer of this size is allocated
|
||||
* at initialization.
|
||||
* @return A handle to the library.
|
||||
*/
|
||||
GameTextInput *GameTextInput_init(JNIEnv *env, uint32_t max_string_size);
|
||||
|
||||
/**
|
||||
* When using GameTextInput, you need to create a gametextinput.InputConnection
|
||||
* on the Java side and pass it using this function to the library, unless using
|
||||
* GameActivity in which case this will be done for you. See the GameActivity
|
||||
* source code or GameTextInput samples for examples of usage.
|
||||
* @param input A valid GameTextInput library handle.
|
||||
* @param inputConnection A gametextinput.InputConnection object.
|
||||
*/
|
||||
void GameTextInput_setInputConnection(GameTextInput *input,
|
||||
jobject inputConnection);
|
||||
|
||||
/**
|
||||
* Unless using GameActivity, it is required to call this function from your
|
||||
* Java gametextinput.Listener.stateChanged method to convert eventState and
|
||||
* trigger any event callbacks. When using GameActivity, this does not need to
|
||||
* be called as event processing is handled by the Activity.
|
||||
* @param input A valid GameTextInput library handle.
|
||||
* @param eventState A Java gametextinput.State object.
|
||||
*/
|
||||
void GameTextInput_processEvent(GameTextInput *input, jobject eventState);
|
||||
|
||||
/**
|
||||
* Free any resources owned by the GameTextInput library.
|
||||
* Any subsequent calls to the library will fail until GameTextInput_init is
|
||||
* called again.
|
||||
* @param input A valid GameTextInput library handle.
|
||||
*/
|
||||
void GameTextInput_destroy(GameTextInput *input);
|
||||
|
||||
/**
|
||||
* Flags to be passed to GameTextInput_showIme.
|
||||
*/
|
||||
enum ShowImeFlags {
|
||||
SHOW_IME_UNDEFINED = 0, // Default value.
|
||||
SHOW_IMPLICIT =
|
||||
1, // Indicates that the user has forced the input method open so it
|
||||
// should not be closed until they explicitly do so.
|
||||
SHOW_FORCED = 2 // Indicates that this is an implicit request to show the
|
||||
// input window, not as the result of a direct request by
|
||||
// the user. The window may not be shown in this case.
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the IME. Calls InputMethodManager.showSoftInput().
|
||||
* @param input A valid GameTextInput library handle.
|
||||
* @param flags Defined in ShowImeFlags above. For more information see:
|
||||
* https://developer.android.com/reference/android/view/inputmethod/InputMethodManager
|
||||
*/
|
||||
void GameTextInput_showIme(GameTextInput *input, uint32_t flags);
|
||||
|
||||
/**
|
||||
* Flags to be passed to GameTextInput_hideIme.
|
||||
*/
|
||||
enum HideImeFlags {
|
||||
HIDE_IME_UNDEFINED = 0, // Default value.
|
||||
HIDE_IMPLICIT_ONLY =
|
||||
1, // Indicates that the soft input window should only be hidden if it
|
||||
// was not explicitly shown by the user.
|
||||
HIDE_NOT_ALWAYS =
|
||||
2, // Indicates that the soft input window should normally be hidden,
|
||||
// unless it was originally shown with SHOW_FORCED.
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the IME. Calls InputMethodManager.hideSoftInputFromWindow().
|
||||
* @param input A valid GameTextInput library handle.
|
||||
* @param flags Defined in HideImeFlags above. For more information see:
|
||||
* https://developer.android.com/reference/android/view/inputmethod/InputMethodManager
|
||||
*/
|
||||
void GameTextInput_hideIme(GameTextInput *input, uint32_t flags);
|
||||
|
||||
/**
|
||||
* Call a callback with the current GameTextInput state, which may have been
|
||||
* modified by changes in the IME and calls to GameTextInput_setState. We use a
|
||||
* callback rather than returning the state in order to simplify ownership of
|
||||
* text_UTF8 strings. These strings are only valid during the calling of the
|
||||
* callback.
|
||||
* @param input A valid GameTextInput library handle.
|
||||
* @param callback A function that will be called with valid state.
|
||||
* @param context Context used by the callback.
|
||||
*/
|
||||
void GameTextInput_getState(GameTextInput *input,
|
||||
GameTextInputGetStateCallback callback,
|
||||
void *context);
|
||||
|
||||
/**
|
||||
* Set the current GameTextInput state. This state is reflected to any active
|
||||
* IME.
|
||||
* @param input A valid GameTextInput library handle.
|
||||
* @param state The state to set. Ownership is maintained by the caller and must
|
||||
* remain valid for the duration of the call.
|
||||
*/
|
||||
void GameTextInput_setState(GameTextInput *input,
|
||||
const GameTextInputState *state);
|
||||
|
||||
/**
|
||||
* Type of the callback needed by GameTextInput_setEventCallback that will be
|
||||
* called every time the IME state changes.
|
||||
* @param context User-defined context set in GameTextInput_setEventCallback.
|
||||
* @param current_state Current IME state, owned by the library and valid during
|
||||
* the callback.
|
||||
*/
|
||||
typedef void (*GameTextInputEventCallback)(
|
||||
void *context, const GameTextInputState *current_state);
|
||||
|
||||
/**
|
||||
* Optionally set a callback to be called whenever the IME state changes.
|
||||
* Not necessary if you are using GameActivity, which handles these callbacks
|
||||
* for you.
|
||||
* @param input A valid GameTextInput library handle.
|
||||
* @param callback Called by the library when the IME state changes.
|
||||
* @param context Context passed as first argument to the callback.
|
||||
*/
|
||||
void GameTextInput_setEventCallback(GameTextInput *input,
|
||||
GameTextInputEventCallback callback,
|
||||
void *context);
|
||||
|
||||
/**
|
||||
* Type of the callback needed by GameTextInput_setImeInsetsCallback that will
|
||||
* be called every time the IME window insets change.
|
||||
* @param context User-defined context set in
|
||||
* GameTextInput_setImeWIndowInsetsCallback.
|
||||
* @param current_insets Current IME insets, owned by the library and valid
|
||||
* during the callback.
|
||||
*/
|
||||
typedef void (*GameTextInputImeInsetsCallback)(void *context,
|
||||
const ARect *current_insets);
|
||||
|
||||
/**
|
||||
* Optionally set a callback to be called whenever the IME insets change.
|
||||
* Not necessary if you are using GameActivity, which handles these callbacks
|
||||
* for you.
|
||||
* @param input A valid GameTextInput library handle.
|
||||
* @param callback Called by the library when the IME insets change.
|
||||
* @param context Context passed as first argument to the callback.
|
||||
*/
|
||||
void GameTextInput_setImeInsetsCallback(GameTextInput *input,
|
||||
GameTextInputImeInsetsCallback callback,
|
||||
void *context);
|
||||
|
||||
/**
|
||||
* Get the current window insets for the IME.
|
||||
* @param input A valid GameTextInput library handle.
|
||||
* @param insets Filled with the current insets by this function.
|
||||
*/
|
||||
void GameTextInput_getImeInsets(const GameTextInput *input, ARect *insets);
|
||||
|
||||
/**
|
||||
* Unless using GameActivity, it is required to call this function from your
|
||||
* Java gametextinput.Listener.onImeInsetsChanged method to
|
||||
* trigger any event callbacks. When using GameActivity, this does not need to
|
||||
* be called as insets processing is handled by the Activity.
|
||||
* @param input A valid GameTextInput library handle.
|
||||
* @param eventState A Java gametextinput.State object.
|
||||
*/
|
||||
void GameTextInput_processImeInsets(GameTextInput *input, const ARect *insets);
|
||||
|
||||
/**
|
||||
* Convert a GameTextInputState struct to a Java gametextinput.State object.
|
||||
* Don't forget to delete the returned Java local ref when you're done.
|
||||
* @param input A valid GameTextInput library handle.
|
||||
* @param state Input state to convert.
|
||||
* @return A Java object of class gametextinput.State. The caller is required to
|
||||
* delete this local reference.
|
||||
*/
|
||||
jobject GameTextInputState_toJava(const GameTextInput *input,
|
||||
const GameTextInputState *state);
|
||||
|
||||
/**
|
||||
* Convert from a Java gametextinput.State object into a C GameTextInputState
|
||||
* struct.
|
||||
* @param input A valid GameTextInput library handle.
|
||||
* @param state A Java gametextinput.State object.
|
||||
* @param callback A function called with the C struct, valid for the duration
|
||||
* of the call.
|
||||
* @param context Context passed to the callback.
|
||||
*/
|
||||
void GameTextInputState_fromJava(const GameTextInput *input, jobject state,
|
||||
GameTextInputGetStateCallback callback,
|
||||
void *context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"export_libraries":[],"library_name":null,"android":{"export_libraries":null,"library_name":null}}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
#
|
||||
# Copyright (C) 2021 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
project(paddleboat C CXX)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_LIST_DIR}/paddleboat/include)
|
||||
|
||||
set( PADDLEBOAT_SRCS
|
||||
${CMAKE_CURRENT_LIST_DIR}/InternalControllerTable.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/GameController.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/GameControllerDeviceInfo.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/GameControllerLog.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/GameControllerManager.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/GameControllerMappingUtils.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/paddleboat_c.cpp)
|
||||
|
||||
# set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Os")
|
||||
# set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti")
|
||||
# set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g0")
|
||||
|
||||
add_library(paddleboat_static STATIC ${PADDLEBOAT_SRCS})
|
||||
|
||||
set_target_properties( paddleboat_static PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build )
|
||||
|
||||
add_library(paddleboat SHARED ${CMAKE_CURRENT_LIST_DIR}/paddleboat_c.cpp)
|
||||
|
||||
target_link_libraries(paddleboat
|
||||
paddleboat_static
|
||||
android
|
||||
atomic
|
||||
log)
|
||||
|
|
@ -0,0 +1,458 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "GameController.h"
|
||||
|
||||
#include <android/input.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
|
||||
#include "GameControllerInternalConstants.h"
|
||||
|
||||
namespace paddleboat {
|
||||
struct ControllerButtonMap {
|
||||
int32_t buttonKeycodes[PADDLEBOAT_BUTTON_COUNT];
|
||||
};
|
||||
|
||||
const ControllerButtonMap defaultButtonMap = {{
|
||||
AKEYCODE_DPAD_UP, // PADDLEBOAT_BUTTON_DPAD_UP
|
||||
AKEYCODE_DPAD_LEFT, // PADDLEBOAT_BUTTON_DPAD_LEFT
|
||||
AKEYCODE_DPAD_DOWN, // PADDLEBOAT_BUTTON_DPAD_DOWN
|
||||
AKEYCODE_DPAD_RIGHT, // PADDLEBOAT_BUTTON_DPAD_RIGHT
|
||||
AKEYCODE_BUTTON_A, // PADDLEBOAT_BUTTON_A
|
||||
AKEYCODE_BUTTON_B, // PADDLEBOAT_BUTTON_B
|
||||
AKEYCODE_BUTTON_X, // PADDLEBOAT_BUTTON_X
|
||||
AKEYCODE_BUTTON_Y, // PADDLEBOAT_BUTTON_Y
|
||||
AKEYCODE_BUTTON_L1, // PADDLEBOAT_BUTTON_L1
|
||||
AKEYCODE_BUTTON_L2, // PADDLEBOAT_BUTTON_L2
|
||||
AKEYCODE_BUTTON_THUMBL, // PADDLEBOAT_BUTTON_L3
|
||||
AKEYCODE_BUTTON_R1, // PADDLEBOAT_BUTTON_R1
|
||||
AKEYCODE_BUTTON_R2, // PADDLEBOAT_BUTTON_R2
|
||||
AKEYCODE_BUTTON_THUMBR, // PADDLEBOAT_BUTTON_R3
|
||||
AKEYCODE_BUTTON_SELECT, // PADDLEBOAT_BUTTON_SELECT
|
||||
AKEYCODE_BUTTON_START, // PADDLEBOAT_BUTTON_START
|
||||
AKEYCODE_BUTTON_MODE, // PADDLEBOAT_BUTTON_SYSTEM
|
||||
0, // PADDLEBOAT_BUTTON_TOUCHPAD
|
||||
0, // PADDLEBOAT_BUTTON_AUX1
|
||||
0, // PADDLEBOAT_BUTTON_AUX2
|
||||
0, // PADDLEBOAT_BUTTON_AUX3
|
||||
0 // PADDLEBOAT_BUTTON_AUX4
|
||||
}};
|
||||
|
||||
// Axis must be at least this value to trigger a mapped button press
|
||||
constexpr float AXIS_BUTTON_THRESHOLD = 0.1f;
|
||||
|
||||
void resetData(Paddleboat_Controller_Data &pbData) {
|
||||
pbData.timestamp = 0;
|
||||
pbData.buttonsDown = 0;
|
||||
pbData.leftStick.stickX = 0.0f;
|
||||
pbData.leftStick.stickY = 0.0f;
|
||||
pbData.rightStick.stickX = 0.0f;
|
||||
pbData.rightStick.stickY = 0.0f;
|
||||
pbData.triggerL1 = 0.0f;
|
||||
pbData.triggerL2 = 0.0f;
|
||||
pbData.triggerR1 = 0.0f;
|
||||
pbData.triggerR2 = 0.0f;
|
||||
pbData.virtualPointer.pointerX = 0.0f;
|
||||
pbData.virtualPointer.pointerY = 0.0f;
|
||||
}
|
||||
|
||||
void resetInfo(Paddleboat_Controller_Info &pbInfo) {
|
||||
pbInfo.controllerNumber = -1;
|
||||
pbInfo.controllerFlags = 0;
|
||||
pbInfo.vendorId = 0;
|
||||
pbInfo.productId = 0;
|
||||
pbInfo.deviceId = -1;
|
||||
pbInfo.leftStickPrecision.stickFlatX = 0.0f;
|
||||
pbInfo.leftStickPrecision.stickFlatY = 0.0f;
|
||||
pbInfo.leftStickPrecision.stickFuzzX = 0.0f;
|
||||
pbInfo.leftStickPrecision.stickFuzzY = 0.0f;
|
||||
pbInfo.rightStickPrecision.stickFlatX = 0.0f;
|
||||
pbInfo.rightStickPrecision.stickFlatY = 0.0f;
|
||||
pbInfo.rightStickPrecision.stickFuzzX = 0.0f;
|
||||
pbInfo.rightStickPrecision.stickFuzzY = 0.0f;
|
||||
}
|
||||
|
||||
GameController::GameController()
|
||||
: mConnectionIndex(-1),
|
||||
mControllerData(),
|
||||
mControllerInfo(),
|
||||
mAxisInfo(),
|
||||
mDeviceInfo(),
|
||||
mControllerDataDirty(true) {
|
||||
memset(mButtonKeycodes, 0, sizeof(mButtonKeycodes));
|
||||
resetControllerData();
|
||||
}
|
||||
|
||||
void GameController::resetControllerData() {
|
||||
resetData(mControllerData);
|
||||
resetInfo(mControllerInfo);
|
||||
}
|
||||
|
||||
void GameController::setupController(
|
||||
const Paddleboat_Controller_Mapping_Data *mappingData) {
|
||||
const GameControllerDeviceInfo::InfoFields &infoFields =
|
||||
*(mDeviceInfo.getInfo());
|
||||
uint64_t axisLow = static_cast<uint64_t>(infoFields.mAxisBitsLow);
|
||||
uint64_t axisHigh = static_cast<uint64_t>(infoFields.mAxisBitsHigh);
|
||||
mControllerAxisMask = axisLow | (axisHigh << 32ULL);
|
||||
mControllerInfo.controllerFlags = infoFields.mControllerFlags;
|
||||
mControllerInfo.controllerNumber = infoFields.mControllerNumber;
|
||||
mControllerInfo.deviceId = infoFields.mDeviceId;
|
||||
mControllerInfo.productId = infoFields.mProductId;
|
||||
mControllerInfo.vendorId = infoFields.mVendorId;
|
||||
|
||||
if (mappingData != nullptr) {
|
||||
mControllerInfo.controllerFlags |= mappingData->flags;
|
||||
for (int32_t i = 0; i < PADDLEBOAT_BUTTON_COUNT; ++i) {
|
||||
if (mappingData->buttonMapping[i] != PADDLEBOAT_BUTTON_IGNORED) {
|
||||
mButtonKeycodes[i] = mappingData->buttonMapping[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < PADDLEBOAT_MAPPING_AXIS_COUNT; ++i) {
|
||||
if (mappingData->axisMapping[i] != PADDLEBOAT_AXIS_IGNORED) {
|
||||
const GameControllerAxis gcAxis =
|
||||
static_cast<GameControllerAxis>(i);
|
||||
const int32_t nativeAxisId = mappingData->axisMapping[i];
|
||||
const int32_t positiveButton =
|
||||
(mappingData->axisPositiveButtonMapping[i] ==
|
||||
PADDLEBOAT_AXIS_BUTTON_IGNORED)
|
||||
? 0
|
||||
: (1 << mappingData->axisPositiveButtonMapping[i]);
|
||||
const int32_t negativeButton =
|
||||
(mappingData->axisNegativeButtonMapping[i] ==
|
||||
PADDLEBOAT_AXIS_BUTTON_IGNORED)
|
||||
? 0
|
||||
: (1 << mappingData->axisNegativeButtonMapping[i]);
|
||||
setupAxis(gcAxis, nativeAxisId, nativeAxisId, positiveButton,
|
||||
negativeButton);
|
||||
}
|
||||
}
|
||||
adjustAxisConstants();
|
||||
} else {
|
||||
// Fallback defaults if there wasn't mapping data provided
|
||||
initializeDefaultAxisMapping();
|
||||
memcpy(mButtonKeycodes, &defaultButtonMap, sizeof(ControllerButtonMap));
|
||||
mControllerInfo.controllerFlags |=
|
||||
PADDLEBOAT_CONTROLLER_FLAG_GENERIC_PROFILE;
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::initializeDefaultAxisMapping() {
|
||||
setupAxis(GAMECONTROLLER_AXIS_LSTICK_X, AMOTION_EVENT_AXIS_X,
|
||||
AMOTION_EVENT_AXIS_X, 0, 0);
|
||||
setupAxis(GAMECONTROLLER_AXIS_LSTICK_Y, AMOTION_EVENT_AXIS_Y,
|
||||
AMOTION_EVENT_AXIS_Y, 0, 0);
|
||||
setupAxis(GAMECONTROLLER_AXIS_RSTICK_X, AMOTION_EVENT_AXIS_Z,
|
||||
AMOTION_EVENT_AXIS_RX, 0, 0);
|
||||
setupAxis(GAMECONTROLLER_AXIS_RSTICK_Y, AMOTION_EVENT_AXIS_RZ,
|
||||
AMOTION_EVENT_AXIS_RY, 0, 0);
|
||||
setupAxis(GAMECONTROLLER_AXIS_L2, AMOTION_EVENT_AXIS_LTRIGGER,
|
||||
AMOTION_EVENT_AXIS_BRAKE, PADDLEBOAT_BUTTON_L2, 0);
|
||||
setupAxis(GAMECONTROLLER_AXIS_R2, AMOTION_EVENT_AXIS_RTRIGGER,
|
||||
AMOTION_EVENT_AXIS_GAS, PADDLEBOAT_BUTTON_R2, 0);
|
||||
setupAxis(GAMECONTROLLER_AXIS_HAT_X, AMOTION_EVENT_AXIS_HAT_X,
|
||||
AMOTION_EVENT_AXIS_HAT_X, PADDLEBOAT_BUTTON_DPAD_RIGHT,
|
||||
PADDLEBOAT_BUTTON_DPAD_LEFT);
|
||||
setupAxis(GAMECONTROLLER_AXIS_HAT_Y, AMOTION_EVENT_AXIS_HAT_Y,
|
||||
AMOTION_EVENT_AXIS_HAT_Y, PADDLEBOAT_BUTTON_DPAD_DOWN,
|
||||
PADDLEBOAT_BUTTON_DPAD_UP);
|
||||
adjustAxisConstants();
|
||||
}
|
||||
|
||||
void GameController::adjustAxisConstants() {
|
||||
if (mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_X].axisIndex >= 0) {
|
||||
const bool stickAxisAdjust =
|
||||
((mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_X].axisFlags &
|
||||
GAMECONTROLLER_AXIS_FLAG_APPLY_ADJUSTMENTS) != 0);
|
||||
|
||||
mControllerInfo.leftStickPrecision.stickFlatX =
|
||||
mDeviceInfo.getFlatArray()[mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_X]
|
||||
.axisIndex];
|
||||
mControllerInfo.leftStickPrecision.stickFlatY =
|
||||
mDeviceInfo.getFlatArray()[mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_Y]
|
||||
.axisIndex];
|
||||
mControllerInfo.leftStickPrecision.stickFuzzX =
|
||||
mDeviceInfo.getFuzzArray()[mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_X]
|
||||
.axisIndex];
|
||||
mControllerInfo.leftStickPrecision.stickFuzzY =
|
||||
mDeviceInfo.getFuzzArray()[mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_Y]
|
||||
.axisIndex];
|
||||
if (stickAxisAdjust) {
|
||||
// We are adjusting the raw axis values, so we also adjust the
|
||||
// 'flat' and 'fuzz' values for the sticks
|
||||
mControllerInfo.leftStickPrecision.stickFlatX *=
|
||||
mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_X].axisMultiplier;
|
||||
mControllerInfo.leftStickPrecision.stickFlatY *=
|
||||
mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_Y].axisMultiplier;
|
||||
mControllerInfo.leftStickPrecision.stickFuzzX *=
|
||||
mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_X].axisMultiplier;
|
||||
mControllerInfo.leftStickPrecision.stickFuzzY *=
|
||||
mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_Y].axisMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
if (mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X].axisIndex >= 0) {
|
||||
const bool stickAxisAdjust =
|
||||
((mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X].axisFlags &
|
||||
GAMECONTROLLER_AXIS_FLAG_APPLY_ADJUSTMENTS) != 0);
|
||||
|
||||
mControllerInfo.rightStickPrecision.stickFlatX =
|
||||
mDeviceInfo.getFlatArray()[mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X]
|
||||
.axisIndex];
|
||||
mControllerInfo.rightStickPrecision.stickFlatY =
|
||||
mDeviceInfo.getFlatArray()[mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_Y]
|
||||
.axisIndex];
|
||||
mControllerInfo.rightStickPrecision.stickFuzzX =
|
||||
mDeviceInfo.getFuzzArray()[mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X]
|
||||
.axisIndex];
|
||||
mControllerInfo.rightStickPrecision.stickFuzzY =
|
||||
mDeviceInfo.getFuzzArray()[mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_Y]
|
||||
.axisIndex];
|
||||
if (stickAxisAdjust) {
|
||||
// We are adjusting the raw axis values, so we also adjust the
|
||||
// 'flat' and 'fuzz' values for the sticks
|
||||
mControllerInfo.rightStickPrecision.stickFlatX *=
|
||||
mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X].axisMultiplier;
|
||||
mControllerInfo.rightStickPrecision.stickFlatY *=
|
||||
mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_Y].axisMultiplier;
|
||||
mControllerInfo.rightStickPrecision.stickFuzzX *=
|
||||
mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X].axisMultiplier;
|
||||
mControllerInfo.rightStickPrecision.stickFuzzY *=
|
||||
mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_Y].axisMultiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::setupAxis(const GameControllerAxis gcAxis,
|
||||
const int32_t preferredNativeAxisId,
|
||||
const int32_t secondaryNativeAxisId,
|
||||
const int32_t buttonMask,
|
||||
const int32_t buttonNegativeMask) {
|
||||
mAxisInfo[gcAxis].resetInfo();
|
||||
mAxisInfo[gcAxis].axisButtonMask = buttonMask;
|
||||
mAxisInfo[gcAxis].axisButtonNegativeMask = buttonNegativeMask;
|
||||
|
||||
// Do we have a mapping for the preferred native axis?
|
||||
if (preferredNativeAxisId < MAX_AXIS_COUNT &&
|
||||
secondaryNativeAxisId < MAX_AXIS_COUNT) {
|
||||
const int32_t preferredMask = (1U << preferredNativeAxisId);
|
||||
const int32_t secondaryMask = (1U << secondaryNativeAxisId);
|
||||
if ((mControllerAxisMask & preferredMask) != 0) {
|
||||
// preferred axis is mapped
|
||||
mAxisInfo[gcAxis].axisIndex = preferredNativeAxisId;
|
||||
} else if ((preferredNativeAxisId != secondaryNativeAxisId) &&
|
||||
((mControllerAxisMask & secondaryMask) != 0)) {
|
||||
// secondary axis is mapped
|
||||
mAxisInfo[gcAxis].axisIndex = secondaryNativeAxisId;
|
||||
} else if (buttonMask) {
|
||||
// There wasn't a matching axis,
|
||||
// but we will fake this axis using a digital button mapping
|
||||
mAxisInfo[gcAxis].axisFlags |=
|
||||
GAMECONTROLLER_AXIS_FLAG_DIGITAL_TRIGGER;
|
||||
}
|
||||
|
||||
// If we found a native axis, check its ranges and see if we need to set
|
||||
// up adjustment values if the ranges aren't -1.0 to 1.0 for a stick or
|
||||
// 0.0 to 1.0 for a trigger
|
||||
if (mAxisInfo[gcAxis].axisIndex >= 0) {
|
||||
bool isStickAxis = (!(gcAxis >= GAMECONTROLLER_AXIS_L1 &&
|
||||
gcAxis <= GAMECONTROLLER_AXIS_R2));
|
||||
const float minAdjust = isStickAxis ? 1.0f : 0.0f;
|
||||
const float rawMin =
|
||||
mDeviceInfo.getMinArray()[mAxisInfo[gcAxis].axisIndex];
|
||||
const float rawMax =
|
||||
mDeviceInfo.getMaxArray()[mAxisInfo[gcAxis].axisIndex];
|
||||
const float diffMin = fabsf(rawMin + minAdjust);
|
||||
const float diffMax = fabsf(1.0f - rawMax);
|
||||
if (!(diffMin <= FLT_MIN && diffMax <= FLT_MIN)) {
|
||||
mAxisInfo[gcAxis].axisFlags |=
|
||||
GAMECONTROLLER_AXIS_FLAG_APPLY_ADJUSTMENTS;
|
||||
if (isStickAxis) {
|
||||
// normalize min and max axis to 1.0
|
||||
mAxisInfo[gcAxis].axisMultiplier = 1.0f / rawMax;
|
||||
// Make sure center of stick is 0.0
|
||||
const float rawCenter = ((rawMax - rawMin) * 0.5f) + rawMin;
|
||||
if (rawCenter >= FLT_MIN) {
|
||||
mAxisInfo[gcAxis].axisAdjust =
|
||||
-(rawCenter * mAxisInfo[gcAxis].axisMultiplier);
|
||||
}
|
||||
} else {
|
||||
// This case is hit on PS5 API <= 30 having weird trigger
|
||||
// axis mappings of: L2=RX R2=RY with a -1.0 to 1.0 range
|
||||
mAxisInfo[gcAxis].axisMultiplier = 1.0f / (rawMax - rawMin);
|
||||
mAxisInfo[gcAxis].axisAdjust =
|
||||
(-rawMin) * mAxisInfo[gcAxis].axisMultiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t GameController::processGameActivityKeyEvent(
|
||||
const Paddleboat_GameActivityKeyEvent *event, const size_t eventSize) {
|
||||
return processKeyEventInternal(event->keyCode, event->action);
|
||||
}
|
||||
|
||||
int32_t GameController::processGameActivityMotionEvent(
|
||||
const Paddleboat_GameActivityMotionEvent *event, const size_t eventSize) {
|
||||
int32_t handledEvent = IGNORED_EVENT;
|
||||
if (event->pointerCount > 0) {
|
||||
handledEvent =
|
||||
processMotionEventInternal(event->pointers->axisValues, nullptr);
|
||||
}
|
||||
return handledEvent;
|
||||
}
|
||||
|
||||
int32_t GameController::processKeyEvent(const AInputEvent *event) {
|
||||
const int32_t eventKeyCode = AKeyEvent_getKeyCode(event);
|
||||
const int32_t eventKeyAction = AKeyEvent_getAction(event);
|
||||
return processKeyEventInternal(eventKeyCode, eventKeyAction);
|
||||
}
|
||||
|
||||
int32_t GameController::processKeyEventInternal(const int32_t eventKeyCode,
|
||||
const int32_t eventKeyAction) {
|
||||
int32_t handledEvent = IGNORED_EVENT;
|
||||
int32_t buttonMask = 0;
|
||||
const bool bDown = (eventKeyAction == AKEY_EVENT_ACTION_DOWN);
|
||||
|
||||
for (uint32_t i = 0; i < PADDLEBOAT_BUTTON_COUNT; ++i) {
|
||||
if (eventKeyCode == mButtonKeycodes[i]) {
|
||||
int32_t newButtonFlag = (1 << i);
|
||||
buttonMask |= newButtonFlag;
|
||||
|
||||
// If we have no analog axis mapped for a trigger, allow the
|
||||
// button events to set the analog trigger data
|
||||
switch (newButtonFlag) {
|
||||
case PADDLEBOAT_BUTTON_L1:
|
||||
if (mAxisInfo[GAMECONTROLLER_AXIS_L1].axisIndex == -1) {
|
||||
mControllerData.triggerL1 = bDown ? 1.0f : 0.0f;
|
||||
}
|
||||
break;
|
||||
case PADDLEBOAT_BUTTON_L2:
|
||||
if (mAxisInfo[GAMECONTROLLER_AXIS_L2].axisIndex == -1) {
|
||||
mControllerData.triggerL2 = bDown ? 1.0f : 0.0f;
|
||||
}
|
||||
break;
|
||||
case PADDLEBOAT_BUTTON_R1:
|
||||
if (mAxisInfo[GAMECONTROLLER_AXIS_R1].axisIndex == -1) {
|
||||
mControllerData.triggerR1 = bDown ? 1.0f : 0.0f;
|
||||
}
|
||||
break;
|
||||
case PADDLEBOAT_BUTTON_R2:
|
||||
if (mAxisInfo[GAMECONTROLLER_AXIS_R2].axisIndex == -1) {
|
||||
mControllerData.triggerR2 = bDown ? 1.0f : 0.0f;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (buttonMask != 0) {
|
||||
if (bDown) {
|
||||
mControllerData.buttonsDown |= buttonMask;
|
||||
setControllerDataDirty(true);
|
||||
} else {
|
||||
mControllerData.buttonsDown &= (~buttonMask);
|
||||
setControllerDataDirty(true);
|
||||
}
|
||||
handledEvent = HANDLED_EVENT;
|
||||
}
|
||||
return handledEvent;
|
||||
}
|
||||
|
||||
int32_t GameController::processMotionEvent(const AInputEvent *event) {
|
||||
return processMotionEventInternal(nullptr, event);
|
||||
}
|
||||
|
||||
int32_t GameController::processMotionEventInternal(const float *axisArray,
|
||||
const AInputEvent *event) {
|
||||
int32_t handledEvent = IGNORED_EVENT;
|
||||
|
||||
for (uint32_t axis = GAMECONTROLLER_AXIS_LSTICK_X;
|
||||
axis < GAMECONTROLLER_AXIS_COUNT; ++axis) {
|
||||
if (mAxisInfo[axis].axisIndex >= 0 &&
|
||||
mAxisInfo[axis].axisIndex <
|
||||
PADDLEBOAT_GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) {
|
||||
float axisValue = 0.0f;
|
||||
if (axisArray != nullptr) {
|
||||
axisValue = axisArray[mAxisInfo[axis].axisIndex];
|
||||
} else if (event != nullptr) {
|
||||
axisValue = AMotionEvent_getAxisValue(
|
||||
event, mAxisInfo[axis].axisIndex, 0);
|
||||
}
|
||||
if ((mAxisInfo[axis].axisFlags &
|
||||
GAMECONTROLLER_AXIS_FLAG_APPLY_ADJUSTMENTS) != 0) {
|
||||
axisValue = ((axisValue * mAxisInfo[axis].axisMultiplier) +
|
||||
mAxisInfo[axis].axisAdjust);
|
||||
}
|
||||
|
||||
// We take advantage of the GameControllerAxis matching the axis
|
||||
// order in the Paddleboat_Controller_Data struct to use the current
|
||||
// axis as an index into the axis entries in the
|
||||
// Paddleboat_Controller_Data struct
|
||||
if (axis < GAMECONTROLLER_AXIS_HAT_X) {
|
||||
float *axisData = &mControllerData.leftStick.stickX;
|
||||
axisData[axis] = axisValue;
|
||||
}
|
||||
|
||||
// If this axis has a button associated with it, set/clear the flags
|
||||
// as appropriate
|
||||
if (mAxisInfo[axis].axisButtonMask != 0 ||
|
||||
mAxisInfo[axis].axisButtonNegativeMask) {
|
||||
if (axisValue > -AXIS_BUTTON_THRESHOLD &&
|
||||
axisValue < AXIS_BUTTON_THRESHOLD) {
|
||||
const uint32_t buttonMask =
|
||||
mAxisInfo[axis].axisButtonMask |
|
||||
mAxisInfo[axis].axisButtonNegativeMask;
|
||||
mControllerData.buttonsDown &= (~buttonMask);
|
||||
} else if (axisValue > AXIS_BUTTON_THRESHOLD) {
|
||||
mControllerData.buttonsDown |=
|
||||
mAxisInfo[axis].axisButtonMask;
|
||||
} else if (axisValue < -AXIS_BUTTON_THRESHOLD) {
|
||||
mControllerData.buttonsDown |=
|
||||
mAxisInfo[axis].axisButtonNegativeMask;
|
||||
}
|
||||
}
|
||||
|
||||
setControllerDataDirty(true);
|
||||
handledEvent = HANDLED_EVENT;
|
||||
}
|
||||
}
|
||||
return handledEvent;
|
||||
}
|
||||
|
||||
void GameController::setControllerDataDirty(const bool dirty) {
|
||||
mControllerDataDirty = dirty;
|
||||
if (dirty) {
|
||||
// update the timestamp any time we mark dirty
|
||||
const auto timestamp =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count();
|
||||
mControllerData.timestamp = static_cast<uint64_t>(timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace paddleboat
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/input.h>
|
||||
|
||||
#include "GameControllerDeviceInfo.h"
|
||||
#include "GameControllerGameActivityMirror.h"
|
||||
#include "paddleboat.h"
|
||||
|
||||
namespace paddleboat {
|
||||
|
||||
// set if axisMultiplier/axisAdjustment should be applied (raw device axis isn't
|
||||
// -1.0 to 1.0)
|
||||
inline constexpr uint32_t GAMECONTROLLER_AXIS_FLAG_APPLY_ADJUSTMENTS =
|
||||
(1U << 0);
|
||||
// set if trigger is being faked as an analog axis (device just has a on/off
|
||||
// button flag)
|
||||
inline constexpr uint32_t GAMECONTROLLER_AXIS_FLAG_DIGITAL_TRIGGER = (1U << 1);
|
||||
|
||||
class GameController {
|
||||
public:
|
||||
enum GameControllerAxis {
|
||||
GAMECONTROLLER_AXIS_LSTICK_X = 0,
|
||||
GAMECONTROLLER_AXIS_LSTICK_Y,
|
||||
GAMECONTROLLER_AXIS_RSTICK_X,
|
||||
GAMECONTROLLER_AXIS_RSTICK_Y,
|
||||
GAMECONTROLLER_AXIS_L1,
|
||||
GAMECONTROLLER_AXIS_L2,
|
||||
GAMECONTROLLER_AXIS_R1,
|
||||
GAMECONTROLLER_AXIS_R2,
|
||||
GAMECONTROLLER_AXIS_HAT_X,
|
||||
GAMECONTROLLER_AXIS_HAT_Y,
|
||||
GAMECONTROLLER_AXIS_COUNT
|
||||
};
|
||||
|
||||
struct GameControllerAxisInfo {
|
||||
// Index into the device axis array, -1 is unmapped
|
||||
int32_t axisIndex = -1;
|
||||
// See GAMECONTROLLER_AXIS_FLAG constants
|
||||
uint32_t axisFlags = 0;
|
||||
// Button mask flag, if backed/shadowed by digital button when axis >
|
||||
// 0.0
|
||||
uint32_t axisButtonMask = 0;
|
||||
// Button mask flag, if backed/shadowed by digital button when axis <
|
||||
// 0.0
|
||||
uint32_t axisButtonNegativeMask = 0;
|
||||
// Multiplier to normalize to a 0.0 center -> 1.0 edge
|
||||
float axisMultiplier = 1.0f;
|
||||
// Adjustment to bring center to 0.0 if necessary
|
||||
float axisAdjust = 0.0f;
|
||||
|
||||
void resetInfo() {
|
||||
axisIndex = -1;
|
||||
axisFlags = 0;
|
||||
axisButtonMask = 0;
|
||||
axisButtonNegativeMask = 0;
|
||||
axisMultiplier = 1.0f;
|
||||
axisAdjust = 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
GameController();
|
||||
|
||||
void setupController(const Paddleboat_Controller_Mapping_Data *mappingData);
|
||||
|
||||
void initializeDefaultAxisMapping();
|
||||
|
||||
int32_t processGameActivityKeyEvent(
|
||||
const Paddleboat_GameActivityKeyEvent *event, const size_t eventSize);
|
||||
|
||||
int32_t processGameActivityMotionEvent(
|
||||
const Paddleboat_GameActivityMotionEvent *event,
|
||||
const size_t eventSize);
|
||||
|
||||
int32_t processKeyEvent(const AInputEvent *event);
|
||||
|
||||
int32_t processMotionEvent(const AInputEvent *event);
|
||||
|
||||
Paddleboat_ControllerStatus getControllerStatus() const {
|
||||
return mControllerStatus;
|
||||
}
|
||||
|
||||
void setControllerStatus(
|
||||
const Paddleboat_ControllerStatus controllerStatus) {
|
||||
mControllerStatus = controllerStatus;
|
||||
}
|
||||
|
||||
int32_t getConnectionIndex() const { return mConnectionIndex; }
|
||||
|
||||
void setConnectionIndex(const int32_t connectionIndex) {
|
||||
mConnectionIndex = connectionIndex;
|
||||
}
|
||||
|
||||
uint64_t getControllerAxisMask() const { return mControllerAxisMask; }
|
||||
|
||||
Paddleboat_Controller_Data &getControllerData() { return mControllerData; }
|
||||
|
||||
const Paddleboat_Controller_Data &getControllerData() const {
|
||||
return mControllerData;
|
||||
}
|
||||
|
||||
Paddleboat_Controller_Info &getControllerInfo() { return mControllerInfo; }
|
||||
|
||||
const Paddleboat_Controller_Info &getControllerInfo() const {
|
||||
return mControllerInfo;
|
||||
}
|
||||
|
||||
GameControllerDeviceInfo &getDeviceInfo() { return mDeviceInfo; }
|
||||
|
||||
const GameControllerDeviceInfo &getDeviceInfo() const {
|
||||
return mDeviceInfo;
|
||||
}
|
||||
|
||||
GameControllerAxisInfo *getAxisInfo() { return mAxisInfo; }
|
||||
|
||||
const GameControllerAxisInfo *getAxisInfo() const { return mAxisInfo; }
|
||||
|
||||
bool getControllerDataDirty() const { return mControllerDataDirty; }
|
||||
|
||||
void setControllerDataDirty(const bool dirty);
|
||||
|
||||
void resetControllerData();
|
||||
|
||||
private:
|
||||
int32_t processKeyEventInternal(const int32_t eventKeyCode,
|
||||
const int32_t eventKeyAction);
|
||||
|
||||
int32_t processMotionEventInternal(const float *axisArray,
|
||||
const AInputEvent *event);
|
||||
|
||||
void setupAxis(const GameControllerAxis gcAxis,
|
||||
const int32_t preferredNativeAxisId,
|
||||
const int32_t secondaryNativeAxisId,
|
||||
const int32_t buttonMask, const int32_t buttonNegativeMask);
|
||||
|
||||
void adjustAxisConstants();
|
||||
|
||||
uint64_t mControllerAxisMask = 0;
|
||||
Paddleboat_ControllerStatus mControllerStatus =
|
||||
PADDLEBOAT_CONTROLLER_INACTIVE;
|
||||
int32_t mConnectionIndex = -1;
|
||||
Paddleboat_Controller_Data mControllerData;
|
||||
Paddleboat_Controller_Info mControllerInfo;
|
||||
int32_t mButtonKeycodes[PADDLEBOAT_BUTTON_COUNT];
|
||||
GameControllerAxisInfo mAxisInfo[GAMECONTROLLER_AXIS_COUNT];
|
||||
GameControllerDeviceInfo mDeviceInfo;
|
||||
// Controller data has been updated since the last time it was read
|
||||
bool mControllerDataDirty;
|
||||
};
|
||||
} // namespace paddleboat
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "GameControllerDeviceInfo.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace paddleboat {
|
||||
GameControllerDeviceInfo::GameControllerDeviceInfo() {
|
||||
mName[0] = '\0';
|
||||
mInfo.mDeviceId = -1;
|
||||
mInfo.mProductId = -1;
|
||||
mInfo.mVendorId = -1;
|
||||
mInfo.mAxisBitsLow = 0;
|
||||
mInfo.mAxisBitsHigh = 0;
|
||||
mInfo.mControllerNumber = -1;
|
||||
mInfo.mControllerFlags = 0;
|
||||
|
||||
for (size_t i = 0; i < paddleboat::MAX_AXIS_COUNT; ++i) {
|
||||
mAxisMinArray[i] = 0.0f;
|
||||
mAxisMaxArray[i] = 0.0f;
|
||||
mAxisFlatArray[i] = 0.0f;
|
||||
mAxisFuzzArray[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void GameControllerDeviceInfo::setName(const char *name) {
|
||||
strncpy(mName, name, DEVICEINFO_MAX_NAME_LENGTH);
|
||||
}
|
||||
} // namespace paddleboat
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameControllerInternalConstants.h"
|
||||
|
||||
namespace paddleboat {
|
||||
|
||||
class GameControllerDeviceInfo {
|
||||
public:
|
||||
// These are copied directly from the int[] mGameControllerDeviceInfoArray,
|
||||
// the layout should match the field definitions in
|
||||
// Paddleboat_Controller_Info.java
|
||||
struct InfoFields {
|
||||
int32_t mDeviceId;
|
||||
int32_t mVendorId;
|
||||
int32_t mProductId;
|
||||
int32_t mAxisBitsLow;
|
||||
int32_t mAxisBitsHigh;
|
||||
int32_t mControllerNumber;
|
||||
int32_t mControllerFlags;
|
||||
};
|
||||
|
||||
GameControllerDeviceInfo();
|
||||
|
||||
void setName(const char *name);
|
||||
|
||||
const char *getName() const { return mName; }
|
||||
|
||||
const InfoFields &getInfo() const { return mInfo; }
|
||||
|
||||
const float *getMinArray() const { return mAxisMinArray; }
|
||||
|
||||
const float *getMaxArray() const { return mAxisMaxArray; }
|
||||
|
||||
const float *getFlatArray() const { return mAxisFlatArray; }
|
||||
|
||||
const float *getFuzzArray() const { return mAxisFuzzArray; }
|
||||
|
||||
InfoFields *getInfo() { return &mInfo; }
|
||||
|
||||
float *getMinArray() { return mAxisMinArray; }
|
||||
|
||||
float *getMaxArray() { return mAxisMaxArray; }
|
||||
|
||||
float *getFlatArray() { return mAxisFlatArray; }
|
||||
|
||||
float *getFuzzArray() { return mAxisFuzzArray; }
|
||||
|
||||
private:
|
||||
char mName[paddleboat::DEVICEINFO_MAX_NAME_LENGTH];
|
||||
InfoFields mInfo;
|
||||
float mAxisMinArray[paddleboat::MAX_AXIS_COUNT];
|
||||
float mAxisMaxArray[paddleboat::MAX_AXIS_COUNT];
|
||||
float mAxisFlatArray[paddleboat::MAX_AXIS_COUNT];
|
||||
float mAxisFuzzArray[paddleboat::MAX_AXIS_COUNT];
|
||||
};
|
||||
} // namespace paddleboat
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace paddleboat {
|
||||
// GameActivity needs to use its own event data structures for key and motion
|
||||
// events. We do not want to have a dependency on GameActivity. Unfortunately,
|
||||
// this means we need to internally mirror the relevant structures.
|
||||
// Paddleboat_processGameActivityInputEvent includes a struct size parameter,
|
||||
// and we know that the GameActivity structures will only ever add fields, so we
|
||||
// can determine if we are being passed a later version of the struct than the
|
||||
// mirrored internal version.
|
||||
|
||||
// The following should mirror GameActivity.h
|
||||
#define PADDLEBOAT_GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT 48
|
||||
#define PADDLEBOAT_MAX_NUM_POINTERS_IN_MOTION_EVENT 8
|
||||
|
||||
typedef struct Paddleboat_GameActivityPointerInfo {
|
||||
int32_t id;
|
||||
float axisValues[PADDLEBOAT_GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT];
|
||||
float rawX;
|
||||
float rawY;
|
||||
} Paddleboat_GameActivityPointerInfo;
|
||||
|
||||
typedef struct Paddleboat_GameActivityMotionEvent {
|
||||
int32_t deviceId;
|
||||
int32_t source;
|
||||
int32_t action;
|
||||
int64_t eventTime;
|
||||
int64_t downTime;
|
||||
int32_t flags;
|
||||
int32_t metaState;
|
||||
int32_t actionButton;
|
||||
int32_t buttonState;
|
||||
int32_t classification;
|
||||
int32_t edgeFlags;
|
||||
uint32_t pointerCount;
|
||||
Paddleboat_GameActivityPointerInfo
|
||||
pointers[PADDLEBOAT_MAX_NUM_POINTERS_IN_MOTION_EVENT];
|
||||
float precisionX;
|
||||
float precisionY;
|
||||
} Paddleboat_GameActivityMotionEvent;
|
||||
|
||||
typedef struct Paddleboat_GameActivityKeyEvent {
|
||||
int32_t deviceId;
|
||||
int32_t source;
|
||||
int32_t action;
|
||||
int64_t eventTime;
|
||||
int64_t downTime;
|
||||
int32_t flags;
|
||||
int32_t metaState;
|
||||
int32_t modifiers;
|
||||
int32_t repeatCount;
|
||||
int32_t keyCode;
|
||||
} Paddleboat_GameActivityKeyEvent;
|
||||
} // namespace paddleboat
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace paddleboat {
|
||||
inline constexpr size_t MAX_AXIS_COUNT = 48;
|
||||
// Must match GameControllerManager.DEVICEINFO_ARRAY_SIZE
|
||||
inline constexpr size_t DEVICEINFO_ARRAY_SIZE = 7;
|
||||
inline constexpr size_t DEVICEINFO_ARRAY_BYTESIZE =
|
||||
sizeof(int32_t) * DEVICEINFO_ARRAY_SIZE;
|
||||
inline constexpr size_t DEVICEINFO_MAX_NAME_LENGTH = 128;
|
||||
|
||||
inline constexpr int32_t IGNORED_EVENT = 0;
|
||||
inline constexpr int32_t HANDLED_EVENT = 1;
|
||||
} // namespace paddleboat
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "GameControllerLog.h"
|
||||
|
||||
#include <android/keycodes.h>
|
||||
|
||||
#include "GameControllerLogStrings.h"
|
||||
#include "Log.h"
|
||||
|
||||
#define LOG_TAG "GameControllerManager"
|
||||
// Filter input event logging to qualifying 'gamecontroller' event sources
|
||||
#define LOG_FILTER_PADDLEBOAT_SOURCES
|
||||
|
||||
#define ELEMENTS_OF(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
namespace paddleboat {
|
||||
const char *LogGetInputSourceString(const int32_t eventSource) {
|
||||
const char *inputSourceString = "AINPUT_SOURCE_UNKNOWN";
|
||||
|
||||
switch (eventSource) {
|
||||
case AINPUT_SOURCE_KEYBOARD:
|
||||
inputSourceString = "AINPUT_SOURCE_KEYBOARD";
|
||||
break;
|
||||
case AINPUT_SOURCE_DPAD:
|
||||
inputSourceString = "AINPUT_SOURCE_DPAD";
|
||||
break;
|
||||
case AINPUT_SOURCE_GAMEPAD:
|
||||
inputSourceString = "AINPUT_SOURCE_GAMEPAD";
|
||||
break;
|
||||
case AINPUT_SOURCE_TOUCHSCREEN:
|
||||
inputSourceString = "AINPUT_SOURCE_TOUCHSCREEN";
|
||||
break;
|
||||
case AINPUT_SOURCE_MOUSE:
|
||||
inputSourceString = "AINPUT_SOURCE_MOUSE";
|
||||
break;
|
||||
case AINPUT_SOURCE_STYLUS:
|
||||
inputSourceString = "AINPUT_SOURCE_STYLUS";
|
||||
break;
|
||||
case AINPUT_SOURCE_BLUETOOTH_STYLUS:
|
||||
inputSourceString = "AINPUT_SOURCE_BLUETOOTH_STYLUS";
|
||||
break;
|
||||
case AINPUT_SOURCE_MOUSE_RELATIVE:
|
||||
inputSourceString = "AINPUT_SOURCE_MOUSE_RELATIVE";
|
||||
break;
|
||||
case AINPUT_SOURCE_TOUCHPAD:
|
||||
inputSourceString = "AINPUT_SOURCE_TOUCHPAD";
|
||||
break;
|
||||
case AINPUT_SOURCE_TOUCH_NAVIGATION:
|
||||
inputSourceString = "AINPUT_SOURCE_TOUCH_NAVIGATION";
|
||||
break;
|
||||
case AINPUT_SOURCE_JOYSTICK:
|
||||
inputSourceString = "AINPUT_SOURCE_JOYSTICK";
|
||||
break;
|
||||
case AINPUT_SOURCE_ROTARY_ENCODER:
|
||||
inputSourceString = "AINPUT_SOURCE_ROTARY_ENCODER";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return inputSourceString;
|
||||
}
|
||||
|
||||
void LogInputEvent(const AInputEvent *event) {
|
||||
const int32_t eventSource = AInputEvent_getSource(event);
|
||||
#if defined LOG_FILTER_PADDLEBOAT_SOURCES
|
||||
if (!(eventSource == AINPUT_SOURCE_DPAD ||
|
||||
eventSource == AINPUT_SOURCE_GAMEPAD ||
|
||||
eventSource == AINPUT_SOURCE_JOYSTICK)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
const int32_t eventDeviceId = AInputEvent_getDeviceId(event);
|
||||
const int32_t eventType = AInputEvent_getType(event);
|
||||
const char *inputSourceString = LogGetInputSourceString(eventSource);
|
||||
|
||||
if (eventType == AINPUT_EVENT_TYPE_KEY) {
|
||||
const int32_t eventAction = AKeyEvent_getAction(event);
|
||||
const int32_t eventFlags = AKeyEvent_getFlags(event);
|
||||
const int32_t eventKeycode = AKeyEvent_getKeyCode(event);
|
||||
const char *actionString =
|
||||
(eventAction < ELEMENTS_OF(AKEY_ACTION_STRINGS) && eventAction >= 0)
|
||||
? AKEY_ACTION_STRINGS[eventAction]
|
||||
: "AKEY_ACTION out of range";
|
||||
const char *keycodeString =
|
||||
(eventKeycode < ELEMENTS_OF(AKEYCODE_STRINGS) && eventKeycode >= 0)
|
||||
? AKEYCODE_STRINGS[eventKeycode]
|
||||
: "AKEYCODE out of range";
|
||||
ALOGI(
|
||||
"LogInputEvent\nAINPUT_EVENT_TYPE_KEY deviceId %d source %s\n%s %s "
|
||||
"%08x",
|
||||
eventDeviceId, inputSourceString, actionString, keycodeString,
|
||||
eventFlags);
|
||||
} else if (eventType == AINPUT_EVENT_TYPE_MOTION) {
|
||||
const int32_t eventAction =
|
||||
AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK;
|
||||
const int32_t eventFlags = AMotionEvent_getFlags(event);
|
||||
const char *actionString =
|
||||
(eventAction < ELEMENTS_OF(AMOTION_ACTION_STRINGS) &&
|
||||
eventAction >= 0)
|
||||
? AMOTION_ACTION_STRINGS[eventAction]
|
||||
: "AMOTION_ACTION out of range";
|
||||
ALOGI(
|
||||
"LogInputEvent\nAINPUT_EVENT_TYPE_MOTION deviceId %d source %s\n%s "
|
||||
"%08x",
|
||||
eventDeviceId, inputSourceString, actionString, eventFlags);
|
||||
}
|
||||
}
|
||||
} // namespace paddleboat
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/input.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace paddleboat {
|
||||
const char *LogGetInputSourceString(const int32_t eventSource);
|
||||
|
||||
void LogInputEvent(const AInputEvent *event);
|
||||
} // namespace paddleboat
|
||||
|
|
@ -0,0 +1,391 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace paddleboat {
|
||||
// String tables for debug logging purposes that must exactly map
|
||||
// to the enum tables in input.h and keycode.h
|
||||
const char *AINPUT_EVENT_STRINGS[3] = {
|
||||
"AINPUT_EVENT_NULL", // 0
|
||||
"AINPUT_EVENT_TYPE_KEY", // 1
|
||||
"AINPUT_EVENT_TYPE_MOTION" // 2
|
||||
};
|
||||
|
||||
const char *AKEY_ACTION_STRINGS[3] = {
|
||||
"AKEY_EVENT_ACTION_DOWN", // 0
|
||||
"AKEY_EVENT_ACTION_UP", // 1
|
||||
"AKEY_EVENT_ACTION_MULTIPLE" // 2
|
||||
};
|
||||
|
||||
const char *AKEYCODE_STRINGS[289] = {
|
||||
"AKEYCODE_UNKNOWN", // 0
|
||||
"AKEYCODE_SOFT_LEFT", // 1
|
||||
"AKEYCODE_SOFT_RIGHT", // 2
|
||||
"AKEYCODE_HOME", // 3
|
||||
"AKEYCODE_BACK", // 4
|
||||
"AKEYCODE_CALL", // 5
|
||||
"AKEYCODE_ENDCALL", // 6
|
||||
"AKEYCODE_0", // 7
|
||||
"AKEYCODE_1", // 8
|
||||
"AKEYCODE_2", // 9
|
||||
"AKEYCODE_3", // 10
|
||||
"AKEYCODE_4", // 11
|
||||
"AKEYCODE_5", // 12
|
||||
"AKEYCODE_6", // 13
|
||||
"AKEYCODE_7", // 14
|
||||
"AKEYCODE_8", // 15
|
||||
"AKEYCODE_9", // 16
|
||||
"AKEYCODE_STAR", // 17
|
||||
"AKEYCODE_POUND", // 18
|
||||
"AKEYCODE_DPAD_UP", // 19
|
||||
"AKEYCODE_DPAD_DOWN", // 20
|
||||
"AKEYCODE_DPAD_LEFT", // 21
|
||||
"AKEYCODE_DPAD_RIGHT", // 22
|
||||
"AKEYCODE_DPAD_CENTER", // 23
|
||||
"AKEYCODE_VOLUME_UP", // 24
|
||||
"AKEYCODE_VOLUME_DOWN", // 25
|
||||
"AKEYCODE_POWER", // 26
|
||||
"AKEYCODE_CAMERA", // 27
|
||||
"AKEYCODE_CLEAR", // 28
|
||||
"AKEYCODE_A", // 29
|
||||
"AKEYCODE_B", // 30
|
||||
"AKEYCODE_C", // 31
|
||||
"AKEYCODE_D", // 32
|
||||
"AKEYCODE_E", // 33
|
||||
"AKEYCODE_F", // 34
|
||||
"AKEYCODE_G", // 35
|
||||
"AKEYCODE_H", // 36
|
||||
"AKEYCODE_I", // 37
|
||||
"AKEYCODE_J", // 38
|
||||
"AKEYCODE_K", // 39
|
||||
"AKEYCODE_L", // 40
|
||||
"AKEYCODE_M", // 41
|
||||
"AKEYCODE_N", // 42
|
||||
"AKEYCODE_O", // 43
|
||||
"AKEYCODE_P", // 44
|
||||
"AKEYCODE_Q", // 45
|
||||
"AKEYCODE_R", // 46
|
||||
"AKEYCODE_S", // 47
|
||||
"AKEYCODE_T", // 48
|
||||
"AKEYCODE_U", // 49
|
||||
"AKEYCODE_V", // 50
|
||||
"AKEYCODE_W", // 51
|
||||
"AKEYCODE_X", // 52
|
||||
"AKEYCODE_Y", // 53
|
||||
"AKEYCODE_Z", // 54
|
||||
"AKEYCODE_COMMA", // 55
|
||||
"AKEYCODE_PERIOD", // 56
|
||||
"AKEYCODE_ALT_LEFT", // 57
|
||||
"AKEYCODE_ALT_RIGHT", // 58
|
||||
"AKEYCODE_SHIFT_LEFT", // 59
|
||||
"AKEYCODE_SHIFT_RIGHT", // 60
|
||||
"AKEYCODE_TAB", // 61
|
||||
"AKEYCODE_SPACE", // 62
|
||||
"AKEYCODE_SYM", // 63
|
||||
"AKEYCODE_EXPLORER", // 64
|
||||
"AKEYCODE_ENVELOPE", // 65
|
||||
"AKEYCODE_ENTER", // 66
|
||||
"AKEYCODE_DEL", // 67
|
||||
"AKEYCODE_GRAVE", // 68
|
||||
"AKEYCODE_MINUS", // 69
|
||||
"AKEYCODE_EQUALS", // 70
|
||||
"AKEYCODE_LEFT_BRACKET", // 71
|
||||
"AKEYCODE_RIGHT_BRACKET", // 72
|
||||
"AKEYCODE_BACKSLASH", // 73
|
||||
"AKEYCODE_SEMICOLON", // 74
|
||||
"AKEYCODE_APOSTROPHE", // 75
|
||||
"AKEYCODE_SLASH", // 76
|
||||
"AKEYCODE_AT", // 77
|
||||
"AKEYCODE_NUM", // 78
|
||||
"AKEYCODE_HEADSETHOOK", // 79
|
||||
"AKEYCODE_FOCUS", // 80
|
||||
"AKEYCODE_PLUS", // 81
|
||||
"AKEYCODE_MENU", // 82
|
||||
"AKEYCODE_NOTIFICATION", // 83
|
||||
"AKEYCODE_SEARCH", // 84
|
||||
"AKEYCODE_MEDIA_PLAY_PAUSE", // 85
|
||||
"AKEYCODE_MEDIA_STOP", // 86
|
||||
"AKEYCODE_MEDIA_NEXT", // 87
|
||||
"AKEYCODE_MEDIA_PREVIOUS", // 88
|
||||
"AKEYCODE_MEDIA_REWIND", // 89
|
||||
"AKEYCODE_MEDIA_FAST_FORWARD", // 90
|
||||
"AKEYCODE_MUTE", // 91
|
||||
"AKEYCODE_PAGE_UP", // 92
|
||||
"AKEYCODE_PAGE_DOWN", // 93
|
||||
"AKEYCODE_PICTSYMBOLS", // 94
|
||||
"AKEYCODE_SWITCH_CHARSET", // 95
|
||||
"AKEYCODE_BUTTON_A", // 96
|
||||
"AKEYCODE_BUTTON_B", // 97
|
||||
"AKEYCODE_BUTTON_C", // 98
|
||||
"AKEYCODE_BUTTON_X", // 99
|
||||
"AKEYCODE_BUTTON_Y", // 100
|
||||
"AKEYCODE_BUTTON_Z", // 101
|
||||
"AKEYCODE_BUTTON_L1", // 102
|
||||
"AKEYCODE_BUTTON_R1", // 103
|
||||
"AKEYCODE_BUTTON_L2", // 104
|
||||
"AKEYCODE_BUTTON_R2", // 105
|
||||
"AKEYCODE_BUTTON_THUMBL", // 106
|
||||
"AKEYCODE_BUTTON_THUMBR", // 107
|
||||
"AKEYCODE_BUTTON_START", // 108
|
||||
"AKEYCODE_BUTTON_SELECT", // 109
|
||||
"AKEYCODE_BUTTON_MODE", // 110
|
||||
"AKEYCODE_ESCAPE", // 111
|
||||
"AKEYCODE_FORWARD_DEL", // 112
|
||||
"AKEYCODE_CTRL_LEFT", // 113
|
||||
"AKEYCODE_CTRL_RIGHT", // 114
|
||||
"AKEYCODE_CAPS_LOCK", // 115
|
||||
"AKEYCODE_SCROLL_LOCK", // 116
|
||||
"AKEYCODE_META_LEFT", // 117
|
||||
"AKEYCODE_META_RIGHT", // 118
|
||||
"AKEYCODE_FUNCTION", // 119
|
||||
"AKEYCODE_SYSRQ", // 120
|
||||
"AKEYCODE_BREAK", // 121
|
||||
"AKEYCODE_MOVE_HOME", // 122
|
||||
"AKEYCODE_MOVE_END", // 123
|
||||
"AKEYCODE_INSERT", // 124
|
||||
"AKEYCODE_FORWARD", // 125
|
||||
"AKEYCODE_MEDIA_PLAY", // 126
|
||||
"AKEYCODE_MEDIA_PAUSE", // 127
|
||||
"AKEYCODE_MEDIA_CLOSE", // 128
|
||||
"AKEYCODE_MEDIA_EJECT", // 129
|
||||
"AKEYCODE_MEDIA_RECORD", // 130
|
||||
"AKEYCODE_F1", // 131
|
||||
"AKEYCODE_F2", // 132
|
||||
"AKEYCODE_F3", // 133
|
||||
"AKEYCODE_F4", // 134
|
||||
"AKEYCODE_F5", // 135
|
||||
"AKEYCODE_F6", // 136
|
||||
"AKEYCODE_F7", // 137
|
||||
"AKEYCODE_F8", // 138
|
||||
"AKEYCODE_F9", // 139
|
||||
"AKEYCODE_F10", // 140
|
||||
"AKEYCODE_F11", // 141
|
||||
"AKEYCODE_F12", // 142
|
||||
"AKEYCODE_NUM_LOCK", // 143
|
||||
"AKEYCODE_NUMPAD_0", // 144
|
||||
"AKEYCODE_NUMPAD_1", // 145
|
||||
"AKEYCODE_NUMPAD_2", // 146
|
||||
"AKEYCODE_NUMPAD_3", // 147
|
||||
"AKEYCODE_NUMPAD_4", // 148
|
||||
"AKEYCODE_NUMPAD_5", // 149
|
||||
"AKEYCODE_NUMPAD_6", // 150
|
||||
"AKEYCODE_NUMPAD_7", // 151
|
||||
"AKEYCODE_NUMPAD_8", // 152
|
||||
"AKEYCODE_NUMPAD_9", // 153
|
||||
"AKEYCODE_NUMPAD_DIVIDE", // 154
|
||||
"AKEYCODE_NUMPAD_MULTIPLY", // 155
|
||||
"AKEYCODE_NUMPAD_SUBTRACT", // 156
|
||||
"AKEYCODE_NUMPAD_ADD", // 157
|
||||
"AKEYCODE_NUMPAD_DOT", // 158
|
||||
"AKEYCODE_NUMPAD_COMMA", // 159
|
||||
"AKEYCODE_NUMPAD_ENTER", // 160
|
||||
"AKEYCODE_NUMPAD_EQUALS", // 161
|
||||
"AKEYCODE_NUMPAD_LEFT_PAREN", // 162
|
||||
"AKEYCODE_NUMPAD_RIGHT_PAREN", // 163
|
||||
"AKEYCODE_VOLUME_MUTE", // 164
|
||||
"AKEYCODE_INFO", // 165
|
||||
"AKEYCODE_CHANNEL_UP", // 166
|
||||
"AKEYCODE_CHANNEL_DOWN", // 167
|
||||
"AKEYCODE_ZOOM_IN", // 168
|
||||
"AKEYCODE_ZOOM_OUT", // 169
|
||||
"AKEYCODE_TV", // 170
|
||||
"AKEYCODE_WINDOW", // 171
|
||||
"AKEYCODE_GUIDE", // 172
|
||||
"AKEYCODE_DVR", // 173
|
||||
"AKEYCODE_BOOKMARK", // 174
|
||||
"AKEYCODE_CAPTIONS", // 175
|
||||
"AKEYCODE_SETTINGS", // 176
|
||||
"AKEYCODE_TV_POWER", // 177
|
||||
"AKEYCODE_TV_INPUT", // 178
|
||||
"AKEYCODE_STB_POWER", // 179
|
||||
"AKEYCODE_STB_INPUT", // 180
|
||||
"AKEYCODE_AVR_POWER", // 181
|
||||
"AKEYCODE_AVR_INPUT", // 182
|
||||
"AKEYCODE_PROG_RED", // 183
|
||||
"AKEYCODE_PROG_GREEN", // 184
|
||||
"AKEYCODE_PROG_YELLOW", // 185
|
||||
"AKEYCODE_PROG_BLUE", // 186
|
||||
"AKEYCODE_APP_SWITCH", // 187
|
||||
"AKEYCODE_BUTTON_1", // 188
|
||||
"AKEYCODE_BUTTON_2", // 189
|
||||
"AKEYCODE_BUTTON_3", // 190
|
||||
"AKEYCODE_BUTTON_4", // 191
|
||||
"AKEYCODE_BUTTON_5", // 192
|
||||
"AKEYCODE_BUTTON_6", // 193
|
||||
"AKEYCODE_BUTTON_7", // 194
|
||||
"AKEYCODE_BUTTON_8", // 195
|
||||
"AKEYCODE_BUTTON_9", // 196
|
||||
"AKEYCODE_BUTTON_10", // 197
|
||||
"AKEYCODE_BUTTON_11", // 198
|
||||
"AKEYCODE_BUTTON_12", // 199
|
||||
"AKEYCODE_BUTTON_13", // 200
|
||||
"AKEYCODE_BUTTON_14", // 201
|
||||
"AKEYCODE_BUTTON_15", // 202
|
||||
"AKEYCODE_BUTTON_16", // 203
|
||||
"AKEYCODE_LANGUAGE_SWITCH", // 204
|
||||
"AKEYCODE_MANNER_MODE", // 205
|
||||
"AKEYCODE_3D_MODE", // 206
|
||||
"AKEYCODE_CONTACTS", // 207
|
||||
"AKEYCODE_CALENDAR", // 208
|
||||
"AKEYCODE_MUSIC", // 209
|
||||
"AKEYCODE_CALCULATOR", // 210
|
||||
"AKEYCODE_ZENKAKU_HANKAKU", // 211
|
||||
"AKEYCODE_EISU", // 212
|
||||
"AKEYCODE_MUHENKAN", // 213
|
||||
"AKEYCODE_HENKAN", // 214
|
||||
"AKEYCODE_KATAKANA_HIRAGANA", // 215
|
||||
"AKEYCODE_YEN", // 216
|
||||
"AKEYCODE_RO", // 217
|
||||
"AKEYCODE_KANA", // 218
|
||||
"AKEYCODE_ASSIST", // 219
|
||||
"AKEYCODE_BRIGHTNESS_DOWN", // 220
|
||||
"AKEYCODE_BRIGHTNESS_UP", // 221
|
||||
"AKEYCODE_MEDIA_AUDIO_TRACK", // 222
|
||||
"AKEYCODE_SLEEP", // 223
|
||||
"AKEYCODE_WAKEUP", // 224
|
||||
"AKEYCODE_PAIRING", // 225
|
||||
"AKEYCODE_MEDIA_TOP_MENU", // 226
|
||||
"AKEYCODE_11", // 227
|
||||
"AKEYCODE_12", // 228
|
||||
"AKEYCODE_LAST_CHANNEL", // 229
|
||||
"AKEYCODE_TV_DATA_SERVICE", // 230
|
||||
"AKEYCODE_VOICE_ASSIST", // 231
|
||||
"AKEYCODE_TV_RADIO_SERVICE", // 232
|
||||
"AKEYCODE_TV_TELETEXT", // 233
|
||||
"AKEYCODE_TV_NUMBER_ENTRY", // 234
|
||||
"AKEYCODE_TV_TERRESTRIAL_ANALOG", // 235
|
||||
"AKEYCODE_TV_TERRESTRIAL_DIGITAL", // 236
|
||||
"AKEYCODE_TV_SATELLITE", // 237
|
||||
"AKEYCODE_TV_SATELLITE_BS", // 238
|
||||
"AKEYCODE_TV_SATELLITE_CS", // 239
|
||||
"AKEYCODE_TV_SATELLITE_SERVICE", // 240
|
||||
"AKEYCODE_TV_NETWORK", // 241
|
||||
"AKEYCODE_TV_ANTENNA_CABLE", // 242
|
||||
"AKEYCODE_TV_INPUT_HDMI_1", // 243
|
||||
"AKEYCODE_TV_INPUT_HDMI_2", // 244
|
||||
"AKEYCODE_TV_INPUT_HDMI_3", // 245
|
||||
"AKEYCODE_TV_INPUT_HDMI_4", // 246
|
||||
"AKEYCODE_TV_INPUT_COMPOSITE_1", // 247
|
||||
"AKEYCODE_TV_INPUT_COMPOSITE_2", // 248
|
||||
"AKEYCODE_TV_INPUT_COMPONENT_1", // 249
|
||||
"AKEYCODE_TV_INPUT_COMPONENT_2", // 250
|
||||
"AKEYCODE_TV_INPUT_VGA_1", // 251
|
||||
"AKEYCODE_TV_AUDIO_DESCRIPTION", // 252
|
||||
"AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP", // 253
|
||||
"AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN", // 254
|
||||
"AKEYCODE_TV_ZOOM_MODE", // 255
|
||||
"AKEYCODE_TV_CONTENTS_MENU", // 256
|
||||
"AKEYCODE_TV_MEDIA_CONTEXT_MENU", // 257
|
||||
"AKEYCODE_TV_TIMER_PROGRAMMING", // 258
|
||||
"AKEYCODE_HELP", // 259
|
||||
"AKEYCODE_NAVIGATE_PREVIOUS", // 260
|
||||
"AKEYCODE_NAVIGATE_NEXT", // 261
|
||||
"AKEYCODE_NAVIGATE_IN", // 262
|
||||
"AKEYCODE_NAVIGATE_OUT", // 263
|
||||
"AKEYCODE_STEM_PRIMARY", // 264
|
||||
"AKEYCODE_STEM_1", // 265
|
||||
"AKEYCODE_STEM_2", // 266
|
||||
"AKEYCODE_STEM_3", // 267
|
||||
"AKEYCODE_DPAD_UP_LEFT", // 268
|
||||
"AKEYCODE_DPAD_DOWN_LEFT", // 269
|
||||
"AKEYCODE_DPAD_UP_RIGHT", // 270
|
||||
"AKEYCODE_DPAD_DOWN_RIGHT", // 271
|
||||
"AKEYCODE_MEDIA_SKIP_FORWARD", // 272
|
||||
"AKEYCODE_MEDIA_SKIP_BACKWARD", // 273
|
||||
"AKEYCODE_MEDIA_STEP_FORWARD", // 274
|
||||
"AKEYCODE_MEDIA_STEP_BACKWARD", // 275
|
||||
"AKEYCODE_SOFT_SLEEP", // 276
|
||||
"AKEYCODE_CUT", // 277
|
||||
"AKEYCODE_COPY", // 278
|
||||
"AKEYCODE_PASTE", // 279
|
||||
"AKEYCODE_SYSTEM_NAVIGATION_UP", // 280
|
||||
"AKEYCODE_SYSTEM_NAVIGATION_DOWN", // 281
|
||||
"AKEYCODE_SYSTEM_NAVIGATION_LEFT", // 282
|
||||
"AKEYCODE_SYSTEM_NAVIGATION_RIGHT", // 283
|
||||
"AKEYCODE_ALL_APPS", // 284
|
||||
"AKEYCODE_REFRESH", // 285
|
||||
"AKEYCODE_THUMBS_UP", // 286
|
||||
"AKEYCODE_THUMBS_DOWN", // 287
|
||||
"AKEYCODE_PROFILE_SWITCH", // 288
|
||||
};
|
||||
|
||||
const char *AMOTION_AXIS_STRINGS[48] = {
|
||||
"AMOTION_EVENT_AXIS_X", // 0
|
||||
"AMOTION_EVENT_AXIS_Y", // 1
|
||||
"AMOTION_EVENT_AXIS_PRESSURE", // 2
|
||||
"AMOTION_EVENT_AXIS_SIZE", // 3
|
||||
"AMOTION_EVENT_AXIS_TOUCH_MAJOR", // 4
|
||||
"AMOTION_EVENT_AXIS_TOUCH_MINOR", // 5
|
||||
"AMOTION_EVENT_AXIS_TOOL_MAJOR", // 6
|
||||
"AMOTION_EVENT_AXIS_TOOL_MINOR", // 7
|
||||
"AMOTION_EVENT_AXIS_ORIENTATION", // 8
|
||||
"AMOTION_EVENT_AXIS_VSCROLL", // 9
|
||||
"AMOTION_EVENT_AXIS_HSCROLL", // 10
|
||||
"AMOTION_EVENT_AXIS_Z", // 11
|
||||
"AMOTION_EVENT_AXIS_RX", // 12
|
||||
"AMOTION_EVENT_AXIS_RY", // 13
|
||||
"AMOTION_EVENT_AXIS_RZ", // 14
|
||||
"AMOTION_EVENT_AXIS_HAT_X", // 15
|
||||
"AMOTION_EVENT_AXIS_HAT_Y", // 16
|
||||
"AMOTION_EVENT_AXIS_LTRIGGER", // 17
|
||||
"AMOTION_EVENT_AXIS_RTRIGGER", // 18
|
||||
"AMOTION_EVENT_AXIS_THROTTLE", // 19
|
||||
"AMOTION_EVENT_AXIS_RUDDER", // 20
|
||||
"AMOTION_EVENT_AXIS_WHEEL", // 21
|
||||
"AMOTION_EVENT_AXIS_GAS", // 22
|
||||
"AMOTION_EVENT_AXIS_BRAKE", // 23
|
||||
"AMOTION_EVENT_AXIS_DISTANCE", // 24
|
||||
"AMOTION_EVENT_AXIS_TILT", // 25
|
||||
"AMOTION_EVENT_AXIS_SCROLL", // 26
|
||||
"AMOTION_EVENT_AXIS_RELATIVE_X", // 27
|
||||
"AMOTION_EVENT_AXIS_RELATIVE_Y", // 28
|
||||
"AMOTION_EVENT_UNDEFINED", // 29
|
||||
"AMOTION_EVENT_UNDEFINED", // 30
|
||||
"AMOTION_EVENT_UNDEFINED", // 31
|
||||
"AMOTION_EVENT_AXIS_GENERIC_1", // 32
|
||||
"AMOTION_EVENT_AXIS_GENERIC_2", // 33
|
||||
"AMOTION_EVENT_AXIS_GENERIC_3", // 34
|
||||
"AMOTION_EVENT_AXIS_GENERIC_4", // 35
|
||||
"AMOTION_EVENT_AXIS_GENERIC_5", // 36
|
||||
"AMOTION_EVENT_AXIS_GENERIC_6", // 37
|
||||
"AMOTION_EVENT_AXIS_GENERIC_7", // 38
|
||||
"AMOTION_EVENT_AXIS_GENERIC_8", // 39
|
||||
"AMOTION_EVENT_AXIS_GENERIC_9", // 40
|
||||
"AMOTION_EVENT_AXIS_GENERIC_10", // 41
|
||||
"AMOTION_EVENT_AXIS_GENERIC_11", // 42
|
||||
"AMOTION_EVENT_AXIS_GENERIC_12", // 43
|
||||
"AMOTION_EVENT_AXIS_GENERIC_13", // 44
|
||||
"AMOTION_EVENT_AXIS_GENERIC_14", // 45
|
||||
"AMOTION_EVENT_AXIS_GENERIC_15", // 46
|
||||
"AMOTION_EVENT_AXIS_GENERIC_16" // 47
|
||||
};
|
||||
|
||||
const char *AMOTION_ACTION_STRINGS[13] = {
|
||||
"AMOTION_EVENT_ACTION_DOWN", // 0
|
||||
"AMOTION_EVENT_ACTION_UP", // 1,
|
||||
"AMOTION_EVENT_ACTION_MOVE", // 2
|
||||
"AMOTION_EVENT_ACTION_CANCEL", // 3
|
||||
"AMOTION_EVENT_ACTION_OUTSIDE", // 4
|
||||
"AMOTION_EVENT_ACTION_POINTER_DOWN", // 5
|
||||
"AMOTION_EVENT_ACTION_POINTER_UP", // 6
|
||||
"AMOTION_EVENT_ACTION_HOVER_MOVE", // 7
|
||||
"AMOTION_EVENT_ACTION_SCROLL", // 8
|
||||
"AMOTION_EVENT_ACTION_HOVER_ENTER", // 9
|
||||
"AMOTION_EVENT_ACTION_HOVER_EXIT", // 10
|
||||
"AMOTION_EVENT_ACTION_BUTTON_PRESS", // 11
|
||||
"AMOTION_EVENT_ACTION_BUTTON_RELEASE" // 12 _
|
||||
};
|
||||
} // namespace paddleboat
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/input.h>
|
||||
#include <jni.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "GameController.h"
|
||||
#include "ThreadUtil.h"
|
||||
|
||||
namespace paddleboat {
|
||||
|
||||
class GameControllerManager {
|
||||
private:
|
||||
// Allows construction with std::unique_ptr from a static method, but
|
||||
// disallows construction outside of the class since no one else can
|
||||
// construct a ConstructorTag
|
||||
struct ConstructorTag {};
|
||||
|
||||
static constexpr int32_t MAX_MOUSE_DEVICES = 2;
|
||||
static constexpr int32_t INVALID_MOUSE_ID = -1;
|
||||
static constexpr int32_t MAX_REMAP_TABLE_SIZE = 256;
|
||||
|
||||
// Assuming update is getting called at 60Hz, wait one minute in between
|
||||
// checking battery status
|
||||
static constexpr int32_t BATTERY_REFRESH_WAIT = 60 * 60;
|
||||
|
||||
public:
|
||||
GameControllerManager(JNIEnv *env, jobject jcontext, ConstructorTag);
|
||||
|
||||
~GameControllerManager();
|
||||
|
||||
static inline int32_t getRemapTableSize() { return MAX_REMAP_TABLE_SIZE; }
|
||||
|
||||
static Paddleboat_ErrorCode init(JNIEnv *env, jobject jcontext);
|
||||
|
||||
static void destroyInstance(JNIEnv *env);
|
||||
|
||||
static bool isInitialized();
|
||||
|
||||
// Get/Set whether AKEYCODE_BACK is 'eaten' or allowed to pass through to
|
||||
// the system This can be used to block the OS backing out of the game, or
|
||||
// allowing it if the game is in an appropriate state (i.e. the title
|
||||
// screen)
|
||||
static bool getBackButtonConsumed();
|
||||
|
||||
static void setBackButtonConsumed(bool consumed);
|
||||
|
||||
static Paddleboat_ErrorCode setControllerLight(
|
||||
const int32_t controllerIndex, const Paddleboat_LightType lightType,
|
||||
const uint32_t lightData, JNIEnv *env);
|
||||
|
||||
static void setControllerStatusCallback(
|
||||
Paddleboat_ControllerStatusCallback statusCallback, void *userData);
|
||||
|
||||
static void setMotionDataCallback(
|
||||
Paddleboat_MotionDataCallback motionDataCallback, void *userData);
|
||||
|
||||
static void setMouseStatusCallback(
|
||||
Paddleboat_MouseStatusCallback statusCallback, void *userData);
|
||||
|
||||
static void onStop(JNIEnv *env);
|
||||
|
||||
static void onStart(JNIEnv *env);
|
||||
|
||||
static void update(JNIEnv *env);
|
||||
|
||||
static Paddleboat_ErrorCode getControllerData(
|
||||
const int32_t controllerIndex,
|
||||
Paddleboat_Controller_Data *controllerData);
|
||||
|
||||
static Paddleboat_ErrorCode getControllerInfo(
|
||||
const int32_t controllerIndex, Paddleboat_Controller_Info *deviceInfo);
|
||||
|
||||
static Paddleboat_ErrorCode getControllerName(const int32_t controllerIndex,
|
||||
const size_t bufferSize,
|
||||
char *controllerName);
|
||||
|
||||
static Paddleboat_ControllerStatus getControllerStatus(
|
||||
const int32_t controllerIndex);
|
||||
|
||||
static Paddleboat_ErrorCode setControllerVibrationData(
|
||||
const int32_t controllerIndex,
|
||||
const Paddleboat_Vibration_Data *vibrationData, JNIEnv *env);
|
||||
|
||||
static Paddleboat_ErrorCode getMouseData(Paddleboat_Mouse_Data *mouseData);
|
||||
|
||||
static Paddleboat_MouseStatus getMouseStatus();
|
||||
|
||||
static int32_t processInputEvent(const AInputEvent *event);
|
||||
|
||||
static int32_t processGameActivityKeyInputEvent(const void *event,
|
||||
const size_t eventSize);
|
||||
|
||||
static int32_t processGameActivityMotionInputEvent(const void *event,
|
||||
const size_t eventSize);
|
||||
|
||||
static uint64_t getActiveAxisMask();
|
||||
|
||||
static void addControllerRemapData(
|
||||
const Paddleboat_Remap_Addition_Mode addMode,
|
||||
const int32_t remapTableEntryCount,
|
||||
const Paddleboat_Controller_Mapping_Data *mappingData);
|
||||
|
||||
static int32_t getControllerRemapTableData(
|
||||
const int32_t destRemapTableEntryCount,
|
||||
Paddleboat_Controller_Mapping_Data *mappingData);
|
||||
|
||||
// Called from the JNI bridge functions
|
||||
static GameControllerDeviceInfo *onConnection();
|
||||
|
||||
static void onDisconnection(const int32_t deviceId);
|
||||
|
||||
static void onMotionData(const int32_t deviceId, const int32_t motionType,
|
||||
const uint64_t timestamp, const float dataX,
|
||||
const float dataY, const float dataZ);
|
||||
|
||||
static void onMouseConnection(const int32_t deviceId);
|
||||
|
||||
static void onMouseDisconnection(const int32_t deviceId);
|
||||
|
||||
static jclass getGameControllerClass();
|
||||
|
||||
static jobject getGameControllerObject();
|
||||
|
||||
// device debug helper function
|
||||
static int32_t getLastKeycode();
|
||||
|
||||
private:
|
||||
static GameControllerManager *getInstance();
|
||||
|
||||
Paddleboat_ErrorCode initMethods(JNIEnv *env);
|
||||
|
||||
bool isLightTypeSupported(const Paddleboat_Controller_Info &controllerInfo,
|
||||
const Paddleboat_LightType lightType);
|
||||
|
||||
int32_t processControllerKeyEvent(const AInputEvent *event,
|
||||
GameController &gameController);
|
||||
|
||||
int32_t processControllerGameActivityKeyEvent(
|
||||
const Paddleboat_GameActivityKeyEvent *event, const size_t eventSize,
|
||||
GameController &gameController);
|
||||
|
||||
int32_t processMouseEvent(const AInputEvent *event);
|
||||
|
||||
int32_t processGameActivityMouseEvent(const void *event,
|
||||
const size_t eventSize,
|
||||
const int32_t eventDeviceId);
|
||||
|
||||
void rescanVirtualMouseControllers();
|
||||
|
||||
void updateBattery(JNIEnv *env);
|
||||
|
||||
void updateMouseDataTimestamp();
|
||||
|
||||
void releaseGlobals(JNIEnv *env);
|
||||
|
||||
const Paddleboat_Controller_Mapping_Data *getMapForController(
|
||||
const GameController &gameController);
|
||||
|
||||
bool mInitialized = false;
|
||||
bool mGCMClassInitialized = false;
|
||||
bool mBackButtonConsumed = true;
|
||||
bool mMotionEventReporting = false;
|
||||
|
||||
int32_t mApiLevel = 16;
|
||||
int32_t mBatteryWait = BATTERY_REFRESH_WAIT;
|
||||
jobject mContext = NULL;
|
||||
jclass mGameControllerClass = NULL;
|
||||
jobject mGameControllerObject = NULL;
|
||||
jmethodID mInitMethodId = NULL;
|
||||
jmethodID mGetApiLevelMethodId = NULL;
|
||||
jmethodID mGetBatteryLevelMethodId = NULL;
|
||||
jmethodID mGetBatteryStatusMethodId = NULL;
|
||||
jmethodID mSetLightMethodId = NULL;
|
||||
jmethodID mSetNativeReadyMethodId = NULL;
|
||||
jmethodID mSetReportMotionEventsMethodId = NULL;
|
||||
jmethodID mSetVibrationMethodId = NULL;
|
||||
|
||||
uint64_t mActiveAxisMask = 0;
|
||||
|
||||
int32_t mRemapEntryCount = 0;
|
||||
Paddleboat_Controller_Mapping_Data mMappingTable[MAX_REMAP_TABLE_SIZE];
|
||||
|
||||
Paddleboat_MotionDataCallback mMotionDataCallback = nullptr;
|
||||
void *mMotionDataCallbackUserData = nullptr;
|
||||
|
||||
GameController mGameControllers[PADDLEBOAT_MAX_CONTROLLERS];
|
||||
Paddleboat_ControllerStatusCallback mStatusCallback = nullptr;
|
||||
void *mStatusCallbackUserData = nullptr;
|
||||
// device debug helper
|
||||
int32_t mLastKeyEventKeyCode = 0;
|
||||
|
||||
Paddleboat_MouseStatus mMouseStatus = PADDLEBOAT_MOUSE_NONE;
|
||||
int32_t mMouseDeviceIds[MAX_MOUSE_DEVICES] = {INVALID_MOUSE_ID,
|
||||
INVALID_MOUSE_ID};
|
||||
int32_t mMouseControllerIndex = INVALID_MOUSE_ID;
|
||||
Paddleboat_Mouse_Data mMouseData;
|
||||
Paddleboat_MouseStatusCallback mMouseCallback = nullptr;
|
||||
void *mMouseCallbackUserData = nullptr;
|
||||
|
||||
std::mutex mUpdateMutex;
|
||||
static std::mutex sInstanceMutex;
|
||||
static std::unique_ptr<GameControllerManager> sInstance
|
||||
GUARDED_BY(sInstanceMutex);
|
||||
};
|
||||
} // namespace paddleboat
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "GameControllerMappingUtils.h"
|
||||
|
||||
#include "GameControllerManager.h"
|
||||
|
||||
namespace paddleboat {
|
||||
MappingTableSearch::MappingTableSearch()
|
||||
: mappingRoot(nullptr),
|
||||
vendorId(0),
|
||||
productId(0),
|
||||
minApi(0),
|
||||
maxApi(0),
|
||||
tableIndex(0),
|
||||
mapEntryCount(0),
|
||||
tableEntryCount(0),
|
||||
tableMaxEntryCount(GameControllerManager::getRemapTableSize()) {}
|
||||
|
||||
MappingTableSearch::MappingTableSearch(
|
||||
Paddleboat_Controller_Mapping_Data *mapRoot, int32_t entryCount)
|
||||
: mappingRoot(mapRoot),
|
||||
vendorId(0),
|
||||
productId(0),
|
||||
minApi(0),
|
||||
maxApi(0),
|
||||
tableIndex(0),
|
||||
mapEntryCount(0),
|
||||
tableEntryCount(entryCount),
|
||||
tableMaxEntryCount(GameControllerManager::getRemapTableSize()) {}
|
||||
|
||||
void MappingTableSearch::initSearchParameters(const int32_t newVendorId,
|
||||
const int32_t newProductId,
|
||||
const int32_t newMinApi,
|
||||
const int32_t newMaxApi) {
|
||||
vendorId = newVendorId;
|
||||
productId = newProductId;
|
||||
minApi = newMinApi;
|
||||
maxApi = newMaxApi;
|
||||
tableIndex = 0;
|
||||
}
|
||||
|
||||
bool GameControllerMappingUtils::findMatchingMapEntry(
|
||||
MappingTableSearch *searchEntry) {
|
||||
int32_t currentIndex = 0;
|
||||
|
||||
// Starting out with a linear search. Updating the map table is something
|
||||
// that should only ever be done once at startup, if it actually takes an
|
||||
// appreciable time to execute when working with a big remap dictionary,
|
||||
// this is low-hanging fruit to optimize.
|
||||
const Paddleboat_Controller_Mapping_Data *mapRoot =
|
||||
searchEntry->mappingRoot;
|
||||
while (currentIndex < searchEntry->tableEntryCount) {
|
||||
const Paddleboat_Controller_Mapping_Data &mapEntry =
|
||||
mapRoot[currentIndex];
|
||||
if (mapEntry.vendorId > searchEntry->vendorId) {
|
||||
// Passed by the search vendorId value, so we don't already exist in
|
||||
// the table, set the current index as the insert point and bail
|
||||
searchEntry->tableIndex = currentIndex;
|
||||
return false;
|
||||
} else if (searchEntry->vendorId == mapEntry.vendorId) {
|
||||
if (mapEntry.productId > searchEntry->productId) {
|
||||
// Passed by the search productId value, so we don't already
|
||||
// exist in the table, set the current index as the insert point
|
||||
// and bail
|
||||
searchEntry->tableIndex = currentIndex;
|
||||
return false;
|
||||
} else if (searchEntry->productId == mapEntry.productId) {
|
||||
// Any overlap of the min/max API range is treated as matching
|
||||
// an existing entry
|
||||
if ((searchEntry->minApi >= mapEntry.minimumEffectiveApiLevel &&
|
||||
searchEntry->minApi <=
|
||||
mapEntry.maximumEffectiveApiLevel) ||
|
||||
(searchEntry->minApi >= mapEntry.minimumEffectiveApiLevel &&
|
||||
mapEntry.maximumEffectiveApiLevel == 0)) {
|
||||
searchEntry->tableIndex = currentIndex;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
++currentIndex;
|
||||
}
|
||||
searchEntry->tableIndex = currentIndex;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GameControllerMappingUtils::insertMapEntry(
|
||||
const Paddleboat_Controller_Mapping_Data *mappingData,
|
||||
MappingTableSearch *searchEntry) {
|
||||
bool insertSuccess = false;
|
||||
// Verify there is room in the table for another entry
|
||||
if (searchEntry->tableEntryCount < searchEntry->tableMaxEntryCount &&
|
||||
searchEntry->tableIndex < searchEntry->tableMaxEntryCount) {
|
||||
// Empty table, or inserting at the end, no relocation of existing data
|
||||
// required, otherwise shift existing data down starting at the insert
|
||||
// index.
|
||||
if (!(searchEntry->tableEntryCount == 0 ||
|
||||
searchEntry->tableIndex == searchEntry->tableEntryCount)) {
|
||||
const size_t copySize =
|
||||
(searchEntry->tableEntryCount - searchEntry->tableIndex) *
|
||||
sizeof(Paddleboat_Controller_Mapping_Data);
|
||||
memmove(&searchEntry->mappingRoot[searchEntry->tableIndex + 1],
|
||||
&searchEntry->mappingRoot[searchEntry->tableIndex],
|
||||
copySize);
|
||||
}
|
||||
// Insert the new data
|
||||
memcpy(&searchEntry->mappingRoot[searchEntry->tableIndex], mappingData,
|
||||
sizeof(Paddleboat_Controller_Mapping_Data));
|
||||
insertSuccess = true;
|
||||
}
|
||||
return insertSuccess;
|
||||
}
|
||||
|
||||
const Paddleboat_Controller_Mapping_Data *
|
||||
GameControllerMappingUtils::validateMapTable(
|
||||
const Paddleboat_Controller_Mapping_Data *mappingRoot,
|
||||
const int32_t tableEntryCount) {
|
||||
// The map table is always assumed to be sorted by increasing vendorId. Each
|
||||
// sequence of entries with the same vendorId are sorted by increasing
|
||||
// productId. Multiple entries with the same vendorId/productId range are
|
||||
// sorted by increasing min/max API ranges. vendorId
|
||||
// productId
|
||||
// minApi
|
||||
int32_t currentIndex = 0;
|
||||
int32_t previousVendorId = -1;
|
||||
while (currentIndex < tableEntryCount) {
|
||||
if (mappingRoot[currentIndex].vendorId < previousVendorId) {
|
||||
// failure in vendorId order, return the offending entry
|
||||
return &mappingRoot[currentIndex];
|
||||
}
|
||||
|
||||
int32_t previousProductId = mappingRoot[currentIndex].productId;
|
||||
int32_t previousMinApi =
|
||||
mappingRoot[currentIndex].minimumEffectiveApiLevel;
|
||||
int32_t previousMaxApi =
|
||||
mappingRoot[currentIndex].maximumEffectiveApiLevel;
|
||||
previousVendorId = mappingRoot[currentIndex++].vendorId;
|
||||
|
||||
while (currentIndex < tableEntryCount &&
|
||||
mappingRoot[currentIndex].vendorId == previousVendorId) {
|
||||
while (currentIndex < tableEntryCount &&
|
||||
mappingRoot[currentIndex].productId == previousProductId) {
|
||||
if (mappingRoot[currentIndex].minimumEffectiveApiLevel <
|
||||
previousMinApi ||
|
||||
mappingRoot[currentIndex].minimumEffectiveApiLevel <
|
||||
previousMaxApi) {
|
||||
// failure in API order, return the offending entry
|
||||
return &mappingRoot[currentIndex];
|
||||
}
|
||||
previousMinApi =
|
||||
mappingRoot[currentIndex].minimumEffectiveApiLevel;
|
||||
previousMaxApi =
|
||||
mappingRoot[currentIndex++].maximumEffectiveApiLevel;
|
||||
}
|
||||
if (mappingRoot[currentIndex].productId < previousProductId) {
|
||||
// failure in productId order, return the offending entry
|
||||
return &mappingRoot[currentIndex];
|
||||
}
|
||||
previousProductId = mappingRoot[currentIndex++].productId;
|
||||
}
|
||||
}
|
||||
|
||||
// Validation success, return nullptr (no offending entries to return)
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace paddleboat
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#ifndef PADDLEBOAT_H
|
||||
#include "paddleboat.h"
|
||||
#endif
|
||||
|
||||
namespace paddleboat {
|
||||
class MappingTableSearch {
|
||||
public:
|
||||
MappingTableSearch();
|
||||
|
||||
MappingTableSearch(Paddleboat_Controller_Mapping_Data *mapRoot,
|
||||
int32_t entryCount);
|
||||
|
||||
void initSearchParameters(const int32_t newVendorId,
|
||||
const int32_t newProductId,
|
||||
const int32_t newMinApi, const int32_t newMaxApi);
|
||||
|
||||
Paddleboat_Controller_Mapping_Data *mappingRoot;
|
||||
int32_t vendorId;
|
||||
int32_t productId;
|
||||
int32_t minApi;
|
||||
int32_t maxApi;
|
||||
int32_t tableIndex;
|
||||
int32_t mapEntryCount;
|
||||
int32_t tableEntryCount;
|
||||
int32_t tableMaxEntryCount;
|
||||
};
|
||||
|
||||
class GameControllerMappingUtils {
|
||||
public:
|
||||
static bool findMatchingMapEntry(MappingTableSearch *searchEntry);
|
||||
|
||||
static bool insertMapEntry(
|
||||
const Paddleboat_Controller_Mapping_Data *mappingData,
|
||||
MappingTableSearch *searchEntry);
|
||||
|
||||
static const Paddleboat_Controller_Mapping_Data *validateMapTable(
|
||||
const Paddleboat_Controller_Mapping_Data *mappingRoot,
|
||||
const int32_t tableEntryCount);
|
||||
};
|
||||
} // namespace paddleboat
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "paddleboat.h"
|
||||
|
||||
namespace paddleboat {
|
||||
const Paddleboat_Controller_Mapping_Data *GetInternalControllerData();
|
||||
|
||||
int32_t GetInternalControllerDataCount();
|
||||
} // namespace paddleboat
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
||||
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
#define ALOGW_ONCE_IF(cond, ...) \
|
||||
do { \
|
||||
static bool alogw_once##__FILE__##__LINE__##__ = true; \
|
||||
if (cond && alogw_once##__FILE__##__LINE__##__) { \
|
||||
alogw_once##__FILE__##__LINE__##__ = false; \
|
||||
ALOGW(__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define ALOGV(...) \
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define ALOGV(...)
|
||||
#endif
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
// Enable thread safety attributes only with clang.
|
||||
// The attributes can be safely erased when compiling with other compilers.
|
||||
#if defined(__clang__) && (!defined(SWIG))
|
||||
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
|
||||
#else
|
||||
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
|
||||
#endif
|
||||
|
||||
#if !defined GAMESDK_THREAD_CHECKS
|
||||
#define GAMESDK_THREAD_CHECKS 1
|
||||
#endif
|
||||
|
||||
#if GAMESDK_THREAD_CHECKS
|
||||
#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
|
||||
|
||||
#define REQUIRES(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
|
||||
|
||||
#define NO_THREAD_SAFETY_ANALYSIS \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
|
||||
#else
|
||||
#define GUARDED_BY(x)
|
||||
#define REQUIRES(...)
|
||||
#define NO_THREAD_SAFETY_ANALYSIS
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "GameControllerManager.h"
|
||||
#include "paddleboat.h"
|
||||
|
||||
using namespace paddleboat;
|
||||
|
||||
extern "C" {
|
||||
|
||||
// Internal macros to track Paddleboat version, do not use directly.
|
||||
#define PADDLEBOAT_MAJOR_VERSION 1
|
||||
#define PADDLEBOAT_MINOR_VERSION 1
|
||||
#define PADDLEBOAT_BUGFIX_VERSION 0
|
||||
|
||||
#define PADDLEBOAT_PACKED_VERSION \
|
||||
((PADDLEBOAT_MAJOR_VERSION << 24) | (PADDLEBOAT_MINOR_VERSION << 16) | \
|
||||
(PADDLEBOAT_BUGFIX_VERSION))
|
||||
|
||||
#define PADDLEBOAT_VERSION_CONCAT_NX(PREFIX, MAJOR, MINOR, BUGFIX) \
|
||||
PREFIX##_##MAJOR##_##MINOR##_##BUGFIX
|
||||
#define PADDLEBOAT_VERSION_CONCAT(PREFIX, MAJOR, MINOR, BUGFIX) \
|
||||
PADDLEBOAT_VERSION_CONCAT_NX(PREFIX, MAJOR, MINOR, BUGFIX)
|
||||
#define PADDLEBOAT_VERSION_SYMBOL \
|
||||
PADDLEBOAT_VERSION_CONCAT(PADDLEBOAT_version, PADDLEBOAT_MAJOR_VERSION, \
|
||||
PADDLEBOAT_MINOR_VERSION, \
|
||||
PADDLEBOAT_BUGFIX_VERSION)
|
||||
|
||||
void PADDLEBOAT_VERSION_SYMBOL();
|
||||
|
||||
Paddleboat_ErrorCode Paddleboat_init(JNIEnv *env, jobject jcontext) {
|
||||
PADDLEBOAT_VERSION_SYMBOL();
|
||||
Paddleboat_ErrorCode errorCode = GameControllerManager::init(env, jcontext);
|
||||
if (errorCode == PADDLEBOAT_NO_ERROR) {
|
||||
GameControllerManager::update(env);
|
||||
}
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
bool Paddleboat_isInitialized() {
|
||||
return GameControllerManager::isInitialized();
|
||||
}
|
||||
|
||||
void Paddleboat_destroy(JNIEnv *env) {
|
||||
GameControllerManager::destroyInstance(env);
|
||||
}
|
||||
|
||||
void Paddleboat_onStop(JNIEnv *env) { GameControllerManager::onStop(env); }
|
||||
|
||||
void Paddleboat_onStart(JNIEnv *env) { GameControllerManager::onStart(env); }
|
||||
|
||||
int32_t Paddleboat_processInputEvent(const AInputEvent *event) {
|
||||
return GameControllerManager::processInputEvent(event);
|
||||
}
|
||||
|
||||
int32_t Paddleboat_processGameActivityKeyInputEvent(const void *event,
|
||||
const size_t eventSize) {
|
||||
return GameControllerManager::processGameActivityKeyInputEvent(event,
|
||||
eventSize);
|
||||
}
|
||||
|
||||
int32_t Paddleboat_processGameActivityMotionInputEvent(const void *event,
|
||||
const size_t eventSize) {
|
||||
return GameControllerManager::processGameActivityMotionInputEvent(
|
||||
event, eventSize);
|
||||
}
|
||||
|
||||
uint64_t Paddleboat_getActiveAxisMask() {
|
||||
return GameControllerManager::getActiveAxisMask();
|
||||
}
|
||||
|
||||
bool Paddleboat_getBackButtonConsumed() {
|
||||
return GameControllerManager::getBackButtonConsumed();
|
||||
}
|
||||
|
||||
void Paddleboat_setBackButtonConsumed(bool consumeBackButton) {
|
||||
GameControllerManager::setBackButtonConsumed(consumeBackButton);
|
||||
}
|
||||
|
||||
void Paddleboat_setControllerStatusCallback(
|
||||
Paddleboat_ControllerStatusCallback statusCallback, void *userData) {
|
||||
GameControllerManager::setControllerStatusCallback(statusCallback,
|
||||
userData);
|
||||
}
|
||||
|
||||
void Paddleboat_setMotionDataCallback(
|
||||
Paddleboat_MotionDataCallback motionDataCallback, void *userData) {
|
||||
GameControllerManager::setMotionDataCallback(motionDataCallback, userData);
|
||||
}
|
||||
|
||||
void Paddleboat_setMouseStatusCallback(
|
||||
Paddleboat_MouseStatusCallback statusCallback, void *userData) {
|
||||
GameControllerManager::setMouseStatusCallback(statusCallback, userData);
|
||||
}
|
||||
|
||||
Paddleboat_ErrorCode Paddleboat_getControllerData(
|
||||
const int32_t controllerIndex, Paddleboat_Controller_Data *controllerData) {
|
||||
return GameControllerManager::getControllerData(controllerIndex,
|
||||
controllerData);
|
||||
}
|
||||
|
||||
Paddleboat_ErrorCode Paddleboat_getControllerInfo(
|
||||
const int32_t controllerIndex, Paddleboat_Controller_Info *controllerInfo) {
|
||||
return GameControllerManager::getControllerInfo(controllerIndex,
|
||||
controllerInfo);
|
||||
}
|
||||
|
||||
Paddleboat_ErrorCode Paddleboat_getControllerName(const int32_t controllerIndex,
|
||||
const size_t bufferSize,
|
||||
char *controllerName) {
|
||||
return GameControllerManager::getControllerName(controllerIndex, bufferSize,
|
||||
controllerName);
|
||||
}
|
||||
|
||||
Paddleboat_ControllerStatus Paddleboat_getControllerStatus(
|
||||
const int32_t controllerIndex) {
|
||||
return GameControllerManager::getControllerStatus(controllerIndex);
|
||||
}
|
||||
|
||||
Paddleboat_ErrorCode Paddleboat_setControllerLight(
|
||||
const int32_t controllerIndex, const Paddleboat_LightType lightType,
|
||||
const uint32_t lightData, JNIEnv *env) {
|
||||
return GameControllerManager::setControllerLight(controllerIndex, lightType,
|
||||
lightData, env);
|
||||
}
|
||||
|
||||
Paddleboat_ErrorCode Paddleboat_setControllerVibrationData(
|
||||
const int32_t controllerIndex,
|
||||
const Paddleboat_Vibration_Data *vibrationData, JNIEnv *env) {
|
||||
return GameControllerManager::setControllerVibrationData(
|
||||
controllerIndex, vibrationData, env);
|
||||
}
|
||||
|
||||
Paddleboat_ErrorCode Paddleboat_getMouseData(Paddleboat_Mouse_Data *mouseData) {
|
||||
return GameControllerManager::getMouseData(mouseData);
|
||||
}
|
||||
|
||||
Paddleboat_MouseStatus Paddleboat_getMouseStatus() {
|
||||
return GameControllerManager::getMouseStatus();
|
||||
}
|
||||
|
||||
void Paddleboat_addControllerRemapData(
|
||||
const Paddleboat_Remap_Addition_Mode addMode,
|
||||
const int32_t remapTableEntryCount,
|
||||
const Paddleboat_Controller_Mapping_Data *mappingData) {
|
||||
GameControllerManager::addControllerRemapData(addMode, remapTableEntryCount,
|
||||
mappingData);
|
||||
}
|
||||
|
||||
int32_t Paddleboat_getControllerRemapTableData(
|
||||
const int32_t destRemapTableEntryCount,
|
||||
Paddleboat_Controller_Mapping_Data *mappingData) {
|
||||
return GameControllerManager::getControllerRemapTableData(
|
||||
destRemapTableEntryCount, mappingData);
|
||||
}
|
||||
|
||||
void Paddleboat_update(JNIEnv *env) { GameControllerManager::update(env); }
|
||||
|
||||
int32_t Paddleboat_getLastKeycode() {
|
||||
return GameControllerManager::getLastKeycode();
|
||||
}
|
||||
|
||||
void PADDLEBOAT_VERSION_SYMBOL() {
|
||||
// Intentionally empty: this function is used to ensure that the proper
|
||||
// version of the library is linked against the proper headers.
|
||||
// In case of mismatch, a linker error will be triggered because of an
|
||||
// undefined symbol, as the name of the function depends on the version.
|
||||
}
|
||||
} // extern "C" {
|
||||
Loading…
Reference in New Issue