#include <android_native_app_glue.h>
#include <android/window.h>
#include <jni.h>
#include <EGL/egl.h>
typedef struct {
struct android_app *app; struct android_poll_source *source; bool appEnabled; bool contextRebindRequired;
EGLDisplay device; EGLSurface surface; EGLContext context; EGLConfig config; } PlatformData;
extern CoreData CORE; extern bool isGpuReady; static PlatformData platform = { 0 };
#define KEYCODE_MAP_SIZE 162
static const KeyboardKey mapKeycode[KEYCODE_MAP_SIZE] = {
KEY_NULL, 0, 0, 0, KEY_BACK, 0, 0, KEY_ZERO, KEY_ONE, KEY_TWO, KEY_THREE, KEY_FOUR, KEY_FIVE, KEY_SIX, KEY_SEVEN, KEY_EIGHT, KEY_NINE, 0, 0, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, 0, KEY_VOLUME_UP, KEY_VOLUME_DOWN, 0, 0, 0, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_COMMA, KEY_PERIOD, KEY_LEFT_ALT, KEY_RIGHT_ALT, KEY_LEFT_SHIFT, KEY_RIGHT_SHIFT, KEY_TAB, KEY_SPACE, 0, 0, 0, KEY_ENTER, KEY_BACKSPACE, KEY_GRAVE, KEY_MINUS, KEY_EQUAL, KEY_LEFT_BRACKET, KEY_RIGHT_BRACKET, KEY_BACKSLASH, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_SLASH, 0, 0, 0, 0, 0, KEY_MENU, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PAGE_UP, KEY_PAGE_DOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_ESCAPE, KEY_DELETE, KEY_LEFT_CONTROL, KEY_RIGHT_CONTROL, KEY_CAPS_LOCK, KEY_SCROLL_LOCK, KEY_LEFT_SUPER, KEY_RIGHT_SUPER, 0, KEY_PRINT_SCREEN, KEY_PAUSE, KEY_HOME, KEY_END, KEY_INSERT, 0, 0, 0, 0, 0, 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_NUM_LOCK, KEY_KP_0, KEY_KP_1, KEY_KP_2, KEY_KP_3, KEY_KP_4, KEY_KP_5, KEY_KP_6, KEY_KP_7, KEY_KP_8, KEY_KP_9, KEY_KP_DIVIDE, KEY_KP_MULTIPLY, KEY_KP_SUBTRACT, KEY_KP_ADD, KEY_KP_DECIMAL, 0, KEY_KP_ENTER, KEY_KP_EQUAL };
int InitPlatform(void); void ClosePlatform(void);
static void AndroidCommandCallback(struct android_app *app, int32_t cmd); static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); static GamepadButton AndroidTranslateGamepadButton(int button);
extern int main(int argc, char *argv[]);
void android_main(struct android_app *app)
{
char arg0[] = "raylib"; platform.app = app;
(void)main(1, (char *[]) { arg0, NULL });
ANativeActivity_finish(app->activity);
int pollResult = 0;
int pollEvents = 0;
while (!app->destroyRequested)
{
while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, (void **)&platform.source)) > ALOOPER_POLL_TIMEOUT)
{
if (platform.source != NULL) platform.source->process(app, platform.source);
}
}
}
struct android_app *GetAndroidApp(void)
{
return platform.app;
}
bool WindowShouldClose(void)
{
if (CORE.Window.ready) return CORE.Window.shouldClose;
else return true;
}
void ToggleFullscreen(void)
{
TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform");
}
void ToggleBorderlessWindowed(void)
{
TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform");
}
void MaximizeWindow(void)
{
TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform");
}
void MinimizeWindow(void)
{
TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
}
void RestoreWindow(void)
{
TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform");
}
void SetWindowState(unsigned int flags)
{
TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform");
}
void ClearWindowState(unsigned int flags)
{
TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform");
}
void SetWindowIcon(Image image)
{
TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform");
}
void SetWindowIcons(Image *images, int count)
{
TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
}
void SetWindowTitle(const char *title)
{
CORE.Window.title = title;
}
void SetWindowPosition(int x, int y)
{
TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform");
}
void SetWindowMonitor(int monitor)
{
TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform");
}
void SetWindowMinSize(int width, int height)
{
CORE.Window.screenMin.width = width;
CORE.Window.screenMin.height = height;
}
void SetWindowMaxSize(int width, int height)
{
CORE.Window.screenMax.width = width;
CORE.Window.screenMax.height = height;
}
void SetWindowSize(int width, int height)
{
TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform");
}
void SetWindowOpacity(float opacity)
{
TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform");
}
void SetWindowFocused(void)
{
TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform");
}
void *GetWindowHandle(void)
{
TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform");
return NULL;
}
int GetMonitorCount(void)
{
TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform");
return 1;
}
int GetCurrentMonitor(void)
{
TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
return 0;
}
Vector2 GetMonitorPosition(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform");
return (Vector2){ 0, 0 };
}
int GetMonitorWidth(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform");
return 0;
}
int GetMonitorHeight(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform");
return 0;
}
int GetMonitorPhysicalWidth(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform");
return 0;
}
int GetMonitorPhysicalHeight(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform");
return 0;
}
int GetMonitorRefreshRate(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform");
return 0;
}
const char *GetMonitorName(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform");
return "";
}
Vector2 GetWindowPosition(void)
{
TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform");
return (Vector2){ 0, 0 };
}
Vector2 GetWindowScaleDPI(void)
{
TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
return (Vector2){ 1.0f, 1.0f };
}
void SetClipboardText(const char *text)
{
TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform");
}
const char *GetClipboardText(void)
{
TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform");
return NULL;
}
Image GetClipboardImage(void)
{
Image image = { 0 };
TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform");
return image;
}
void ShowCursor(void)
{
CORE.Input.Mouse.cursorHidden = false;
}
void HideCursor(void)
{
CORE.Input.Mouse.cursorHidden = true;
}
void EnableCursor(void)
{
SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
CORE.Input.Mouse.cursorHidden = false;
}
void DisableCursor(void)
{
SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
CORE.Input.Mouse.cursorHidden = true;
}
void SwapScreenBuffer(void)
{
eglSwapBuffers(platform.device, platform.surface);
}
double GetTime(void)
{
double time = 0.0;
struct timespec ts = { 0 };
clock_gettime(CLOCK_MONOTONIC, &ts);
unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
time = (double)(nanoSeconds - CORE.Time.base)*1e-9;
return time;
}
void OpenURL(const char *url)
{
if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
else
{
JNIEnv *env = NULL;
JavaVM *vm = platform.app->activity->vm;
(*vm)->AttachCurrentThread(vm, &env, NULL);
jstring urlString = (*env)->NewStringUTF(env, url);
jclass uriClass = (*env)->FindClass(env, "android/net/Uri");
jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;");
jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString);
jclass intentClass = (*env)->FindClass(env, "android/content/Intent");
jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;");
jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId);
jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "<init>", "(Ljava/lang/String;Landroid/net/Uri;)V");
jobject intent = (*env)->AllocObject(env, intentClass);
(*env)->CallVoidMethod(env, intent, newIntent, actionView, uri);
jclass activityClass = (*env)->FindClass(env, "android/app/Activity");
jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V");
(*env)->CallVoidMethod(env, platform.app->activity->clazz, startActivity, intent);
(*vm)->DetachCurrentThread(vm);
}
}
int SetGamepadMappings(const char *mappings)
{
TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform");
return 0;
}
void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
{
TRACELOG(LOG_WARNING, "GamepadSetVibration() not implemented on target platform");
}
void SetMousePosition(int x, int y)
{
CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
}
void SetMouseCursor(int cursor)
{
TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
}
const char *GetKeyName(int key)
{
TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
return "";
}
void PollInputEvents(void)
{
#if defined(SUPPORT_GESTURES_SYSTEM)
UpdateGestures();
#endif
CORE.Input.Keyboard.keyPressedQueueCount = 0;
CORE.Input.Keyboard.charPressedQueueCount = 0;
for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
CORE.Input.Gamepad.lastButtonPressed = 0;
for (int i = 0; i < MAX_GAMEPADS; i++)
{
if (CORE.Input.Gamepad.ready[i]) {
for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++)
CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
}
}
for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
for (int i = 0; i < 260; i++)
{
CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
}
int pollResult = 0;
int pollEvents = 0;
while ((pollResult = ALooper_pollOnce(platform.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&platform.source)) > ALOOPER_POLL_TIMEOUT)
{
if (platform.source != NULL) platform.source->process(platform.app, platform.source);
if (platform.app->destroyRequested != 0)
{
CORE.Window.shouldClose = true;
}
}
}
int InitPlatform(void)
{
CORE.Window.currentFbo.width = CORE.Window.screen.width;
CORE.Window.currentFbo.height = CORE.Window.screen.height;
ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0);
int orientation = AConfiguration_getOrientation(platform.app->config);
if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait");
else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape");
if (CORE.Window.screen.width <= CORE.Window.screen.height)
{
AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT);
TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait");
}
else
{
AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_LAND);
TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape");
}
CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
platform.app->onAppCmd = AndroidCommandCallback;
platform.app->onInputEvent = AndroidInputCallback;
InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath);
CORE.Storage.basePath = platform.app->activity->internalDataPath;
TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Initialized successfully");
int pollResult = 0;
int pollEvents = 0;
while (!CORE.Window.ready)
{
while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, (void**)&platform.source)) > ALOOPER_POLL_TIMEOUT)
{
if (platform.source != NULL) platform.source->process(platform.app, platform.source);
}
}
return 0;
}
void ClosePlatform(void)
{
if (platform.device != EGL_NO_DISPLAY)
{
eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (platform.surface != EGL_NO_SURFACE)
{
eglDestroySurface(platform.device, platform.surface);
platform.surface = EGL_NO_SURFACE;
}
if (platform.context != EGL_NO_CONTEXT)
{
eglDestroyContext(platform.device, platform.context);
platform.context = EGL_NO_CONTEXT;
}
eglTerminate(platform.device);
platform.device = EGL_NO_DISPLAY;
}
if (platform.app->destroyRequested != 0) {
CORE = (CoreData){0};
platform = (PlatformData){0};
}
}
static int InitGraphicsDevice(void)
{
CORE.Window.fullscreen = true;
CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
EGLint samples = 0;
EGLint sampleBuffer = 0;
if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
{
samples = 4;
sampleBuffer = 1;
TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
}
const EGLint framebufferAttribs[] =
{
EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_DEPTH_SIZE, 16, EGL_SAMPLE_BUFFERS, sampleBuffer, EGL_SAMPLES, samples, EGL_NONE
};
const EGLint contextAttribs[] =
{
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLint numConfigs = 0;
platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (platform.device == EGL_NO_DISPLAY)
{
TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
return -1;
}
if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
{
TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
return -1;
}
eglChooseConfig(platform.device, framebufferAttribs, &platform.config, 1, &numConfigs);
eglBindAPI(EGL_OPENGL_ES_API);
platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs);
if (platform.context == EGL_NO_CONTEXT)
{
TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
return -1;
}
EGLint displayFormat = 0;
eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
ANativeWindow_setBuffersGeometry(platform.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat);
platform.surface = eglCreateWindowSurface(platform.device, platform.config, platform.app->window, NULL);
if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE)
{
TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface");
return -1;
}
else
{
CORE.Window.render.width = CORE.Window.screen.width;
CORE.Window.render.height = CORE.Window.screen.height;
CORE.Window.currentFbo.width = CORE.Window.render.width;
CORE.Window.currentFbo.height = CORE.Window.render.height;
TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
}
rlLoadExtensions(eglGetProcAddress);
CORE.Window.ready = true;
if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
return 0;
}
static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
{
switch (cmd)
{
case APP_CMD_START:
{
} break;
case APP_CMD_RESUME: break;
case APP_CMD_INIT_WINDOW:
{
if (app->window != NULL)
{
if (platform.contextRebindRequired)
{
EGLint displayFormat = 0;
eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
ANativeWindow_setBuffersGeometry(app->window,
CORE.Window.render.width + CORE.Window.renderOffset.x,
CORE.Window.render.height + CORE.Window.renderOffset.y,
displayFormat);
platform.surface = eglCreateWindowSurface(platform.device, platform.config, app->window, NULL);
eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context);
platform.contextRebindRequired = false;
}
else
{
CORE.Window.display.width = ANativeWindow_getWidth(platform.app->window);
CORE.Window.display.height = ANativeWindow_getHeight(platform.app->window);
InitGraphicsDevice();
rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
isGpuReady = true;
SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
InitTimer();
#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
LoadFontDefault();
#if defined(SUPPORT_MODULE_RSHAPES)
Rectangle rec = GetFontDefault().recs[95];
if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
{
SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 });
}
else
{
SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 });
}
#endif
#else
#if defined(SUPPORT_MODULE_RSHAPES)
Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); #endif
#endif
SetRandomSeed((unsigned int)time(NULL));
}
}
} break;
case APP_CMD_GAINED_FOCUS:
{
platform.appEnabled = true;
CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
} break;
case APP_CMD_PAUSE: break;
case APP_CMD_LOST_FOCUS:
{
platform.appEnabled = false;
CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
} break;
case APP_CMD_TERM_WINDOW:
{
if (platform.device != EGL_NO_DISPLAY)
{
eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (platform.surface != EGL_NO_SURFACE)
{
eglDestroySurface(platform.device, platform.surface);
platform.surface = EGL_NO_SURFACE;
}
platform.contextRebindRequired = true;
}
} break;
case APP_CMD_SAVE_STATE: break;
case APP_CMD_STOP: break;
case APP_CMD_DESTROY: break;
case APP_CMD_CONFIG_CHANGED:
{
} break;
default: break;
}
}
static GamepadButton AndroidTranslateGamepadButton(int button)
{
switch (button)
{
case AKEYCODE_BUTTON_A: return GAMEPAD_BUTTON_RIGHT_FACE_DOWN;
case AKEYCODE_BUTTON_B: return GAMEPAD_BUTTON_RIGHT_FACE_RIGHT;
case AKEYCODE_BUTTON_X: return GAMEPAD_BUTTON_RIGHT_FACE_LEFT;
case AKEYCODE_BUTTON_Y: return GAMEPAD_BUTTON_RIGHT_FACE_UP;
case AKEYCODE_BUTTON_L1: return GAMEPAD_BUTTON_LEFT_TRIGGER_1;
case AKEYCODE_BUTTON_R1: return GAMEPAD_BUTTON_RIGHT_TRIGGER_1;
case AKEYCODE_BUTTON_L2: return GAMEPAD_BUTTON_LEFT_TRIGGER_2;
case AKEYCODE_BUTTON_R2: return GAMEPAD_BUTTON_RIGHT_TRIGGER_2;
case AKEYCODE_BUTTON_THUMBL: return GAMEPAD_BUTTON_LEFT_THUMB;
case AKEYCODE_BUTTON_THUMBR: return GAMEPAD_BUTTON_RIGHT_THUMB;
case AKEYCODE_BUTTON_START: return GAMEPAD_BUTTON_MIDDLE_RIGHT;
case AKEYCODE_BUTTON_SELECT: return GAMEPAD_BUTTON_MIDDLE_LEFT;
case AKEYCODE_BUTTON_MODE: return GAMEPAD_BUTTON_MIDDLE;
case AKEYCODE_DPAD_DOWN: return GAMEPAD_BUTTON_LEFT_FACE_DOWN;
case AKEYCODE_DPAD_RIGHT: return GAMEPAD_BUTTON_LEFT_FACE_RIGHT;
case AKEYCODE_DPAD_LEFT: return GAMEPAD_BUTTON_LEFT_FACE_LEFT;
case AKEYCODE_DPAD_UP: return GAMEPAD_BUTTON_LEFT_FACE_UP;
default: return GAMEPAD_BUTTON_UNKNOWN;
}
}
static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
{
int type = AInputEvent_getType(event);
int source = AInputEvent_getSource(event);
if (type == AINPUT_EVENT_TYPE_MOTION)
{
if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD))
{
CORE.Input.Gamepad.ready[0] = true;
CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_X] = AMotionEvent_getAxisValue(
event, AMOTION_EVENT_AXIS_X, 0);
CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_Y] = AMotionEvent_getAxisValue(
event, AMOTION_EVENT_AXIS_Y, 0);
CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_X] = AMotionEvent_getAxisValue(
event, AMOTION_EVENT_AXIS_Z, 0);
CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_Y] = AMotionEvent_getAxisValue(
event, AMOTION_EVENT_AXIS_RZ, 0);
CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_TRIGGER] = AMotionEvent_getAxisValue(
event, AMOTION_EVENT_AXIS_BRAKE, 0)*2.0f - 1.0f;
CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_TRIGGER] = AMotionEvent_getAxisValue(
event, AMOTION_EVENT_AXIS_GAS, 0)*2.0f - 1.0f;
float dpadX = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, 0);
float dpadY = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_Y, 0);
if (dpadX == 1.0f)
{
CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 1;
CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0;
}
else if (dpadX == -1.0f)
{
CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0;
CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 1;
}
else
{
CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0;
CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0;
}
if (dpadY == 1.0f)
{
CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 1;
CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0;
}
else if (dpadY == -1.0f)
{
CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0;
CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 1;
}
else
{
CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0;
CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0;
}
return 1; }
}
else if (type == AINPUT_EVENT_TYPE_KEY)
{
int32_t keycode = AKeyEvent_getKeyCode(event);
if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD))
{
CORE.Input.Gamepad.ready[0] = true;
GamepadButton button = AndroidTranslateGamepadButton(keycode);
if (button == GAMEPAD_BUTTON_UNKNOWN) return 1;
if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
{
CORE.Input.Gamepad.currentButtonState[0][button] = 1;
}
else CORE.Input.Gamepad.currentButtonState[0][button] = 0;
return 1; }
KeyboardKey key = (keycode > 0 && keycode < KEYCODE_MAP_SIZE)? mapKeycode[keycode] : KEY_NULL;
if (key != KEY_NULL)
{
if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
{
CORE.Input.Keyboard.currentKeyState[key] = 1;
CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
CORE.Input.Keyboard.keyPressedQueueCount++;
}
else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_MULTIPLE) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
else CORE.Input.Keyboard.currentKeyState[key] = 0; }
if (keycode == AKEYCODE_POWER)
{
return 0;
}
else if ((keycode == AKEYCODE_BACK) || (keycode == AKEYCODE_MENU))
{
return 1;
}
else if ((keycode == AKEYCODE_VOLUME_UP) || (keycode == AKEYCODE_VOLUME_DOWN))
{
return 0;
}
return 0;
}
CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event);
for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
{
CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i);
CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) };
float widthRatio = (float)(CORE.Window.screen.width + CORE.Window.renderOffset.x)/(float)CORE.Window.display.width;
float heightRatio = (float)(CORE.Window.screen.height + CORE.Window.renderOffset.y)/(float)CORE.Window.display.height;
CORE.Input.Touch.position[i].x = CORE.Input.Touch.position[i].x*widthRatio - (float)CORE.Window.renderOffset.x/2;
CORE.Input.Touch.position[i].y = CORE.Input.Touch.position[i].y*heightRatio - (float)CORE.Window.renderOffset.y/2;
}
int32_t action = AMotionEvent_getAction(event);
unsigned int flags = action & AMOTION_EVENT_ACTION_MASK;
#if defined(SUPPORT_GESTURES_SYSTEM)
GestureEvent gestureEvent = { 0 };
gestureEvent.pointCount = CORE.Input.Touch.pointCount;
if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_ACTION_UP;
else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE;
else if (flags == AMOTION_EVENT_ACTION_CANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL;
for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++)
{
gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i];
gestureEvent.position[i] = CORE.Input.Touch.position[i];
gestureEvent.position[i].x /= (float)GetScreenWidth();
gestureEvent.position[i].y /= (float)GetScreenHeight();
}
ProcessGestureEvent(gestureEvent);
#endif
int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP)
{
for (int i = pointerIndex; (i < CORE.Input.Touch.pointCount - 1) && (i < MAX_TOUCH_POINTS); i++)
{
CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1];
CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1];
}
CORE.Input.Touch.pointCount--;
}
if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0;
if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1;
else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0;
if (flags == AMOTION_EVENT_ACTION_MOVE)
{
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
}
else
{
CORE.Input.Mouse.previousPosition = CORE.Input.Touch.position[0];
}
CORE.Input.Mouse.currentPosition = CORE.Input.Touch.position[0];
CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f };
return 0;
}