mirror of
				https://github.com/ocornut/imgui.git
				synced 2025-10-26 12:27:30 +00:00 
			
		
		
		
	Android: Amend backend and examples with minor consistency tweaks. (#3446)
This commit is contained in:
		| @@ -10,98 +10,18 @@ | ||||
| #include <EGL/egl.h> | ||||
| #include <GLES3/gl3.h> | ||||
|  | ||||
| // Data | ||||
| 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 char                 g_LogTag[] = "ImGuiExample"; | ||||
|  | ||||
| // Unfortunately, there is no way to show the on-screen input from native code. | ||||
| // Therefore, we call showSoftInput() of the main activity implemented in MainActivity.kt via JNI. | ||||
| static int showSoftInput() | ||||
| { | ||||
|     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; | ||||
| } | ||||
|  | ||||
| // Unfortunately, the native KeyEvent implementation has no getUnicodeChar() function. | ||||
| // Therefore, we implement the processing of KeyEvents in MainActivity.kt and poll | ||||
| // the resulting Unicode characters here via JNI and send them to Dear ImGui. | ||||
| 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; | ||||
|  | ||||
|     // Send the actual characters to Dear ImGui | ||||
|     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; | ||||
| } | ||||
| // Forward declarations of helper functions | ||||
| static int ShowSoftKeyboardInput(); | ||||
| static int PollUnicodeChars(); | ||||
| static int GetAssetData(const char* filename, void** out_data); | ||||
|  | ||||
| void init(struct android_app* app) | ||||
| { | ||||
| @@ -113,65 +33,66 @@ void init(struct android_app* app) | ||||
|  | ||||
|     // Initialize EGL | ||||
|     // This is mostly boilerplate code for EGL... | ||||
|     g_EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); | ||||
|     { | ||||
|         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 (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"); | ||||
|  | ||||
|     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"); | ||||
|  | ||||
|     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}; | ||||
|         // Get the first 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); | ||||
|  | ||||
|     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"); | ||||
|         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 (num_configs == 0) | ||||
|         __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglChooseConfig(..) returned 0 matching configs"); | ||||
|         if (g_EglContext == EGL_NO_CONTEXT) | ||||
|             __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglCreateContext() returned EGL_NO_CONTEXT"); | ||||
|  | ||||
|     // Get the (first) 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); | ||||
|         g_EglSurface = eglCreateWindowSurface(g_EglDisplay, egl_config, g_App->window, NULL); | ||||
|         eglMakeCurrent(g_EglDisplay, g_EglSurface, g_EglSurface, g_EglContext); | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|  | ||||
|     // Dear Imgui | ||||
|     // Setup Dear ImGui context | ||||
|     IMGUI_CHECKVERSION(); | ||||
|     ImGui::CreateContext(); | ||||
|     ImGuiIO& io = ImGui::GetIO(); | ||||
|  | ||||
|     // Disable loading/saving of .ini file from disk. | ||||
|     // FIXME: Consider using LoadIniSettingsFromMemory() / SaveIniSettingsToMemory() to save in appropriate location for Android. | ||||
|     io.IniFilename = NULL; | ||||
|  | ||||
|     // Setup Dear ImGui style | ||||
|     ImGui::StyleColorsDark(); | ||||
|     //ImGui::StyleColorsClassic(); | ||||
|  | ||||
|     // Setup Platform/Renderer backends | ||||
|     ImGui_ImplAndroid_Init(g_App->window); | ||||
|     ImGui_ImplOpenGL3_Init("#version 300 es"); | ||||
|  | ||||
|     // Load Fonts | ||||
|     // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. | ||||
|     // - add_font_from_assets_ttf() will return the ImFont* so you can store it if you need to select the font among multiple. | ||||
|     // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). | ||||
|     // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. | ||||
|     // - Read 'docs/FONTS.md' for more instructions and details. | ||||
|     // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! | ||||
|     // - The TTF files have to be placed into the assets/ directory (android/app/src/main/assets). | ||||
|     // - Android: The TTF files have to be placed into the assets/ directory (android/app/src/main/assets), we use our GetAssetData() helper to retrieve them. | ||||
|  | ||||
|     // We load the default font with increased size to improve readability on many devices with "high" DPI. | ||||
|     // FIXME: Put some effort into DPI awareness | ||||
|     // FIXME: Put some effort into DPI awareness. | ||||
|     // Important: when calling AddFontFromMemoryTTF(), ownership of font_data is transfered by Dear ImGui by default (deleted is handled by Dear ImGui), unless we set FontDataOwnedByAtlas=false in ImFontConfig | ||||
|     ImFontConfig font_cfg; | ||||
|     font_cfg.SizePixels = 22.0f; | ||||
|     io.Fonts->AddFontDefault(&font_cfg); | ||||
| @@ -179,19 +100,19 @@ void init(struct android_app* app) | ||||
|     //int font_data_size; | ||||
|     //ImFont* font; | ||||
|     //font_data_size = GetAssetData("Roboto-Medium.ttf", &font_data); | ||||
|     //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 16.0f); // Ownership of font_data is transfered to ImGui. Deletion is handled by ImGui. | ||||
|     //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 16.0f); | ||||
|     //IM_ASSERT(font != NULL); | ||||
|     //font_data_size = GetAssetData("Cousine-Regular.ttf", &font_data); | ||||
|     //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 15.0f); // Ownership of font_data is transfered to ImGui. Deletion is handled by ImGui. | ||||
|     //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 15.0f); | ||||
|     //IM_ASSERT(font != NULL); | ||||
|     //font_data_size = GetAssetData("DroidSans.ttf", &font_data); | ||||
|     //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 16.0f); // Ownership of font_data is transfered to ImGui. Deletion is handled by ImGui. | ||||
|     //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 16.0f); | ||||
|     //IM_ASSERT(font != NULL); | ||||
|     //font_data_size = GetAssetData("ProggyTiny.ttf", &font_data); | ||||
|     //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 10.0f); // Ownership of font_data is transfered to ImGui. Deletion is handled by ImGui. | ||||
|     //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 10.0f); | ||||
|     //IM_ASSERT(font != NULL); | ||||
|     //font_data_size = GetAssetData("ArialUni.ttf", &font_data); | ||||
|     //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); // Ownership of font_data is transfered to ImGui. Deletion is handled by ImGui. | ||||
|     //font = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); | ||||
|     //IM_ASSERT(font != NULL); | ||||
|  | ||||
|     // Arbitrary scale-up | ||||
| @@ -203,75 +124,74 @@ void init(struct android_app* app) | ||||
|  | ||||
| void tick() | ||||
| { | ||||
|     // Our state (Dear Imgui) | ||||
|     ImGuiIO& io = ImGui::GetIO(); | ||||
|     if (g_EglDisplay == EGL_NO_DISPLAY) | ||||
|         return; | ||||
|  | ||||
|     // Our state | ||||
|     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); | ||||
|  | ||||
|     if (g_EglDisplay != EGL_NO_DISPLAY) | ||||
|     // Poll Unicode characters via JNI | ||||
|     // FIXME: do not call this every frame because of JNI overhead | ||||
|     PollUnicodeChars(); | ||||
|  | ||||
|     // Open on-screen (soft) input if requested by Dear ImGui | ||||
|     static bool WantTextInputLast = false; | ||||
|     if (io.WantTextInput && !WantTextInputLast) | ||||
|         ShowSoftKeyboardInput(); | ||||
|     WantTextInputLast = io.WantTextInput; | ||||
|  | ||||
|     // Start the Dear ImGui frame | ||||
|     ImGui_ImplOpenGL3_NewFrame(); | ||||
|     ImGui_ImplAndroid_NewFrame(); | ||||
|     ImGui::NewFrame(); | ||||
|  | ||||
|     // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). | ||||
|     if (show_demo_window) | ||||
|         ImGui::ShowDemoWindow(&show_demo_window); | ||||
|  | ||||
|     // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window. | ||||
|     { | ||||
|         ImGuiIO& io = ImGui::GetIO(); | ||||
|         static float f = 0.0f; | ||||
|         static int counter = 0; | ||||
|  | ||||
|         // Poll Unicode characters via JNI | ||||
|         // FIXME: do not call this every frame because of JNI overhead | ||||
|         pollUnicodeChars(); | ||||
|         ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. | ||||
|  | ||||
|         // Open on-screen (soft) input if demanded by Dear ImGui | ||||
|         static bool WantTextInputLast = false; | ||||
|         if (io.WantTextInput && !WantTextInputLast) | ||||
|             showSoftInput(); | ||||
|         WantTextInputLast = io.WantTextInput; | ||||
|         ImGui::Text("This is some useful text.");               // Display some text (you can use a format strings too) | ||||
|         ImGui::Checkbox("Demo Window", &show_demo_window);      // Edit bools storing our window open/close state | ||||
|         ImGui::Checkbox("Another Window", &show_another_window); | ||||
|  | ||||
|         // Start the Dear ImGui frame | ||||
|         ImGui_ImplOpenGL3_NewFrame(); | ||||
|         ImGui_ImplAndroid_NewFrame(); | ||||
|         ImGui::NewFrame(); | ||||
|         ImGui::SliderFloat("float", &f, 0.0f, 1.0f);            // Edit 1 float using a slider from 0.0f to 1.0f | ||||
|         ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color | ||||
|  | ||||
|         // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). | ||||
|         if (show_demo_window) | ||||
|             ImGui::ShowDemoWindow(&show_demo_window); | ||||
|         if (ImGui::Button("Button"))                            // Buttons return true when clicked (most widgets return true when edited/activated) | ||||
|             counter++; | ||||
|         ImGui::SameLine(); | ||||
|         ImGui::Text("counter = %d", counter); | ||||
|  | ||||
|         // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window. | ||||
|         { | ||||
|             static float f = 0.0f; | ||||
|             static int counter = 0; | ||||
|  | ||||
|             ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. | ||||
|  | ||||
|             ImGui::Text("This is some useful text.");          // Display some text (you can use a format strings too) | ||||
|             ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state | ||||
|             ImGui::Checkbox("Another Window", &show_another_window); | ||||
|  | ||||
|             ImGui::SliderFloat("float", &f, 0.0f, 1.0f);             // Edit 1 float using a slider from 0.0f to 1.0f | ||||
|             ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color | ||||
|  | ||||
|             if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) | ||||
|                 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(); | ||||
|         } | ||||
|  | ||||
|         // 3. Show another simple window. | ||||
|         if (show_another_window) | ||||
|         { | ||||
|             ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) | ||||
|             ImGui::Text("Hello from another window!"); | ||||
|             if (ImGui::Button("Close Me")) | ||||
|                 show_another_window = false; | ||||
|             ImGui::End(); | ||||
|         } | ||||
|  | ||||
|         // Rendering | ||||
|         ImGui::Render(); | ||||
|         glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); | ||||
|         glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); | ||||
|         glClear(GL_COLOR_BUFFER_BIT); | ||||
|         ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); | ||||
|         eglSwapBuffers(g_EglDisplay, g_EglSurface); | ||||
|         ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); | ||||
|         ImGui::End(); | ||||
|     } | ||||
|  | ||||
|     // 3. Show another simple window. | ||||
|     if (show_another_window) | ||||
|     { | ||||
|         ImGui::Begin("Another Window", &show_another_window);   // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) | ||||
|         ImGui::Text("Hello from another window!"); | ||||
|         if (ImGui::Button("Close Me")) | ||||
|             show_another_window = false; | ||||
|         ImGui::End(); | ||||
|     } | ||||
|  | ||||
|     // Rendering | ||||
|     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() | ||||
| @@ -279,7 +199,7 @@ void shutdown() | ||||
|     if (!g_Initialized) | ||||
|         return; | ||||
|  | ||||
|     // Cleanup (Dear Imgui) | ||||
|     // Cleanup | ||||
|     ImGui_ImplOpenGL3_Shutdown(); | ||||
|     ImGui_ImplAndroid_Shutdown(); | ||||
|     ImGui::DestroyContext(); | ||||
| @@ -362,3 +282,88 @@ void android_main(struct android_app* app) | ||||
|         tick(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Unfortunately, there is no way to show the on-screen input from native code. | ||||
| // Therefore, we call ShowSoftKeyboardInput() of the main activity implemented in MainActivity.kt via JNI. | ||||
| 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; | ||||
| } | ||||
|  | ||||
| // Unfortunately, the native KeyEvent implementation has no getUnicodeChar() function. | ||||
| // Therefore, we implement the processing of KeyEvents in MainActivity.kt and poll | ||||
| // the resulting Unicode characters here via JNI and send them to Dear ImGui. | ||||
| 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; | ||||
|  | ||||
|     // Send the actual characters to Dear ImGui | ||||
|     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; | ||||
| } | ||||
|  | ||||
| // Helper to retrieve data placed into the assets/ directory (android/app/src/main/assets) | ||||
| 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; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 ocornut
					ocornut