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

This commit is contained in:
ocornut 2021-03-04 11:03:40 +01:00
parent fb85c0341b
commit 8dd692c29c
12 changed files with 236 additions and 229 deletions

View File

@ -3,9 +3,13 @@
// Implemented features: // Implemented features:
// [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE). // [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE).
// Missing features:
// [ ] Platform: Clipboard support. // [ ] Platform: Clipboard support.
// [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [ ] 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. // [ ] 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. // 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. // 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 // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (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) // 2021-03-04: Initial version.
// 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
#include "imgui.h" #include "imgui.h"
#include "imgui_impl_android.h" #include "imgui_impl_android.h"
#include <time.h> #include <time.h>
#include <map> #include <map>
#include <queue> #include <queue>
// Android
#include <android/native_window.h> #include <android/native_window.h>
#include <android/input.h> #include <android/input.h>
#include <android/keycodes.h> #include <android/keycodes.h>
#include <android/log.h> #include <android/log.h>
// Android data
static double g_Time = 0.0; static double g_Time = 0.0;
static ANativeWindow* g_Window; 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. 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(); ImGuiIO& io = ImGui::GetIO();
int32_t event_type = AInputEvent_getType(inputEvent); int32_t event_type = AInputEvent_getType(input_event);
switch (event_type) switch (event_type)
{ {
case AINPUT_EVENT_TYPE_KEY: case AINPUT_EVENT_TYPE_KEY:
{ {
int32_t event_key_code = AKeyEvent_getKeyCode(inputEvent); int32_t event_key_code = AKeyEvent_getKeyCode(input_event);
int32_t event_action = AKeyEvent_getAction(inputEvent); int32_t event_action = AKeyEvent_getAction(input_event);
int32_t event_meta_state = AKeyEvent_getMetaState(inputEvent); int32_t event_meta_state = AKeyEvent_getMetaState(input_event);
io.KeyCtrl = ((event_meta_state & AMETA_CTRL_ON) != 0); io.KeyCtrl = ((event_meta_state & AMETA_CTRL_ON) != 0);
io.KeyShift = ((event_meta_state & AMETA_SHIFT_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) switch (event_action)
{ {
// FIXME: AKEY_EVENT_ACTION_DOWN and AKEY_EVENT_ACTION_UP occur at once // FIXME: AKEY_EVENT_ACTION_DOWN and AKEY_EVENT_ACTION_UP occur at once as soon as a touch pointer
// as soon as a touch pointer goes up from a key. We use a simple key event queue // goes up from a key. We use a simple key event queue/ and process one event per key per frame in
// and process one event per key per ImGui frame in ImGui_ImplAndroid_NewFrame(). // ImGui_ImplAndroid_NewFrame()...or consider using IO queue, if suitable: https://github.com/ocornut/imgui/issues/2787
// ...or consider ImGui IO queue, if suitable: https://github.com/ocornut/imgui/issues/2787
case AKEY_EVENT_ACTION_DOWN: case AKEY_EVENT_ACTION_DOWN:
case AKEY_EVENT_ACTION_UP: case AKEY_EVENT_ACTION_UP:
g_KeyEventQueues[event_key_code].push(event_action); g_KeyEventQueues[event_key_code].push(event_action);
@ -68,7 +67,7 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* inputEvent)
} }
case AINPUT_EVENT_TYPE_MOTION: 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; 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; event_action &= AMOTION_EVENT_ACTION_MASK;
switch (event_action) switch (event_action)
@ -78,19 +77,17 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* inputEvent)
// Physical mouse buttons (and probably other physical devices) also invoke the actions AMOTION_EVENT_ACTION_DOWN/_UP, // 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 // 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). // 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) if((AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_FINGER)
|| (AMotionEvent_getToolType(inputEvent, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_UNKNOWN)) || (AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_UNKNOWN))
{ {
io.MouseDown[0] = (event_action == AMOTION_EVENT_ACTION_DOWN) ? true : false; io.MouseDown[0] = (event_action == AMOTION_EVENT_ACTION_DOWN) ? true : false;
io.MousePos = ImVec2( io.MousePos = ImVec2(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index));
AMotionEvent_getX(inputEvent, event_pointer_index),
AMotionEvent_getY(inputEvent, event_pointer_index));
} }
break; break;
case AMOTION_EVENT_ACTION_BUTTON_PRESS: case AMOTION_EVENT_ACTION_BUTTON_PRESS:
case AMOTION_EVENT_ACTION_BUTTON_RELEASE: 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[0] = (button_state & AMOTION_EVENT_BUTTON_PRIMARY) ? true : false;
io.MouseDown[1] = (button_state & AMOTION_EVENT_BUTTON_SECONDARY) ? true : false; io.MouseDown[1] = (button_state & AMOTION_EVENT_BUTTON_SECONDARY) ? true : false;
io.MouseDown[2] = (button_state & AMOTION_EVENT_BUTTON_TERTIARY) ? true : false; io.MouseDown[2] = (button_state & AMOTION_EVENT_BUTTON_TERTIARY) ? true : false;
@ -98,13 +95,11 @@ int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* inputEvent)
break; break;
case AMOTION_EVENT_ACTION_HOVER_MOVE: // Hovering: Tool moves while NOT pressed (such as a physical mouse) 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 case AMOTION_EVENT_ACTION_MOVE: // Touch pointer moves while DOWN
io.MousePos = ImVec2( io.MousePos = ImVec2(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index));
AMotionEvent_getX(inputEvent, event_pointer_index),
AMotionEvent_getY(inputEvent, event_pointer_index));
break; break;
case AMOTION_EVENT_ACTION_SCROLL: case AMOTION_EVENT_ACTION_SCROLL:
io.MouseWheel = AMotionEvent_getAxisValue(inputEvent, AMOTION_EVENT_AXIS_VSCROLL, event_pointer_index); io.MouseWheel = AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_VSCROLL, event_pointer_index);
io.MouseWheelH = AMotionEvent_getAxisValue(inputEvent, AMOTION_EVENT_AXIS_HSCROLL, event_pointer_index); io.MouseWheelH = AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_HSCROLL, event_pointer_index);
break; break;
default: default:
break; break;
@ -123,11 +118,11 @@ bool ImGui_ImplAndroid_Init(ANativeWindow* window)
g_Window = window; g_Window = window;
g_Time = 0.0; g_Time = 0.0;
// Setup back-end capabilities flags // Setup backend capabilities flags
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.BackendPlatformName = "imgui_impl_android"; 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_Tab] = AKEYCODE_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = AKEYCODE_DPAD_LEFT; // also covers physical keyboard arrow key 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 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() void ImGui_ImplAndroid_NewFrame()
{ {
ImGuiIO& io = ImGui::GetIO(); 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 // 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) for (auto& key_queue : g_KeyEventQueues)
{ {
if (key_queue.second.empty()) if (key_queue.second.empty())

View File

@ -3,9 +3,13 @@
// Implemented features: // Implemented features:
// [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE). // [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE).
// Missing features:
// [ ] Platform: Clipboard support. // [ ] Platform: Clipboard support.
// [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [ ] 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. // [ ] 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. // 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. // 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 ANativeWindow;
struct AInputEvent; struct AInputEvent;
IMGUI_IMPL_API int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* inputEvent);
IMGUI_IMPL_API bool ImGui_ImplAndroid_Init(ANativeWindow* window); 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_Shutdown();
IMGUI_IMPL_API void ImGui_ImplAndroid_NewFrame(); IMGUI_IMPL_API void ImGui_ImplAndroid_NewFrame();

View File

@ -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.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendPlatformName = "imgui_impl_glfw"; 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_Tab] = GLFW_KEY_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;

View File

@ -220,7 +220,8 @@ bool ImGui_Marmalade_Init(bool install_callbacks)
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.BackendPlatformName = io.BackendRendererName = "imgui_impl_marmalade"; 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_LeftArrow] = s3eKeyLeft;
io.KeyMap[ImGuiKey_RightArrow] = s3eKeyRight; io.KeyMap[ImGuiKey_RightArrow] = s3eKeyRight;
io.KeyMap[ImGuiKey_UpArrow] = s3eKeyUp; io.KeyMap[ImGuiKey_UpArrow] = s3eKeyUp;

View File

@ -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.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendPlatformName = "imgui_impl_sdl"; 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_Tab] = SDL_SCANCODE_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT; io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT; io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT;

