2021-03-04 12:35:44 +03:00
// dear imgui: Platform Binding for Android native app
// This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3)
// Implemented features:
2022-01-06 21:57:34 +03:00
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
2023-04-11 11:01:53 +03:00
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
2021-03-04 13:03:40 +03:00
// Missing features:
2021-03-04 12:35:44 +03:00
// [ ] 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.
2021-03-04 13:03:40 +03:00
// Important:
2022-01-06 21:57:34 +03:00
// - Consider using SDL or GLFW backend on Android, which will be more full-featured than this.
2021-03-04 13:03:40 +03:00
// - 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)
2021-03-04 12:35:44 +03:00
2021-12-21 20:25:46 +03:00
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
2021-05-27 14:59:35 +03:00
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
2021-03-04 12:35:44 +03:00
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
2022-09-22 19:58:33 +03:00
// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
2022-01-17 19:22:37 +03:00
// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
2022-01-10 18:59:31 +03:00
// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
2021-03-04 13:03:40 +03:00
// 2021-03-04: Initial version.
2021-03-04 12:35:44 +03:00
# include "imgui.h"
# include "imgui_impl_android.h"
# include <time.h>
# include <android/native_window.h>
# include <android/input.h>
# include <android/keycodes.h>
# include <android/log.h>
2021-03-04 13:03:40 +03:00
// Android data
2021-03-04 12:35:44 +03:00
static double g_Time = 0.0 ;
static ANativeWindow * g_Window ;
2021-03-04 13:03:40 +03:00
static char g_LogTag [ ] = " ImGuiExample " ;
2022-01-06 21:57:34 +03:00
static ImGuiKey ImGui_ImplAndroid_KeyCodeToImGuiKey ( int32_t key_code )
{
switch ( key_code )
{
case AKEYCODE_TAB : return ImGuiKey_Tab ;
case AKEYCODE_DPAD_LEFT : return ImGuiKey_LeftArrow ;
case AKEYCODE_DPAD_RIGHT : return ImGuiKey_RightArrow ;
case AKEYCODE_DPAD_UP : return ImGuiKey_UpArrow ;
case AKEYCODE_DPAD_DOWN : return ImGuiKey_DownArrow ;
case AKEYCODE_PAGE_UP : return ImGuiKey_PageUp ;
case AKEYCODE_PAGE_DOWN : return ImGuiKey_PageDown ;
case AKEYCODE_MOVE_HOME : return ImGuiKey_Home ;
case AKEYCODE_MOVE_END : return ImGuiKey_End ;
case AKEYCODE_INSERT : return ImGuiKey_Insert ;
case AKEYCODE_FORWARD_DEL : return ImGuiKey_Delete ;
case AKEYCODE_DEL : return ImGuiKey_Backspace ;
case AKEYCODE_SPACE : return ImGuiKey_Space ;
case AKEYCODE_ENTER : return ImGuiKey_Enter ;
case AKEYCODE_ESCAPE : return ImGuiKey_Escape ;
case AKEYCODE_APOSTROPHE : return ImGuiKey_Apostrophe ;
case AKEYCODE_COMMA : return ImGuiKey_Comma ;
case AKEYCODE_MINUS : return ImGuiKey_Minus ;
case AKEYCODE_PERIOD : return ImGuiKey_Period ;
case AKEYCODE_SLASH : return ImGuiKey_Slash ;
case AKEYCODE_SEMICOLON : return ImGuiKey_Semicolon ;
case AKEYCODE_EQUALS : return ImGuiKey_Equal ;
case AKEYCODE_LEFT_BRACKET : return ImGuiKey_LeftBracket ;
case AKEYCODE_BACKSLASH : return ImGuiKey_Backslash ;
case AKEYCODE_RIGHT_BRACKET : return ImGuiKey_RightBracket ;
case AKEYCODE_GRAVE : return ImGuiKey_GraveAccent ;
case AKEYCODE_CAPS_LOCK : return ImGuiKey_CapsLock ;
case AKEYCODE_SCROLL_LOCK : return ImGuiKey_ScrollLock ;
case AKEYCODE_NUM_LOCK : return ImGuiKey_NumLock ;
case AKEYCODE_SYSRQ : return ImGuiKey_PrintScreen ;
case AKEYCODE_BREAK : return ImGuiKey_Pause ;
case AKEYCODE_NUMPAD_0 : return ImGuiKey_Keypad0 ;
case AKEYCODE_NUMPAD_1 : return ImGuiKey_Keypad1 ;
case AKEYCODE_NUMPAD_2 : return ImGuiKey_Keypad2 ;
case AKEYCODE_NUMPAD_3 : return ImGuiKey_Keypad3 ;
case AKEYCODE_NUMPAD_4 : return ImGuiKey_Keypad4 ;
case AKEYCODE_NUMPAD_5 : return ImGuiKey_Keypad5 ;
case AKEYCODE_NUMPAD_6 : return ImGuiKey_Keypad6 ;
case AKEYCODE_NUMPAD_7 : return ImGuiKey_Keypad7 ;
case AKEYCODE_NUMPAD_8 : return ImGuiKey_Keypad8 ;
case AKEYCODE_NUMPAD_9 : return ImGuiKey_Keypad9 ;
case AKEYCODE_NUMPAD_DOT : return ImGuiKey_KeypadDecimal ;
case AKEYCODE_NUMPAD_DIVIDE : return ImGuiKey_KeypadDivide ;
case AKEYCODE_NUMPAD_MULTIPLY : return ImGuiKey_KeypadMultiply ;
case AKEYCODE_NUMPAD_SUBTRACT : return ImGuiKey_KeypadSubtract ;
case AKEYCODE_NUMPAD_ADD : return ImGuiKey_KeypadAdd ;
case AKEYCODE_NUMPAD_ENTER : return ImGuiKey_KeypadEnter ;
case AKEYCODE_NUMPAD_EQUALS : return ImGuiKey_KeypadEqual ;
2022-01-17 17:41:04 +03:00
case AKEYCODE_CTRL_LEFT : return ImGuiKey_LeftCtrl ;
2022-01-06 21:57:34 +03:00
case AKEYCODE_SHIFT_LEFT : return ImGuiKey_LeftShift ;
case AKEYCODE_ALT_LEFT : return ImGuiKey_LeftAlt ;
case AKEYCODE_META_LEFT : return ImGuiKey_LeftSuper ;
2022-01-17 17:41:04 +03:00
case AKEYCODE_CTRL_RIGHT : return ImGuiKey_RightCtrl ;
2022-01-06 21:57:34 +03:00
case AKEYCODE_SHIFT_RIGHT : return ImGuiKey_RightShift ;
case AKEYCODE_ALT_RIGHT : return ImGuiKey_RightAlt ;
case AKEYCODE_META_RIGHT : return ImGuiKey_RightSuper ;
case AKEYCODE_MENU : return ImGuiKey_Menu ;
case AKEYCODE_0 : return ImGuiKey_0 ;
case AKEYCODE_1 : return ImGuiKey_1 ;
case AKEYCODE_2 : return ImGuiKey_2 ;
case AKEYCODE_3 : return ImGuiKey_3 ;
case AKEYCODE_4 : return ImGuiKey_4 ;
case AKEYCODE_5 : return ImGuiKey_5 ;
case AKEYCODE_6 : return ImGuiKey_6 ;
case AKEYCODE_7 : return ImGuiKey_7 ;
case AKEYCODE_8 : return ImGuiKey_8 ;
case AKEYCODE_9 : return ImGuiKey_9 ;
case AKEYCODE_A : return ImGuiKey_A ;
case AKEYCODE_B : return ImGuiKey_B ;
case AKEYCODE_C : return ImGuiKey_C ;
case AKEYCODE_D : return ImGuiKey_D ;
case AKEYCODE_E : return ImGuiKey_E ;
case AKEYCODE_F : return ImGuiKey_F ;
case AKEYCODE_G : return ImGuiKey_G ;
case AKEYCODE_H : return ImGuiKey_H ;
case AKEYCODE_I : return ImGuiKey_I ;
case AKEYCODE_J : return ImGuiKey_J ;
case AKEYCODE_K : return ImGuiKey_K ;
case AKEYCODE_L : return ImGuiKey_L ;
case AKEYCODE_M : return ImGuiKey_M ;
case AKEYCODE_N : return ImGuiKey_N ;
case AKEYCODE_O : return ImGuiKey_O ;
case AKEYCODE_P : return ImGuiKey_P ;
case AKEYCODE_Q : return ImGuiKey_Q ;
case AKEYCODE_R : return ImGuiKey_R ;
case AKEYCODE_S : return ImGuiKey_S ;
case AKEYCODE_T : return ImGuiKey_T ;
case AKEYCODE_U : return ImGuiKey_U ;
case AKEYCODE_V : return ImGuiKey_V ;
case AKEYCODE_W : return ImGuiKey_W ;
case AKEYCODE_X : return ImGuiKey_X ;
case AKEYCODE_Y : return ImGuiKey_Y ;
case AKEYCODE_Z : return ImGuiKey_Z ;
case AKEYCODE_F1 : return ImGuiKey_F1 ;
case AKEYCODE_F2 : return ImGuiKey_F2 ;
case AKEYCODE_F3 : return ImGuiKey_F3 ;
case AKEYCODE_F4 : return ImGuiKey_F4 ;
case AKEYCODE_F5 : return ImGuiKey_F5 ;
case AKEYCODE_F6 : return ImGuiKey_F6 ;
case AKEYCODE_F7 : return ImGuiKey_F7 ;
case AKEYCODE_F8 : return ImGuiKey_F8 ;
case AKEYCODE_F9 : return ImGuiKey_F9 ;
case AKEYCODE_F10 : return ImGuiKey_F10 ;
case AKEYCODE_F11 : return ImGuiKey_F11 ;
case AKEYCODE_F12 : return ImGuiKey_F12 ;
default : return ImGuiKey_None ;
}
}
2021-03-04 12:35:44 +03:00
2021-03-04 13:03:40 +03:00
int32_t ImGui_ImplAndroid_HandleInputEvent ( AInputEvent * input_event )
2021-03-04 12:35:44 +03:00
{
ImGuiIO & io = ImGui : : GetIO ( ) ;
2021-03-04 13:03:40 +03:00
int32_t event_type = AInputEvent_getType ( input_event ) ;
2021-03-04 12:35:44 +03:00
switch ( event_type )
{
case AINPUT_EVENT_TYPE_KEY :
{
2021-03-04 13:03:40 +03:00
int32_t event_key_code = AKeyEvent_getKeyCode ( input_event ) ;
2022-01-06 21:57:34 +03:00
int32_t event_scan_code = AKeyEvent_getScanCode ( input_event ) ;
2021-03-04 13:03:40 +03:00
int32_t event_action = AKeyEvent_getAction ( input_event ) ;
int32_t event_meta_state = AKeyEvent_getMetaState ( input_event ) ;
2021-03-04 12:35:44 +03:00
2022-09-22 19:58:33 +03:00
io . AddKeyEvent ( ImGuiMod_Ctrl , ( event_meta_state & AMETA_CTRL_ON ) ! = 0 ) ;
io . AddKeyEvent ( ImGuiMod_Shift , ( event_meta_state & AMETA_SHIFT_ON ) ! = 0 ) ;
io . AddKeyEvent ( ImGuiMod_Alt , ( event_meta_state & AMETA_ALT_ON ) ! = 0 ) ;
io . AddKeyEvent ( ImGuiMod_Super , ( event_meta_state & AMETA_META_ON ) ! = 0 ) ;
2021-03-04 12:35:44 +03:00
switch ( event_action )
{
2021-03-04 13:03:40 +03:00
// 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
2021-03-04 12:35:44 +03:00
case AKEY_EVENT_ACTION_DOWN :
case AKEY_EVENT_ACTION_UP :
2022-01-06 21:57:34 +03:00
{
ImGuiKey key = ImGui_ImplAndroid_KeyCodeToImGuiKey ( event_key_code ) ;
if ( key ! = ImGuiKey_None & & ( event_action = = AKEY_EVENT_ACTION_DOWN | | event_action = = AKEY_EVENT_ACTION_UP ) )
{
2022-01-27 00:00:50 +03:00
io . AddKeyEvent ( key , event_action = = AKEY_EVENT_ACTION_DOWN ) ;
io . SetKeyEventNativeData ( key , event_key_code , event_scan_code ) ;
2022-01-06 21:57:34 +03:00
}
2021-03-04 12:35:44 +03:00
break ;
2022-01-06 21:57:34 +03:00
}
2021-03-04 12:35:44 +03:00
default :
break ;
}
break ;
}
case AINPUT_EVENT_TYPE_MOTION :
{
2021-03-04 13:03:40 +03:00
int32_t event_action = AMotionEvent_getAction ( input_event ) ;
2021-03-04 12:35:44 +03:00
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 ;
2023-04-11 11:01:53 +03:00
switch ( AMotionEvent_getToolType ( input_event , event_pointer_index ) )
{
case AMOTION_EVENT_TOOL_TYPE_MOUSE :
io . AddMouseSourceEvent ( ImGuiMouseSource_Mouse ) ;
break ;
case AMOTION_EVENT_TOOL_TYPE_STYLUS :
case AMOTION_EVENT_TOOL_TYPE_ERASER :
io . AddMouseSourceEvent ( ImGuiMouseSource_Pen ) ;
break ;
case AMOTION_EVENT_TOOL_TYPE_FINGER :
default :
io . AddMouseSourceEvent ( ImGuiMouseSource_TouchScreen ) ;
break ;
}
2021-03-04 12:35:44 +03:00
switch ( event_action )
{
case AMOTION_EVENT_ACTION_DOWN :
case AMOTION_EVENT_ACTION_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
// AMOTION_EVENT_ACTION_BUTTON_PRESS/_RELEASE. Here, we only process "FINGER" input (and "UNKNOWN", as a fallback).
2021-03-04 13:03:40 +03:00
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 ) )
2021-03-04 12:35:44 +03:00
{
2022-01-17 19:22:37 +03:00
io . AddMousePosEvent ( AMotionEvent_getX ( input_event , event_pointer_index ) , AMotionEvent_getY ( input_event , event_pointer_index ) ) ;
io . AddMouseButtonEvent ( 0 , event_action = = AMOTION_EVENT_ACTION_DOWN ) ;
2021-03-04 12:35:44 +03:00
}
break ;
case AMOTION_EVENT_ACTION_BUTTON_PRESS :
case AMOTION_EVENT_ACTION_BUTTON_RELEASE :
{
2021-03-04 13:03:40 +03:00
int32_t button_state = AMotionEvent_getButtonState ( input_event ) ;
2022-01-17 19:22:37 +03:00
io . AddMouseButtonEvent ( 0 , ( button_state & AMOTION_EVENT_BUTTON_PRIMARY ) ! = 0 ) ;
io . AddMouseButtonEvent ( 1 , ( button_state & AMOTION_EVENT_BUTTON_SECONDARY ) ! = 0 ) ;
io . AddMouseButtonEvent ( 2 , ( button_state & AMOTION_EVENT_BUTTON_TERTIARY ) ! = 0 ) ;
2021-03-04 12:35:44 +03:00
}
break ;
case AMOTION_EVENT_ACTION_HOVER_MOVE : // Hovering: Tool moves while NOT pressed (such as a physical mouse)
2021-03-04 13:03:40 +03:00
case AMOTION_EVENT_ACTION_MOVE : // Touch pointer moves while DOWN
2022-01-17 19:22:37 +03:00
io . AddMousePosEvent ( AMotionEvent_getX ( input_event , event_pointer_index ) , AMotionEvent_getY ( input_event , event_pointer_index ) ) ;
2021-03-04 12:35:44 +03:00
break ;
case AMOTION_EVENT_ACTION_SCROLL :
2022-01-17 19:22:37 +03:00
io . AddMouseWheelEvent ( AMotionEvent_getAxisValue ( input_event , AMOTION_EVENT_AXIS_HSCROLL , event_pointer_index ) , AMotionEvent_getAxisValue ( input_event , AMOTION_EVENT_AXIS_VSCROLL , event_pointer_index ) ) ;
2021-03-04 12:35:44 +03:00
break ;
default :
break ;
}
}
return 1 ;
default :
break ;
}
return 0 ;
}
bool ImGui_ImplAndroid_Init ( ANativeWindow * window )
{
g_Window = window ;
g_Time = 0.0 ;
2021-03-04 13:03:40 +03:00
// Setup backend capabilities flags
2021-03-04 12:35:44 +03:00
ImGuiIO & io = ImGui : : GetIO ( ) ;
io . BackendPlatformName = " imgui_impl_android " ;
return true ;
}
void ImGui_ImplAndroid_Shutdown ( )
{
}
void ImGui_ImplAndroid_NewFrame ( )
{
ImGuiIO & io = ImGui : : GetIO ( ) ;
// Setup display size (every frame to accommodate for window resizing)
int32_t window_width = ANativeWindow_getWidth ( g_Window ) ;
int32_t window_height = ANativeWindow_getHeight ( g_Window ) ;
int display_width = window_width ;
int display_height = window_height ;
io . DisplaySize = ImVec2 ( ( float ) window_width , ( float ) window_height ) ;
if ( window_width > 0 & & window_height > 0 )
io . DisplayFramebufferScale = ImVec2 ( ( float ) display_width / window_width , ( float ) display_height / window_height ) ;
// Setup time step
struct timespec current_timespec ;
clock_gettime ( CLOCK_MONOTONIC , & current_timespec ) ;
double current_time = ( double ) ( current_timespec . tv_sec ) + ( current_timespec . tv_nsec / 1000000000.0 ) ;
io . DeltaTime = g_Time > 0.0 ? ( float ) ( current_time - g_Time ) : ( float ) ( 1.0f / 60.0f ) ;
g_Time = current_time ;
}