| | |
| | |
| |
|
| | #include "imgui.h" |
| | #include "imgui_impl_android.h" |
| | #include "imgui_impl_opengl3.h" |
| | #include <android/log.h> |
| | #include <android_native_app_glue.h> |
| | #include <android/asset_manager.h> |
| | #include <EGL/egl.h> |
| | #include <GLES3/gl3.h> |
| |
|
| | |
| | static EGLDisplay g_EglDisplay = EGL_NO_DISPLAY; |
| | static EGLSurface g_EglSurface = EGL_NO_SURFACE; |
| | static EGLContext g_EglContext = EGL_NO_CONTEXT; |
| | static struct android_app* g_App = NULL; |
| | static bool g_Initialized = false; |
| | static char g_LogTag[] = "ImGuiExample"; |
| |
|
| | |
| | static int ShowSoftKeyboardInput(); |
| | static int PollUnicodeChars(); |
| | static int GetAssetData(const char* filename, void** out_data); |
| |
|
| | void init(struct android_app* app) |
| | { |
| | if (g_Initialized) |
| | return; |
| |
|
| | g_App = app; |
| | ANativeWindow_acquire(g_App->window); |
| |
|
| | |
| | |
| | { |
| | g_EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
| | if (g_EglDisplay == EGL_NO_DISPLAY) |
| | __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglGetDisplay(EGL_DEFAULT_DISPLAY) returned EGL_NO_DISPLAY"); |
| |
|
| | if (eglInitialize(g_EglDisplay, 0, 0) != EGL_TRUE) |
| | __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglInitialize() returned with an error"); |
| |
|
| | const EGLint egl_attributes[] = { EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_DEPTH_SIZE, 24, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; |
| | EGLint num_configs = 0; |
| | if (eglChooseConfig(g_EglDisplay, egl_attributes, nullptr, 0, &num_configs) != EGL_TRUE) |
| | __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglChooseConfig() returned with an error"); |
| | if (num_configs == 0) |
| | __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglChooseConfig() returned 0 matching config"); |
| |
|
| | |
| | EGLConfig egl_config; |
| | eglChooseConfig(g_EglDisplay, egl_attributes, &egl_config, 1, &num_configs); |
| | EGLint egl_format; |
| | eglGetConfigAttrib(g_EglDisplay, egl_config, EGL_NATIVE_VISUAL_ID, &egl_format); |
| | ANativeWindow_setBuffersGeometry(g_App->window, 0, 0, egl_format); |
| |
|
| | const EGLint egl_context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE }; |
| | g_EglContext = eglCreateContext(g_EglDisplay, egl_config, EGL_NO_CONTEXT, egl_context_attributes); |
| |
|
| | if (g_EglContext == EGL_NO_CONTEXT) |
| | __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglCreateContext() returned EGL_NO_CONTEXT"); |
| |
|
| | g_EglSurface = eglCreateWindowSurface(g_EglDisplay, egl_config, g_App->window, NULL); |
| | eglMakeCurrent(g_EglDisplay, g_EglSurface, g_EglSurface, g_EglContext); |
| | } |
| |
|
| | |
| | IMGUI_CHECKVERSION(); |
| | ImGui::CreateContext(); |
| | ImGuiIO& io = ImGui::GetIO(); |
| |
|
| | |
| | |
| | io.IniFilename = NULL; |
| |
|
| | |
| | ImGui::StyleColorsDark(); |
| | |
| |
|
| | |
| | ImGui_ImplAndroid_Init(g_App->window); |
| | ImGui_ImplOpenGL3_Init("#version 300 es"); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | ImFontConfig font_cfg; |
| | font_cfg.SizePixels = 22.0f; |
| | io.Fonts->AddFontDefault(&font_cfg); |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | ImGui::GetStyle().ScaleAllSizes(3.0f); |
| |
|
| | g_Initialized = true; |
| | } |
| |
|
| | void tick() |
| | { |
| | ImGuiIO& io = ImGui::GetIO(); |
| | if (g_EglDisplay == EGL_NO_DISPLAY) |
| | return; |
| |
|
| | |
| | static bool show_demo_window = true; |
| | static bool show_another_window = false; |
| | static ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); |
| |
|
| | |
| | |
| | PollUnicodeChars(); |
| |
|
| | |
| | static bool WantTextInputLast = false; |
| | if (io.WantTextInput && !WantTextInputLast) |
| | ShowSoftKeyboardInput(); |
| | WantTextInputLast = io.WantTextInput; |
| |
|
| | |
| | ImGui_ImplOpenGL3_NewFrame(); |
| | ImGui_ImplAndroid_NewFrame(); |
| | ImGui::NewFrame(); |
| |
|
| | |
| | if (show_demo_window) |
| | ImGui::ShowDemoWindow(&show_demo_window); |
| |
|
| | |
| | { |
| | static float f = 0.0f; |
| | static int counter = 0; |
| |
|
| | ImGui::Begin("Hello, world!"); |
| |
|
| | ImGui::Text("This is some useful text."); |
| | ImGui::Checkbox("Demo Window", &show_demo_window); |
| | ImGui::Checkbox("Another Window", &show_another_window); |
| |
|
| | ImGui::SliderFloat("float", &f, 0.0f, 1.0f); |
| | ImGui::ColorEdit3("clear color", (float*)&clear_color); |
| |
|
| | if (ImGui::Button("Button")) |
| | counter++; |
| | ImGui::SameLine(); |
| | ImGui::Text("counter = %d", counter); |
| |
|
| | ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); |
| | ImGui::End(); |
| | } |
| |
|
| | |
| | if (show_another_window) |
| | { |
| | ImGui::Begin("Another Window", &show_another_window); |
| | ImGui::Text("Hello from another window!"); |
| | if (ImGui::Button("Close Me")) |
| | show_another_window = false; |
| | ImGui::End(); |
| | } |
| |
|
| | |
| | ImGui::Render(); |
| | glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); |
| | glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); |
| | glClear(GL_COLOR_BUFFER_BIT); |
| | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); |
| | eglSwapBuffers(g_EglDisplay, g_EglSurface); |
| | } |
| |
|
| | void shutdown() |
| | { |
| | if (!g_Initialized) |
| | return; |
| |
|
| | |
| | ImGui_ImplOpenGL3_Shutdown(); |
| | ImGui_ImplAndroid_Shutdown(); |
| | ImGui::DestroyContext(); |
| |
|
| | if (g_EglDisplay != EGL_NO_DISPLAY) |
| | { |
| | eglMakeCurrent(g_EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| |
|
| | if (g_EglContext != EGL_NO_CONTEXT) |
| | eglDestroyContext(g_EglDisplay, g_EglContext); |
| |
|
| | if (g_EglSurface != EGL_NO_SURFACE) |
| | eglDestroySurface(g_EglDisplay, g_EglSurface); |
| |
|
| | eglTerminate(g_EglDisplay); |
| | } |
| |
|
| | g_EglDisplay = EGL_NO_DISPLAY; |
| | g_EglContext = EGL_NO_CONTEXT; |
| | g_EglSurface = EGL_NO_SURFACE; |
| | ANativeWindow_release(g_App->window); |
| |
|
| | g_Initialized = false; |
| | } |
| |
|
| | static void handleAppCmd(struct android_app* app, int32_t appCmd) |
| | { |
| | switch (appCmd) |
| | { |
| | case APP_CMD_SAVE_STATE: |
| | break; |
| | case APP_CMD_INIT_WINDOW: |
| | init(app); |
| | break; |
| | case APP_CMD_TERM_WINDOW: |
| | shutdown(); |
| | break; |
| | case APP_CMD_GAINED_FOCUS: |
| | break; |
| | case APP_CMD_LOST_FOCUS: |
| | break; |
| | } |
| | } |
| |
|
| | static int32_t handleInputEvent(struct android_app* app, AInputEvent* inputEvent) |
| | { |
| | return ImGui_ImplAndroid_HandleInputEvent(inputEvent); |
| | } |
| |
|
| | void android_main(struct android_app* app) |
| | { |
| | app->onAppCmd = handleAppCmd; |
| | app->onInputEvent = handleInputEvent; |
| |
|
| | while (true) |
| | { |
| | int out_events; |
| | struct android_poll_source* out_data; |
| |
|
| | |
| | while (ALooper_pollAll(g_Initialized ? 0 : -1, NULL, &out_events, (void**)&out_data) >= 0) |
| | { |
| | |
| | if (out_data != NULL) |
| | out_data->process(app, out_data); |
| |
|
| | |
| | if (app->destroyRequested != 0) |
| | { |
| | |
| | |
| | if (!g_Initialized) |
| | shutdown(); |
| |
|
| | return; |
| | } |
| | } |
| |
|
| | |
| | tick(); |
| | } |
| | } |
| |
|
| | |
| | |
| | static int ShowSoftKeyboardInput() |
| | { |
| | JavaVM* java_vm = g_App->activity->vm; |
| | JNIEnv* java_env = NULL; |
| |
|
| | jint jni_return = java_vm->GetEnv((void**)&java_env, JNI_VERSION_1_6); |
| | if (jni_return == JNI_ERR) |
| | return -1; |
| |
|
| | jni_return = java_vm->AttachCurrentThread(&java_env, NULL); |
| | if (jni_return != JNI_OK) |
| | return -2; |
| |
|
| | jclass native_activity_clazz = java_env->GetObjectClass(g_App->activity->clazz); |
| | if (native_activity_clazz == NULL) |
| | return -3; |
| |
|
| | jmethodID method_id = java_env->GetMethodID(native_activity_clazz, "showSoftInput", "()V"); |
| | if (method_id == NULL) |
| | return -4; |
| |
|
| | java_env->CallVoidMethod(g_App->activity->clazz, method_id); |
| |
|
| | jni_return = java_vm->DetachCurrentThread(); |
| | if (jni_return != JNI_OK) |
| | return -5; |
| |
|
| | return 0; |
| | } |
| |
|
| | |
| | |
| | |
| | static int PollUnicodeChars() |
| | { |
| | JavaVM* java_vm = g_App->activity->vm; |
| | JNIEnv* java_env = NULL; |
| |
|
| | jint jni_return = java_vm->GetEnv((void**)&java_env, JNI_VERSION_1_6); |
| | if (jni_return == JNI_ERR) |
| | return -1; |
| |
|
| | jni_return = java_vm->AttachCurrentThread(&java_env, NULL); |
| | if (jni_return != JNI_OK) |
| | return -2; |
| |
|
| | jclass native_activity_clazz = java_env->GetObjectClass(g_App->activity->clazz); |
| | if (native_activity_clazz == NULL) |
| | return -3; |
| |
|
| | jmethodID method_id = java_env->GetMethodID(native_activity_clazz, "pollUnicodeChar", "()I"); |
| | if (method_id == NULL) |
| | return -4; |
| |
|
| | |
| | ImGuiIO& io = ImGui::GetIO(); |
| | jint unicode_character; |
| | while ((unicode_character = java_env->CallIntMethod(g_App->activity->clazz, method_id)) != 0) |
| | io.AddInputCharacter(unicode_character); |
| |
|
| | jni_return = java_vm->DetachCurrentThread(); |
| | if (jni_return != JNI_OK) |
| | return -5; |
| |
|
| | return 0; |
| | } |
| |
|
| | |
| | static int GetAssetData(const char* filename, void** outData) |
| | { |
| | int num_bytes = 0; |
| | AAsset* asset_descriptor = AAssetManager_open(g_App->activity->assetManager, filename, AASSET_MODE_BUFFER); |
| | if (asset_descriptor) |
| | { |
| | num_bytes = AAsset_getLength(asset_descriptor); |
| | *outData = IM_ALLOC(num_bytes); |
| | int64_t num_bytes_read = AAsset_read(asset_descriptor, *outData, num_bytes); |
| | AAsset_close(asset_descriptor); |
| | IM_ASSERT(num_bytes_read == num_bytes); |
| | } |
| | return num_bytes; |
| | } |
| |
|