2021-03-04 10:35:44 +01: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 19:57:34 +01: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]
2021-03-04 11:03:40 +01:00
// Missing features:
2021-03-04 10:35:44 +01: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 11:03:40 +01:00
// Important:
2022-01-06 19:57:34 +01:00
// - Consider using SDL or GLFW backend on Android, which will be more full-featured than this.
2021-03-04 11:03:40 +01: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 10:35:44 +01:00
2021-12-21 18:25:46 +01:00
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
2021-05-27 13:59:35 +02: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 10:35:44 +01:00
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
2022-01-06 19:57:34 +01:00
// 2022-01-10: Inputs: calling new io.AddKeyEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
2021-03-04 11:03:40 +01:00
// 2021-03-04: Initial version.
2021-03-04 10:35:44 +01:00
# include "imgui.h"
# include "imgui_impl_android.h"
# include <time.h>
# include <map>
# include <queue>
# include <android/native_window.h>
# include <android/input.h>
# include <android/keycodes.h>
# include <android/log.h>
2022-01-06 19:57:34 +01:00
struct KeyEvent
{
ImGuiKey Key ;
bool Down ;
int NativeKeycode ;
int NativeScancode ;
KeyEvent ( ) : Key ( ImGuiKey_None ) , Down ( false ) , NativeKeycode ( - 1 ) , NativeScancode ( - 1 ) { }
} ;
2021-03-04 11:03:40 +01:00
// Android data
2021-03-04 10:35:44 +01:00
static double g_Time = 0.0 ;
static ANativeWindow * g_Window ;
2021-03-04 11:03:40 +01:00
static char g_LogTag [ ] = " ImGuiExample " ;
2022-01-06 19:57:34 +01:00
static std : : map < ImGuiKey , std : : queue < KeyEvent > > g_KeyEventQueues ; // FIXME: Remove dependency on map and queue once we use upcoming input queue.
static ImGuiKeyModFlags g_KeyModFlags = ImGuiKeyModFlags_None ;
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 ;
case AKEYCODE_SHIFT_LEFT : return ImGuiKey_LeftShift ;
case AKEYCODE_CTRL_LEFT : return ImGuiKey_LeftControl ;
case AKEYCODE_ALT_LEFT : return ImGuiKey_LeftAlt ;
case AKEYCODE_META_LEFT : return ImGuiKey_LeftSuper ;
case AKEYCODE_SHIFT_RIGHT : return ImGuiKey_RightShift ;
case AKEYCODE_CTRL_RIGHT : return ImGuiKey_RightControl ;
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 10:35:44 +01:00
2021-03-04 11:03:40 +01:00
int32_t ImGui_ImplAndroid_HandleInputEvent ( AInputEvent * input_event )
2021-03-04 10:35:44 +01:00
{
ImGuiIO & io = ImGui : : GetIO ( ) ;
2021-03-04 11:03:40 +01:00
int32_t event_type = AInputEvent_getType ( input_event ) ;
2021-03-04 10:35:44 +01:00
switch ( event_type )
{
case AINPUT_EVENT_TYPE_KEY :
{
2021-03-04 11:03:40 +01:00
int32_t event_key_code = AKeyEvent_getKeyCode ( input_event ) ;
2022-01-06 19:57:34 +01:00
int32_t event_scan_code = AKeyEvent_getScanCode ( input_event ) ;
2021-03-04 11:03:40 +01:00
int32_t event_action = AKeyEvent_getAction ( input_event ) ;
int32_t event_meta_state = AKeyEvent_getMetaState ( input_event ) ;
2021-03-04 10:35:44 +01:00
2022-01-06 19:57:34 +01:00
g_KeyModFlags = ImGuiKeyModFlags_None ;
if ( ( event_meta_state & AMETA_CTRL_ON ) ! = 0 )
g_KeyModFlags | = ImGuiKeyModFlags_Ctrl ;
if ( ( event_meta_state & AMETA_SHIFT_ON ) ! = 0 )
g_KeyModFlags | = ImGuiKeyModFlags_Shift ;
if ( ( event_meta_state & AMETA_ALT_ON ) ! = 0 )
g_KeyModFlags | = ImGuiKeyModFlags_Alt ;
if ( ( event_meta_state & AMETA_META_ON ) ! = 0 )
g_KeyModFlags | = ImGuiKeyModFlags_Super ;
2021-03-04 10:35:44 +01:00
switch ( event_action )
{
2021-03-04 11:03:40 +01: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 10:35:44 +01:00
case AKEY_EVENT_ACTION_DOWN :
case AKEY_EVENT_ACTION_UP :
2022-01-06 19:57:34 +01: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 ) )
{
KeyEvent io_event ;
io_event . Key = key ;
io_event . Down = event_action = = AKEY_EVENT_ACTION_DOWN ;
io_event . NativeKeycode = event_key_code ;
io_event . NativeScancode = event_scan_code ;
g_KeyEventQueues [ key ] . push ( io_event ) ;
}
2021-03-04 10:35:44 +01:00
break ;
2022-01-06 19:57:34 +01:00
}
2021-03-04 10:35:44 +01:00
default :
break ;
}
break ;
}
case AINPUT_EVENT_TYPE_MOTION :
{
2021-03-04 11:03:40 +01:00
int32_t event_action = AMotionEvent_getAction ( input_event ) ;
2021-03-04 10:35:44 +01: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 ;
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 11:03:40 +01: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 10:35:44 +01:00
{
2021-05-07 17:43:42 +02:00
io . MouseDown [ 0 ] = ( event_action = = AMOTION_EVENT_ACTION_DOWN ) ;
2021-03-04 11:03:40 +01:00
io . MousePos = ImVec2 ( AMotionEvent_getX ( input_event , event_pointer_index ) , AMotionEvent_getY ( input_event , event_pointer_index ) ) ;
2021-03-04 10:35:44 +01:00
}
break ;
case AMOTION_EVENT_ACTION_BUTTON_PRESS :
case AMOTION_EVENT_ACTION_BUTTON_RELEASE :
{
2021-03-04 11:03:40 +01:00
int32_t button_state = AMotionEvent_getButtonState ( input_event ) ;
2021-05-07 17:43:42 +02:00
io . MouseDown [ 0 ] = ( ( button_state & AMOTION_EVENT_BUTTON_PRIMARY ) ! = 0 ) ;
io . MouseDown [ 1 ] = ( ( button_state & AMOTION_EVENT_BUTTON_SECONDARY ) ! = 0 ) ;
io . MouseDown [ 2 ] = ( ( button_state & AMOTION_EVENT_BUTTON_TERTIARY ) ! = 0 ) ;
2021-03-04 10:35:44 +01:00
}
break ;
case AMOTION_EVENT_ACTION_HOVER_MOVE : // Hovering: Tool moves while NOT pressed (such as a physical mouse)
2021-03-04 11:03:40 +01:00
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 ) ) ;
2021-03-04 10:35:44 +01:00
break ;
case AMOTION_EVENT_ACTION_SCROLL :
2021-03-04 11:03:40 +01:00
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 ) ;
2021-03-04 10:35:44 +01: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 11:03:40 +01:00
// Setup backend capabilities flags
2021-03-04 10:35:44 +01:00
ImGuiIO & io = ImGui : : GetIO ( ) ;
io . BackendPlatformName = " imgui_impl_android " ;
return true ;
}
void ImGui_ImplAndroid_Shutdown ( )
{
}
void ImGui_ImplAndroid_NewFrame ( )
{
ImGuiIO & io = ImGui : : GetIO ( ) ;
// Process queued key events
2021-03-04 11:03:40 +01:00
// 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.
2021-03-04 10:35:44 +01:00
for ( auto & key_queue : g_KeyEventQueues )
{
if ( key_queue . second . empty ( ) )
continue ;
2022-01-06 19:57:34 +01:00
auto & key_event = key_queue . second . front ( ) ;
io . AddKeyEvent ( key_event . Key , key_event . Down ) ;
io . SetKeyEventNativeData ( key_event . Key , key_event . NativeKeycode , key_event . NativeScancode ) ; // To support legacy indexing (<1.87 user code)
2021-03-04 10:35:44 +01:00
key_queue . second . pop ( ) ;
}
2022-01-06 19:57:34 +01:00
io . KeyCtrl = ( ( g_KeyModFlags & ImGuiKeyModFlags_Ctrl ) ! = 0 ) ;
io . KeyShift = ( ( g_KeyModFlags & ImGuiKeyModFlags_Shift ) ! = 0 ) ;
io . KeyAlt = ( ( g_KeyModFlags & ImGuiKeyModFlags_Alt ) ! = 0 ) ;
io . KeySuper = ( ( g_KeyModFlags & ImGuiKeyModFlags_Super ) ! = 0 ) ;
2021-03-04 10:35:44 +01:00
// 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 ;
}