|
- #if defined(OS_ANDROID)
-
- #include <android/api-level.h>
- #include <android/ndk-version.h>
-
- #include <EGL/egl.h>
- // #include <GLES/gl.h>
-
- #if __ANDROID_API__ >= 28
- #include <GLES3/gl3.h>
- #else
- #include <GLES2/gl2.h>
- #endif
-
- #include <android/choreographer.h>
- #include <android/keycodes.h>
- #include <android/log.h>
- #include <android/native_activity.h>
- #include <android/set_abort_message.h>
- #include <android_native_app_glue.h>
- #include <jni.h>
-
- #include <assert.h>
- #include <errno.h>
- #include <math.h>
- #include <stdlib.h> /**< atoi abort */
- #include <string.h>
-
- #include "image.h"
-
- #include "sww/app.h"
- #include "window_private.h"
-
- #define EGL_ZBITS 16
- #define EGL_IMMEDIATE_SIZE 2048
-
- #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "sww", __VA_ARGS__))
- #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "sww", __VA_ARGS__))
- #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "sww", __VA_ARGS__))
- #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "sww", __VA_ARGS__))
-
- /* For debug builds, always enable the debug traces in this library */
- #ifndef NDEBUG
- #define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "sww", __VA_ARGS__))
- #else
- #define LOGV(...) ((void)0)
- #endif
-
- #define CHECK_NOT_NULL(value) \
- do { \
- if ((value) == NULL) { \
- Fatal("%s:%d:%s must not be null", __PRETTY_FUNCTION__, __LINE__, #value); \
- } \
- } while (false)
-
- __attribute__((__format__(__printf__, 1, 2))) static void Fatal(const char* fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- char* buf;
- if (vasprintf(&buf, fmt, ap) < 0) {
- android_set_abort_message("failed for format error message");
- } else {
- android_set_abort_message(buf);
- // Also log directly, since the default Android Studio logcat filter hides
- // the backtrace which would otherwise show the abort message.
- LOGE("%s", buf);
- }
- abort();
- }
-
- extern struct android_app* gapp;
- int android_width, android_height;
-
- typedef struct swwApp swwApp;
-
- struct swwApp
- {
- struct android_app* state;
- int android_sdk_version;
-
- swwWindow* window;
-
- int should_exit;
- int window_count;
-
- char keys[swwKeycode_NUM];
- char buttons[swwButton_NUM];
- };
-
- struct swwWindow
- {
- EGLDisplay egl_display;
- EGLSurface egl_surface;
- EGLContext egl_context;
- EGLConfig egl_config;
-
- swwImage* surface;
-
- /* common data */
- swwWindowCallback callback;
- void* userdata;
- };
-
- static EGLint const kConfigAttributeList[] = {EGL_RED_SIZE,
- 8,
- EGL_GREEN_SIZE,
- 8,
- EGL_BLUE_SIZE,
- 8,
- EGL_ALPHA_SIZE,
- 8,
- EGL_BUFFER_SIZE,
- 32,
- EGL_STENCIL_SIZE,
- 0,
- EGL_DEPTH_SIZE,
- EGL_ZBITS,
- // EGL_SAMPLES, 1,
- #ifdef ANDROID
- #if __ANDROID_API__ >= 28
- EGL_RENDERABLE_TYPE,
- EGL_OPENGL_ES3_BIT,
- #else
- EGL_RENDERABLE_TYPE,
- EGL_OPENGL_ES2_BIT,
- #endif
-
- #else
- EGL_RENDERABLE_TYPE,
- EGL_OPENGL_ES2_BIT,
- EGL_SURFACE_TYPE,
- EGL_WINDOW_BIT | EGL_PIXMAP_BIT,
- #endif
- EGL_NONE};
-
- static EGLint WindowAttributeList[] = {EGL_NONE};
-
- static const EGLint kContextAttributeList[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
-
- swwApp g_app = {NULL, 0, NULL, 0, 0, {0}, {0}};
-
- static void HandleCmd(struct android_app* app, int32_t cmd);
- static int32_t HandleInput(struct android_app* app, AInputEvent* event);
-
- // The entry in android_native_app_glue.h
- // Here I convert it to the traditional `main`
- void android_main(struct android_app* state)
- {
- int main(int argc, char* argv[]);
-
- // Assign the g_app.app first, it is very important,
- // because of this reference will be used in the reset
- // part of this program.
- g_app.state = state;
-
- char mainptr[5] = {'m', 'a', 'i', 'n', 0};
- char* argv[] = {mainptr, NULL};
-
- {
- char sdk_ver_str[92];
- int len = __system_property_get("ro.build.version.sdk", sdk_ver_str);
- if (len <= 0) {
- g_app.android_sdk_version = 0;
- } else {
- g_app.android_sdk_version = atoi(sdk_ver_str);
- }
- }
-
- g_app.state->onAppCmd = HandleCmd;
- g_app.state->onInputEvent = HandleInput;
-
- LOGI("Starting with Android SDK Version: %d", g_app.android_sdk_version);
-
- main(1, argv);
-
- LOGI("Main Complete\n");
- }
-
- void HandleCmd(struct android_app* app, int32_t cmd)
- {
- switch (cmd) {
- case APP_CMD_DESTROY:
- // This gets called initially after back.
- // TODO: HandleDestroy();
- ANativeActivity_finish(g_app.state->activity);
- break;
- case APP_CMD_INIT_WINDOW:
- // When returning from a back button suspension, this isn't called.
- if (g_app.state == NULL) {
- LOGI("Got start event");
- } else {
- // TODO: HandleResume();
- }
- break;
- case APP_CMD_TERM_WINDOW:
- // This gets called initially when you click "back"
- // This also gets called when you are brought into standby.
- // Not sure why - callbacks here seem to break stuff.
- if (g_app.window != NULL) {
- if (g_app.window->egl_display != EGL_NO_DISPLAY) {
- eglMakeCurrent(g_app.window->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT);
- if (g_app.window->egl_context != EGL_NO_CONTEXT) {
- eglDestroyContext(g_app.window->egl_display, g_app.window->egl_context);
- }
- if (g_app.window->egl_surface != EGL_NO_SURFACE) {
- eglDestroySurface(g_app.window->egl_display, g_app.window->egl_surface);
- }
- eglTerminate(g_app.window->egl_display);
- }
- g_app.window->egl_context = EGL_NO_CONTEXT;
- g_app.window->egl_surface = EGL_NO_SURFACE;
- g_app.window->egl_display = EGL_NO_DISPLAY;
- }
- // TODO(donkey): The window will be destroied.
- break;
-
- case APP_CMD_PAUSE:
- // TODO: HandleSuspend();
- break;
-
- case APP_CMD_RESUME:
- // TODO: HandleResume();
- break;
-
- // case APP_CMD_CUSTOM_EVENT:
- // TODO: if (HandleCustomEventCallback) HandleCustomEventCallback();
- // break;
-
- default:
- LOGW("event not handled: %d", cmd);
- }
- }
-
- int32_t HandleInput(struct android_app* app, AInputEvent* event)
- {
- if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
- int action = AMotionEvent_getAction(event);
- int whichsource = action >> 8;
- action &= AMOTION_EVENT_ACTION_MASK;
- size_t pointerCount = AMotionEvent_getPointerCount(event);
-
- size_t i = 0;
- for (; i < pointerCount; ++i) {
- int x, y, index;
- x = AMotionEvent_getX(event, i);
- y = AMotionEvent_getY(event, i);
- index = AMotionEvent_getPointerId(event, i);
-
- if (action == AMOTION_EVENT_ACTION_POINTER_DOWN
- || action == AMOTION_EVENT_ACTION_DOWN) {
- int id = index;
- if (action == AMOTION_EVENT_ACTION_POINTER_DOWN && id != whichsource) {
- continue;
- }
- // TODO: HandleButton(x, y, id, 1);
- ANativeActivity_showSoftInput(g_app.state->activity,
- ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED);
- } else if (action == AMOTION_EVENT_ACTION_POINTER_UP
- || action == AMOTION_EVENT_ACTION_UP
- || action == AMOTION_EVENT_ACTION_CANCEL) {
- int id = index;
- if (action == AMOTION_EVENT_ACTION_POINTER_UP && id != whichsource) {
- continue;
- }
- // TODO: HandleButton(x, y, id, 0);
- } else if (action == AMOTION_EVENT_ACTION_MOVE) {
- // TODO: HandleMotion(x, y, index);
- }
- }
- return 1;
- } else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) {
- int code = AKeyEvent_getKeyCode(event);
- #ifdef ANDROID_USE_SCANCODES
- HandleKey(code, AKeyEvent_getAction(event));
- #else
- int unicode = AndroidGetUnicodeChar(code, AMotionEvent_getMetaState(event));
- if (unicode) {
- // TODO: HandleKey(unicode, AKeyEvent_getAction(event));
- } else {
- // TODO: HandleKey(code, !AKeyEvent_getAction(event));
- return (code == 4) ? 1 : 0; // don't override functionality.
- }
- #endif
- return 1;
- }
-
- return 0;
- }
-
- void swwApp_PollEventWithTimeout(float timeout)
- {
- int events;
- struct android_poll_source* source;
- // FIXME(anjingyu): Calculate the timeout accurately.
- while (ALooper_pollOnce((int)(timeout * 1000), 0, &events, (void**)&source) >= 0) {
- if (source != NULL) {
- source->process(g_app.state, source);
- }
- }
- }
-
- void swwApp_PollEvent()
- {
- int events;
- struct android_poll_source* source;
- while (ALooper_pollOnce(0, 0, &events, (void**)&source) >= 0) {
- if (source != NULL) {
- source->process(g_app.state, source);
- }
- }
- }
-
- void swwWindow_GetSize(swwWindow* o, uint32_t* width, uint32_t* height)
- {
- *width = o->surface->width;
- *height = o->surface->height;
- }
-
- swwImage* swwWindow_GetSurface(swwWindow* o)
- {
- return o->surface;
- }
-
- void swwWindow_SetTitle(swwWindow* o, const char* title)
- {
- (void)o;
- (void)title;
- }
-
- static double GetNativeTime(void)
- {
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
- }
-
- float swwApp_GetTime(void)
- {
- static double initial = -1;
- if (initial < 0) {
- initial = GetNativeTime();
- }
- return (float)(GetNativeTime() - initial);
- }
-
- #define NS_PER_SECOND 1000000000
-
- void swwApp_SleepNs(uint64_t ns)
- {
- int was_error;
-
- struct timespec tv, remaining;
-
- // Set the timeout interval
- remaining.tv_sec = (time_t)(ns / NS_PER_SECOND);
- remaining.tv_nsec = (long)(ns % NS_PER_SECOND);
-
- do {
- errno = 0;
- tv.tv_sec = remaining.tv_sec;
- tv.tv_nsec = remaining.tv_nsec;
- was_error = nanosleep(&tv, &remaining);
- } while (was_error && (errno == EINTR));
- }
-
- void swwApp_Sleep(float seconds)
- {
- swwApp_SleepNs((uint64_t)(seconds * NS_PER_SECOND));
- }
-
- #undef NS_PER_SECOND
-
- #endif /* OS_ANDROID */
|