Android: Amend backend and examples with minor consistency tweaks. (#3446)

docking
ocornut 4 years ago
parent fb85c0341b
commit 8dd692c29c

@ -3,9 +3,13 @@
// Implemented features:
// [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE).
// Missing features:
// [ ] Platform: Clipboard support.
// [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android.
// Important:
// - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446)
// - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446)
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
@ -13,39 +17,35 @@
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2021-03-02: Support for physical pointer device input (such as physical mouse)
// 2020-09-13: Support for Unicode characters
// 2020-08-31: On-screen and physical keyboard input (ASCII characters only)
// 2020-03-02: basic draft, touch input
// 2021-03-04: Initial version.
#include "imgui.h"
#include "imgui_impl_android.h"
#include <time.h>
#include <map>
#include <queue>
// Android
#include <android/native_window.h>
#include <android/input.h>
#include <android/keycodes.h>
#include <android/log.h>
// Android data
static double g_Time = 0.0;
static ANativeWindow* g_Window;
static char g_LogTag[] = "ImguiExample";
static char g_LogTag[] = "ImGuiExample";
static std::map<int32_t, std::queue<int32_t>> g_KeyEventQueues; // FIXME: Remove dependency on map and queue once we use upcoming input queue.
int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* inputEvent)
int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event)
{
ImGuiIO& io = ImGui::GetIO();
int32_t event_type = AInputEvent_getType(inputEvent);
int32_t event_type = AInputEvent_getType(input_event);
switch (event_type)
{
case AINPUT_EVENT_TYPE_KEY:
{
int32_t event_key_code = AKeyEvent_getKeyCode(inputEvent);
int32_t event_action = AKeyEvent_getAction(inputEvent);
int32_t event_meta_state = AKeyEvent_getMetaState(inputEvent);
int32_t event_key_code = AKeyEvent_getKeyCode(input_event);
int32_t event_action = AKeyEvent_getAction(input_event);
int32_t event_meta_state = AKeyEvent_getMetaState(input_event);
io.KeyCtrl = ((event_meta_state & AMETA_CTRL_ON) != 0);
io.KeyShift = ((event_meta_state & AMETA_SHIFT_ON) != 0);
@ -53,10 +53,9 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* inputEvent)
switch (event_action)
{
// FIXME: AKEY_EVENT_ACTION_DOWN and AKEY_EVENT_ACTION_UP occur at once
// as soon as a touch pointer goes up from a key. We use a simple key event queue
// and process one event per key per ImGui frame in ImGui_ImplAndroid_NewFrame().
// ...or consider ImGui IO queue, if suitable: https://github.com/ocornut/imgui/issues/2787
// FIXME: AKEY_EVENT_ACTION_DOWN and AKEY_EVENT_ACTION_UP occur at once as soon as a touch pointer
// goes up from a key. We use a simple key event queue/ and process one event per key per frame in
// ImGui_ImplAndroid_NewFrame()...or consider using IO queue, if suitable: https://github.com/ocornut/imgui/issues/2787
case AKEY_EVENT_ACTION_DOWN:
case AKEY_EVENT_ACTION_UP:
g_KeyEventQueues[event_key_code].push(event_action);
@ -68,7 +67,7 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* inputEvent)
}
case AINPUT_EVENT_TYPE_MOTION:
{
int32_t event_action = AMotionEvent_getAction(inputEvent);
int32_t event_action = AMotionEvent_getAction(input_event);
int32_t event_pointer_index = (event_action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
event_action &= AMOTION_EVENT_ACTION_MASK;
switch (event_action)
@ -78,33 +77,29 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* inputEvent)
// Physical mouse buttons (and probably other physical devices) also invoke the actions AMOTION_EVENT_ACTION_DOWN/_UP,
// but we have to process them separately to identify the actual button pressed. This is done below via
// AMOTION_EVENT_ACTION_BUTTON_PRESS/_RELEASE. Here, we only process "FINGER" input (and "UNKNOWN", as a fallback).
if((AMotionEvent_getToolType(inputEvent, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_FINGER)
|| (AMotionEvent_getToolType(inputEvent, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_UNKNOWN))
if((AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_FINGER)
|| (AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_UNKNOWN))
{
io.MouseDown[0] = (event_action == AMOTION_EVENT_ACTION_DOWN) ? true : false;
io.MousePos = ImVec2(
AMotionEvent_getX(inputEvent, event_pointer_index),
AMotionEvent_getY(inputEvent, event_pointer_index));
io.MousePos = ImVec2(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index));
}
break;
case AMOTION_EVENT_ACTION_BUTTON_PRESS:
case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
{
int32_t button_state = AMotionEvent_getButtonState(inputEvent);
int32_t button_state = AMotionEvent_getButtonState(input_event);
io.MouseDown[0] = (button_state & AMOTION_EVENT_BUTTON_PRIMARY) ? true : false;
io.MouseDown[1] = (button_state & AMOTION_EVENT_BUTTON_SECONDARY) ? true : false;
io.MouseDown[2] = (button_state & AMOTION_EVENT_BUTTON_TERTIARY) ? true : false;
}
break;
case AMOTION_EVENT_ACTION_HOVER_MOVE: // Hovering: Tool moves while NOT pressed (such as a physical mouse)
case AMOTION_EVENT_ACTION_MOVE: // Touch pointer moves while DOWN
io.MousePos = ImVec2(
AMotionEvent_getX(inputEvent, event_pointer_index),
AMotionEvent_getY(inputEvent, event_pointer_index));
case AMOTION_EVENT_ACTION_MOVE: // Touch pointer moves while DOWN
io.MousePos = ImVec2(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index));
break;
case AMOTION_EVENT_ACTION_SCROLL:
io.MouseWheel = AMotionEvent_getAxisValue(inputEvent, AMOTION_EVENT_AXIS_VSCROLL, event_pointer_index);
io.MouseWheelH = AMotionEvent_getAxisValue(inputEvent, AMOTION_EVENT_AXIS_HSCROLL, event_pointer_index);
io.MouseWheel = AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_VSCROLL, event_pointer_index);
io.MouseWheelH = AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_HSCROLL, event_pointer_index);
break;
default:
break;
@ -123,11 +118,11 @@ bool ImGui_ImplAndroid_Init(ANativeWindow* window)
g_Window = window;
g_Time = 0.0;
// Setup back-end capabilities flags
// Setup backend capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendPlatformName = "imgui_impl_android";
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
// Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[ImGuiKey_Tab] = AKEYCODE_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = AKEYCODE_DPAD_LEFT; // also covers physical keyboard arrow key
io.KeyMap[ImGuiKey_RightArrow] = AKEYCODE_DPAD_RIGHT; // also covers physical keyboard arrow key
@ -161,10 +156,10 @@ void ImGui_ImplAndroid_Shutdown()
void ImGui_ImplAndroid_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
// Process queued key events
// FIXME: This is a workaround for multiple key event actions occuring at once (see above) and can be removed once we use upcoming input queue.
// FIXME: This is a workaround for multiple key event actions occurring at once (see above) and can be removed once we use upcoming input queue.
for (auto& key_queue : g_KeyEventQueues)
{
if (key_queue.second.empty())

@ -3,9 +3,13 @@
// Implemented features:
// [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE).
// Missing features:
// [ ] Platform: Clipboard support.
// [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android.
// Important:
// - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446)
// - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446)
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
@ -16,7 +20,7 @@
struct ANativeWindow;
struct AInputEvent;
IMGUI_IMPL_API int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* inputEvent);
IMGUI_IMPL_API bool ImGui_ImplAndroid_Init(ANativeWindow* window);
IMGUI_IMPL_API int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event);
IMGUI_IMPL_API void ImGui_ImplAndroid_Shutdown();
IMGUI_IMPL_API void ImGui_ImplAndroid_NewFrame();

