imgui_impl_android.cpp (8786B)
1 // dear imgui: Platform Binding for Android native app 2 // This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3) 3 4 // Implemented features: 5 // [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE). 6 // Missing features: 7 // [ ] Platform: Clipboard support. 8 // [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 9 // [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android. 10 // Important: 11 // - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446) 12 // - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446) 13 14 // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 15 // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. 16 // https://github.com/ocornut/imgui 17 18 // CHANGELOG 19 // (minor and older changes stripped away, please see git history for details) 20 // 2021-03-04: Initial version. 21 22 #include "imgui.h" 23 #include "imgui_impl_android.h" 24 #include <time.h> 25 #include <map> 26 #include <queue> 27 #include <android/native_window.h> 28 #include <android/input.h> 29 #include <android/keycodes.h> 30 #include <android/log.h> 31 32 // Android data 33 static double g_Time = 0.0; 34 static ANativeWindow* g_Window; 35 static char g_LogTag[] = "ImGuiExample"; 36 static std::map<int32_t, std::queue<int32_t>> g_KeyEventQueues; // FIXME: Remove dependency on map and queue once we use upcoming input queue. 37 38 int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event) 39 { 40 ImGuiIO& io = ImGui::GetIO(); 41 int32_t event_type = AInputEvent_getType(input_event); 42 switch (event_type) 43 { 44 case AINPUT_EVENT_TYPE_KEY: 45 { 46 int32_t event_key_code = AKeyEvent_getKeyCode(input_event); 47 int32_t event_action = AKeyEvent_getAction(input_event); 48 int32_t event_meta_state = AKeyEvent_getMetaState(input_event); 49 50 io.KeyCtrl = ((event_meta_state & AMETA_CTRL_ON) != 0); 51 io.KeyShift = ((event_meta_state & AMETA_SHIFT_ON) != 0); 52 io.KeyAlt = ((event_meta_state & AMETA_ALT_ON) != 0); 53 54 switch (event_action) 55 { 56 // FIXME: AKEY_EVENT_ACTION_DOWN and AKEY_EVENT_ACTION_UP occur at once as soon as a touch pointer 57 // goes up from a key. We use a simple key event queue/ and process one event per key per frame in 58 // ImGui_ImplAndroid_NewFrame()...or consider using IO queue, if suitable: https://github.com/ocornut/imgui/issues/2787 59 case AKEY_EVENT_ACTION_DOWN: 60 case AKEY_EVENT_ACTION_UP: 61 g_KeyEventQueues[event_key_code].push(event_action); 62 break; 63 default: 64 break; 65 } 66 break; 67 } 68 case AINPUT_EVENT_TYPE_MOTION: 69 { 70 int32_t event_action = AMotionEvent_getAction(input_event); 71 int32_t event_pointer_index = (event_action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; 72 event_action &= AMOTION_EVENT_ACTION_MASK; 73 switch (event_action) 74 { 75 case AMOTION_EVENT_ACTION_DOWN: 76 case AMOTION_EVENT_ACTION_UP: 77 // Physical mouse buttons (and probably other physical devices) also invoke the actions AMOTION_EVENT_ACTION_DOWN/_UP, 78 // but we have to process them separately to identify the actual button pressed. This is done below via 79 // AMOTION_EVENT_ACTION_BUTTON_PRESS/_RELEASE. Here, we only process "FINGER" input (and "UNKNOWN", as a fallback). 80 if((AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_FINGER) 81 || (AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_UNKNOWN)) 82 { 83 io.MouseDown[0] = (event_action == AMOTION_EVENT_ACTION_DOWN); 84 io.MousePos = ImVec2(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index)); 85 } 86 break; 87 case AMOTION_EVENT_ACTION_BUTTON_PRESS: 88 case AMOTION_EVENT_ACTION_BUTTON_RELEASE: 89 { 90 int32_t button_state = AMotionEvent_getButtonState(input_event); 91 io.MouseDown[0] = ((button_state & AMOTION_EVENT_BUTTON_PRIMARY) != 0); 92 io.MouseDown[1] = ((button_state & AMOTION_EVENT_BUTTON_SECONDARY) != 0); 93 io.MouseDown[2] = ((button_state & AMOTION_EVENT_BUTTON_TERTIARY) != 0); 94 } 95 break; 96 case AMOTION_EVENT_ACTION_HOVER_MOVE: // Hovering: Tool moves while NOT pressed (such as a physical mouse) 97 case AMOTION_EVENT_ACTION_MOVE: // Touch pointer moves while DOWN 98 io.MousePos = ImVec2(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index)); 99 break; 100 case AMOTION_EVENT_ACTION_SCROLL: 101 io.MouseWheel = AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_VSCROLL, event_pointer_index); 102 io.MouseWheelH = AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_HSCROLL, event_pointer_index); 103 break; 104 default: 105 break; 106 } 107 } 108 return 1; 109 default: 110 break; 111 } 112 113 return 0; 114 } 115 116 bool ImGui_ImplAndroid_Init(ANativeWindow* window) 117 { 118 g_Window = window; 119 g_Time = 0.0; 120 121 // Setup backend capabilities flags 122 ImGuiIO& io = ImGui::GetIO(); 123 io.BackendPlatformName = "imgui_impl_android"; 124 125 // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. 126 io.KeyMap[ImGuiKey_Tab] = AKEYCODE_TAB; 127 io.KeyMap[ImGuiKey_LeftArrow] = AKEYCODE_DPAD_LEFT; // also covers physical keyboard arrow key 128 io.KeyMap[ImGuiKey_RightArrow] = AKEYCODE_DPAD_RIGHT; // also covers physical keyboard arrow key 129 io.KeyMap[ImGuiKey_UpArrow] = AKEYCODE_DPAD_UP; // also covers physical keyboard arrow key 130 io.KeyMap[ImGuiKey_DownArrow] = AKEYCODE_DPAD_DOWN; // also covers physical keyboard arrow key 131 io.KeyMap[ImGuiKey_PageUp] = AKEYCODE_PAGE_UP; 132 io.KeyMap[ImGuiKey_PageDown] = AKEYCODE_PAGE_DOWN; 133 io.KeyMap[ImGuiKey_Home] = AKEYCODE_MOVE_HOME; 134 io.KeyMap[ImGuiKey_End] = AKEYCODE_MOVE_END; 135 io.KeyMap[ImGuiKey_Insert] = AKEYCODE_INSERT; 136 io.KeyMap[ImGuiKey_Delete] = AKEYCODE_FORWARD_DEL; 137 io.KeyMap[ImGuiKey_Backspace] = AKEYCODE_DEL; 138 io.KeyMap[ImGuiKey_Space] = AKEYCODE_SPACE; 139 io.KeyMap[ImGuiKey_Enter] = AKEYCODE_ENTER; 140 io.KeyMap[ImGuiKey_Escape] = AKEYCODE_ESCAPE; 141 io.KeyMap[ImGuiKey_KeyPadEnter] = AKEYCODE_NUMPAD_ENTER; 142 io.KeyMap[ImGuiKey_A] = AKEYCODE_A; 143 io.KeyMap[ImGuiKey_C] = AKEYCODE_C; 144 io.KeyMap[ImGuiKey_V] = AKEYCODE_V; 145 io.KeyMap[ImGuiKey_X] = AKEYCODE_X; 146 io.KeyMap[ImGuiKey_Y] = AKEYCODE_Y; 147 io.KeyMap[ImGuiKey_Z] = AKEYCODE_Z; 148 149 return true; 150 } 151 152 void ImGui_ImplAndroid_Shutdown() 153 { 154 } 155 156 void ImGui_ImplAndroid_NewFrame() 157 { 158 ImGuiIO& io = ImGui::GetIO(); 159 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()."); 160 161 // Process queued key events 162 // 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. 163 for (auto& key_queue : g_KeyEventQueues) 164 { 165 if (key_queue.second.empty()) 166 continue; 167 io.KeysDown[key_queue.first] = (key_queue.second.front() == AKEY_EVENT_ACTION_DOWN); 168 key_queue.second.pop(); 169 } 170 171 // Setup display size (every frame to accommodate for window resizing) 172 int32_t window_width = ANativeWindow_getWidth(g_Window); 173 int32_t window_height = ANativeWindow_getHeight(g_Window); 174 int display_width = window_width; 175 int display_height = window_height; 176 177 io.DisplaySize = ImVec2((float)window_width, (float)window_height); 178 if (window_width > 0 && window_height > 0) 179 io.DisplayFramebufferScale = ImVec2((float)display_width / window_width, (float)display_height / window_height); 180 181 // Setup time step 182 struct timespec current_timespec; 183 clock_gettime(CLOCK_MONOTONIC, ¤t_timespec); 184 double current_time = (double)(current_timespec.tv_sec) + (current_timespec.tv_nsec / 1000000000.0); 185 io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f); 186 g_Time = current_time; 187 }