View File

@ -731,7 +731,7 @@ void ImGui_ImplWGPU_InvalidateDeviceObjects()
bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format) 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(); ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_webgpu"; io.BackendRendererName = "imgui_impl_webgpu";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.

View File

@ -91,7 +91,7 @@ bool ImGui_ImplWin32_Init(void* hwnd)
io.BackendPlatformName = "imgui_impl_win32"; io.BackendPlatformName = "imgui_impl_win32";
io.ImeWindowHandle = hwnd; 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_Tab] = VK_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT; io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT; io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;

View File

@ -58,6 +58,7 @@ Other Changes:
- ImDrawList: AddCircle, AddCircleFilled(): Tweaked default segment count calculation to honor MaxError - 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] with more accuracy. Made default segment count always even for better looking result. (#3808) [@thedmd]
- ImDrawList: AddCircle, AddCircleFilled(): New default for style. - 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 - Backends: Win32: Added ImGui_ImplWin32_EnableAlphaCompositing() to facilitate experimenting with
alpha compositing and transparent windows. (#2766, #3447 etc.). alpha compositing and transparent windows. (#2766, #3447 etc.).
- Backends: OpenGL, Vulkan, DX9, DX10, DX11, DX12, Metal, WebGPU, Allegro: Rework blending equation to - 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] (#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 IMGUI_USE_BGRA_PACKED_COLOR. (#3844) [@Xiliusha]
- Backends: DX9: Fix to support colored glyphs, using newly introduced 'TexPixelsUseColors' info. (#3844) - 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. - 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 - CI: Use a dedicated "scheduled" workflow to trigger scheduled builds. Forks may disable this workflow if
scheduled builds builds are not required. [@rokups] scheduled builds builds are not required. [@rokups]

View File

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

View File

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

View File

@ -10,98 +10,18 @@
#include <EGL/egl.h> #include <EGL/egl.h>
#include <GLES3/gl3.h> #include <GLES3/gl3.h>
// Data
static EGLDisplay g_EglDisplay = EGL_NO_DISPLAY; static EGLDisplay g_EglDisplay = EGL_NO_DISPLAY;
static EGLSurface g_EglSurface = EGL_NO_SURFACE; static EGLSurface g_EglSurface = EGL_NO_SURFACE;
static EGLContext g_EglContext = EGL_NO_CONTEXT; static EGLContext g_EglContext = EGL_NO_CONTEXT;
static struct android_app* g_App = NULL; static struct android_app* g_App = NULL;
static bool g_Initialized = false; 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. // Forward declarations of helper functions
// Therefore, we call showSoftInput() of the main activity implemented in MainActivity.kt via JNI. static int ShowSoftKeyboardInput();
static int showSoftInput() static int PollUnicodeChars();
{ static int GetAssetData(const char* filename, void** out_data);
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;
}
void init(struct android_app* app) void init(struct android_app* app)
{ {
@ -113,30 +33,22 @@ void init(struct android_app* app)
// Initialize EGL // Initialize EGL
// This is mostly boilerplate code for 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) if (g_EglDisplay == EGL_NO_DISPLAY)
__android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglGetDisplay(EGL_DEFAULT_DISPLAY) returned 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) if (eglInitialize(g_EglDisplay, 0, 0) != EGL_TRUE)
__android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglInitialize(..) returned with an error"); __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};
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; EGLint num_configs = 0;
if (eglChooseConfig(g_EglDisplay, egl_attributes, nullptr, 0, &num_configs) != EGL_TRUE) 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"); __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglChooseConfig() returned with an error");
if (num_configs == 0) if (num_configs == 0)
__android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglChooseConfig(..) returned 0 matching configs"); __android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglChooseConfig() returned 0 matching config");
// Get the (first) matching config // Get the first matching config
EGLConfig egl_config; EGLConfig egl_config;
eglChooseConfig(g_EglDisplay, egl_attributes, &egl_config, 1, &num_configs); eglChooseConfig(g_EglDisplay, egl_attributes, &egl_config, 1, &num_configs);
EGLint egl_format; EGLint egl_format;
@ -147,31 +59,40 @@ void init(struct android_app* app)
g_EglContext = eglCreateContext(g_EglDisplay, egl_config, EGL_NO_CONTEXT, egl_context_attributes); g_EglContext = eglCreateContext(g_EglDisplay, egl_config, EGL_NO_CONTEXT, egl_context_attributes);
if (g_EglContext == EGL_NO_CONTEXT) if (g_EglContext == EGL_NO_CONTEXT)
__android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s", "eglCreateContext(..) returned 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); g_EglSurface = eglCreateWindowSurface(g_EglDisplay, egl_config, g_App->window, NULL);
eglMakeCurrent(g_EglDisplay, g_EglSurface, g_EglSurface, g_EglContext); eglMakeCurrent(g_EglDisplay, g_EglSurface, g_EglSurface, g_EglContext);
}
// Dear Imgui // Setup Dear ImGui context
IMGUI_CHECKVERSION(); IMGUI_CHECKVERSION();
ImGui::CreateContext(); ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); 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; io.IniFilename = NULL;
// Setup Dear ImGui style
ImGui::StyleColorsDark(); ImGui::StyleColorsDark();
//ImGui::StyleColorsClassic();
// Setup Platform/Renderer backends
ImGui_ImplAndroid_Init(g_App->window); ImGui_ImplAndroid_Init(g_App->window);
ImGui_ImplOpenGL3_Init("#version 300 es"); ImGui_ImplOpenGL3_Init("#version 300 es");
// Load Fonts // 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. // - 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). // - 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. // - 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. // - 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 \\ ! // - 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. // 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; ImFontConfig font_cfg;
font_cfg.SizePixels = 22.0f; font_cfg.SizePixels = 22.0f;
io.Fonts->AddFontDefault(&font_cfg); io.Fonts->AddFontDefault(&font_cfg);
@ -179,19 +100,19 @@ void init(struct android_app* app)
//int font_data_size; //int font_data_size;
//ImFont* font; //ImFont* font;
//font_data_size = GetAssetData("Roboto-Medium.ttf", &font_data); //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); //IM_ASSERT(font != NULL);
//font_data_size = GetAssetData("Cousine-Regular.ttf", &font_data); //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); //IM_ASSERT(font != NULL);
//font_data_size = GetAssetData("DroidSans.ttf", &font_data); //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); //IM_ASSERT(font != NULL);
//font_data_size = GetAssetData("ProggyTiny.ttf", &font_data); //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); //IM_ASSERT(font != NULL);
//font_data_size = GetAssetData("ArialUni.ttf", &font_data); //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); //IM_ASSERT(font != NULL);
// Arbitrary scale-up // Arbitrary scale-up
@ -203,23 +124,23 @@ void init(struct android_app* app)
void tick() 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_demo_window = true;
static bool show_another_window = false; static bool show_another_window = false;
static ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); 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 // Poll Unicode characters via JNI
// FIXME: do not call this every frame because of JNI overhead // FIXME: do not call this every frame because of JNI overhead
pollUnicodeChars(); PollUnicodeChars();
// Open on-screen (soft) input if demanded by Dear ImGui // Open on-screen (soft) input if requested by Dear ImGui
static bool WantTextInputLast = false; static bool WantTextInputLast = false;
if (io.WantTextInput && !WantTextInputLast) if (io.WantTextInput && !WantTextInputLast)
showSoftInput(); ShowSoftKeyboardInput();
WantTextInputLast = io.WantTextInput; WantTextInputLast = io.WantTextInput;
// Start the Dear ImGui frame // Start the Dear ImGui frame
@ -267,19 +188,18 @@ void tick()
// Rendering // Rendering
ImGui::Render(); ImGui::Render();
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); 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); glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
eglSwapBuffers(g_EglDisplay, g_EglSurface); eglSwapBuffers(g_EglDisplay, g_EglSurface);
} }
}
void shutdown() void shutdown()
{ {
if (!g_Initialized) if (!g_Initialized)
return; return;
// Cleanup (Dear Imgui) // Cleanup
ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplAndroid_Shutdown(); ImGui_ImplAndroid_Shutdown();
ImGui::DestroyContext(); ImGui::DestroyContext();
@ -362,3 +282,88 @@ void android_main(struct android_app* app)
tick(); 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;
}

View File

@ -2767,7 +2767,7 @@ enum ImGuiViewportFlags_
struct ImGuiViewport struct ImGuiViewport
{ {
ImGuiViewportFlags Flags; // See ImGuiViewportFlags_ 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 Size; // Main Area: Size of the viewport.
ImVec2 WorkPos; // Work Area: Position of the viewport minus task bars, menus bars, status bars (>= Pos) 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) ImVec2 WorkSize; // Work Area: Size of the viewport minus task bars, menu bars, status bars (<= Size)