@ -149,7 +149,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendPlatformName = "imgui_impl_glfw";
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
// Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;

@ -220,7 +220,8 @@ bool ImGui_Marmalade_Init(bool install_callbacks)
ImGuiIO& io = ImGui::GetIO();
io.BackendPlatformName = io.BackendRendererName = "imgui_impl_marmalade";
io.KeyMap[ImGuiKey_Tab] = s3eKeyTab; // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
// Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[ImGuiKey_Tab] = s3eKeyTab
io.KeyMap[ImGuiKey_LeftArrow] = s3eKeyLeft;
io.KeyMap[ImGuiKey_RightArrow] = s3eKeyRight;
io.KeyMap[ImGuiKey_UpArrow] = s3eKeyUp;

@ -138,7 +138,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendPlatformName = "imgui_impl_sdl";
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
// Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT;

@ -731,7 +731,7 @@ void ImGui_ImplWGPU_InvalidateDeviceObjects()
bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format)
{
// Setup back-end capabilities flags
// Setup backend capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_webgpu";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.

@ -91,7 +91,7 @@ bool ImGui_ImplWin32_Init(void* hwnd)
io.BackendPlatformName = "imgui_impl_win32";
io.ImeWindowHandle = hwnd;
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime.
// Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime.
io.KeyMap[ImGuiKey_Tab] = VK_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;

@ -58,6 +58,7 @@ Other Changes:
- ImDrawList: AddCircle, AddCircleFilled(): Tweaked default segment count calculation to honor MaxError
with more accuracy. Made default segment count always even for better looking result. (#3808) [@thedmd]
- ImDrawList: AddCircle, AddCircleFilled(): New default for style.
- Backends: Android: Added native Android backend. (#3446) [@duddel]
- Backends: Win32: Added ImGui_ImplWin32_EnableAlphaCompositing() to facilitate experimenting with
alpha compositing and transparent windows. (#2766, #3447 etc.).
- Backends: OpenGL, Vulkan, DX9, DX10, DX11, DX12, Metal, WebGPU, Allegro: Rework blending equation to
@ -66,6 +67,7 @@ Other Changes:
(#2693, #2764, #2766, #2873, #3447, #3813, #3816) [@ocornut, @thedmd, @ShawnM427, @Ubpa, @aiekick]
- Backends: DX9: Fix to support IMGUI_USE_BGRA_PACKED_COLOR. (#3844) [@Xiliusha]
- Backends: DX9: Fix to support colored glyphs, using newly introduced 'TexPixelsUseColors' info. (#3844)
- Examples: Android: Added Android + GL ES2 example. (#3446) [@duddel]
- Examples: Reworked setup of clear color to be compatible with transparent values.
- CI: Use a dedicated "scheduled" workflow to trigger scheduled builds. Forks may disable this workflow if
scheduled builds builds are not required. [@rokups]

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.6)
project(ImguiExample)
project(ImGuiExample)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

@ -3,7 +3,7 @@
package="imgui.example.android">
<application
android:label="ImguiExample"
android:label="ImGuiExample"
android:allowBackup="false"
android:fullBackupContent="false"
android:hasCode="true">
@ -13,7 +13,7 @@
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:configChanges="orientation|keyboardHidden|screenSize">
<meta-data android:name="android.app.lib_name"
android:value="ImguiExample" />
android:value="ImGuiExample" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />

@ -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);
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 configs");
// 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);
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);
{
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");
// 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);
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)
{
ImGuiIO& io = ImGui::GetIO();
// Poll Unicode characters via JNI
// FIXME: do not call this every frame because of JNI overhead
pollUnicodeChars();
// Open on-screen (soft) input if demanded by Dear ImGui
static bool WantTextInputLast = false;
if (io.WantTextInput && !WantTextInputLast)
showSoftInput();
WantTextInputLast = io.WantTextInput;
// Poll Unicode characters via JNI
// FIXME: do not call this every frame because of JNI overhead
PollUnicodeChars();
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplAndroid_NewFrame();
ImGui::NewFrame();
// Open on-screen (soft) input if requested by Dear ImGui
static bool WantTextInputLast = false;
if (io.WantTextInput && !WantTextInputLast)
ShowSoftKeyboardInput();
WantTextInputLast = io.WantTextInput;
// 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);
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplAndroid_NewFrame();
ImGui::NewFrame();
// 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;
// 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);
ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
// 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::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::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
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
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);
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::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
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::End();
}
if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
counter++;
ImGui::SameLine();
ImGui::Text("counter = %d", counter);
// 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();
}
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
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);
// 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;
}

@ -2767,7 +2767,7 @@ enum ImGuiViewportFlags_
struct ImGuiViewport
{
ImGuiViewportFlags Flags; // See ImGuiViewportFlags_
ImVec2 Pos; // Main Area: Position of the viewport (Dear Imgui coordinates are the same as OS desktop/native coordinates)
ImVec2 Pos; // Main Area: Position of the viewport (Dear ImGui coordinates are the same as OS desktop/native coordinates)
ImVec2 Size; // Main Area: Size of the viewport.
ImVec2 WorkPos; // Work Area: Position of the viewport minus task bars, menus bars, status bars (>= Pos)
ImVec2 WorkSize; // Work Area: Size of the viewport minus task bars, menu bars, status bars (<= Size)

Loading…
Cancel
Save