ImGui Input Handling for Keyboard, Mouse, and Gamepad: Architecture and Implementation

ImGui does not poll hardware directly; instead, platform backends capture native OS events and forward them to the core library via the ImGuiIO structure using functions like AddKeyEvent(), AddMousePosEvent(), and AddKeyAnalogEvent().

The ocornut/imgui library implements a deliberate abstraction where input handling remains strictly separated from UI rendering. Rather than accessing hardware directly, the core library consumes standardized input events submitted through the ImGuiIO interface, allowing the same UI code to function across Win32, GLFW, SDL, and other platforms. This architecture ensures that keyboard, mouse, and gamepad inputs are processed uniformly regardless of the underlying operating system.

The ImGuiIO Data Structure and Event API

All input state flows through ImGuiIO, declared in [imgui.h](https://github.com/ocornut/imgui/blob/master/imgui.h). This structure stores configuration flags, timing data, and the current frame's input events. Since ImGui 1.87, the modern approach requires backends to use specific event submission functions rather than modifying internal arrays directly.

Key members and methods for input handling include:

  • AddKeyEvent(key, down) – Records digital button presses for keyboard keys (ImGuiKey_A, ImGuiKey_Space) and gamepad buttons (ImGuiKey_Gamepad_Start).
  • AddKeyAnalogEvent(key, down, value) – Records analog inputs such as gamepad triggers and thumbsticks with float values between 0.0 and 1.0.
  • AddMousePosEvent(x, y) – Updates cursor position in screen coordinates.
  • AddMouseButtonEvent(button, down) – Updates mouse button state where 0 = left, 1 = right, 2 = middle.
  • AddMouseWheelEvent(wheel_x, wheel_y) – Submits horizontal and vertical scroll deltas.
  • BackendFlags – Declares backend capabilities such as ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasGamepad.
  • ConfigFlags – Enables navigation features via ImGuiConfigFlags_NavEnableKeyboard and ImGuiConfigFlags_NavEnableGamepad.

The implementation of these functions resides in [imgui.cpp](https://github.com/ocornut/imgui/blob/master/imgui.cpp) around lines 1908-1915, where events are pushed into internal circular buffers and modifier flags are automatically updated.

Platform Backend Implementation Patterns

Platform backends reside in the backends/ directory and serve as the translation layer between native OS events and ImGui's abstract input system. Each backend follows the same pattern: capture native events, translate them to ImGuiKey values, and submit via ImGuiIO methods.

Win32 Backend (imgui_impl_win32.cpp)

The Win32 implementation processes WM_* messages and XInput gamepad state:

  • Keyboard: Translates virtual-key codes (VK_*) to ImGuiKey values and calls io.AddKeyEvent(). Modifier keys use dedicated ImGuiMod_* constants.
  • Mouse: Maps WM_MOUSEMOVE to AddMousePosEvent(), WM_LBUTTONDOWN/WM_RBUTTONDOWN to AddMouseButtonEvent(), and WM_MOUSEWHEEL to AddMouseWheelEvent().
  • Gamepad: Polls XInput devices and maps buttons to ImGuiKey_Gamepad_* via the MAP_BUTTON macro around line 369. Analog sticks use AddKeyAnalogEvent() with values derived from XINPUT_GAMEPAD thumbstick data.

GLFW Backend (imgui_impl_glfw.cpp)

This cross-platform backend uses GLFW callbacks and polling functions:

  • Keyboard: Queries glfwGetKey() for each relevant key and submits via io.AddKeyEvent().
  • Mouse: Retrieves cursor position via glfwGetCursorPos() for AddMousePosEvent() and button states via glfwGetMouseButton() for AddMouseButtonEvent().
  • Gamepad: Accesses joystick state through glfwGetJoystickAxes() and glfwGetJoystickButtons(), translating to analog events around line 912.

SDL2 and SDL3 Backends (imgui_impl_sdl2.cpp, imgui_impl_sdl3.cpp)

Both SDL backends follow identical architectural patterns with API-specific implementations:

  • Keyboard: Uses SDL_GetKeyboardState() to poll key states and SDL_GetModState() for modifiers (lines 371-374 in SDL3).
  • Mouse: Processes SDL_MOUSEMOTION and SDL_MOUSEBUTTONDOWN events into corresponding AddMouse* calls.
  • Gamepad: For SDL2, uses SDL_GameControllerGetAxis() and SDL_GameControllerGetButton() (around line 461). SDL3 implements similar logic at line 757 using the updated controller API.

ImGui maintains an active navigation source that determines whether keyboard or gamepad input drives focus movement. The global state g.NavInputSource (an ImGuiInputSource enum) is updated in [imgui.cpp](https://github.com/ocornut/imgui/blob/master/imgui.cpp) at lines 13948-13957:

if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad)
    g.NavInputSource = ImGuiInputSource_Gamepad;
if (nav_keyboard_active && g.NavInputSource != ImGuiInputSource_Keyboard)
    g.NavInputSource = ImGuiInputSource_Keyboard;

The navigation system checks ImGuiWindowFlags_NoNavInputs at line 13264 to disable navigation for specific windows. When ImGuiConfigFlags_NavEnableKeyboard or ImGuiConfigFlags_NavEnableGamepad are set in io.ConfigFlags, the corresponding input source becomes eligible for navigation control.

Practical Input Implementation Example

The following example demonstrates initializing ImGui with GLFW and manually injecting synthetic input events:

#include "imgui.h"
#include "backends/imgui_impl_glfw.h"
#include "backends/imgui_impl_opengl3.h"
#include <GLFW/glfw3.h>

int main()
{
    // Initialize GLFW and ImGui context
    glfwInit();
    GLFWwindow* window = glfwCreateWindow(1280, 720, "ImGui Input Demo", nullptr, nullptr);
    glfwMakeContextCurrent(window);
    
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO();
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
    
    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init("#version 150");

    while (!glfwWindowShouldClose(window))
    {
        // Poll native events (backend populates ImGuiIO internally)
        glfwPollEvents();
        ImGui_ImplGlfw_NewFrame();
        ImGui_ImplOpenGL3_NewFrame();
        ImGui::NewFrame();

        // Inject synthetic keyboard input (Space key press)
        io.AddKeyEvent(ImGuiKey_Space, true);
        io.AddKeyEvent(ImGuiKey_Space, false);

        // Inject mouse movement and left-click
        io.AddMousePosEvent(400.0f, 300.0f);
        io.AddMouseButtonEvent(0, true);
        io.AddMouseButtonEvent(0, false);

        // Inject gamepad analog input (left stick 75% left)
        io.AddKeyAnalogEvent(ImGuiKey_Gamepad_LStickLeft, true, 0.75f);

        // Build UI
        ImGui::Begin("Input Test");
        ImGui::Text("Input events processed from multiple sources");
        ImGui::End();

        // Render
        ImGui::Render();
        // ... OpenGL rendering code ...
        glfwSwapBuffers(window);
    }
    
    // Cleanup omitted for brevity
    return 0;
}

This example illustrates that while the GLFW backend automatically populates ImGuiIO from window events, applications can inject additional synthetic events programmatically using the public Add* API.

Summary

  • ImGui uses a backend-driven architecture where platform code translates native events into standardized ImGuiIO calls rather than the core library polling hardware directly.
  • Modern input submission requires using AddKeyEvent(), AddMousePosEvent(), and AddKeyAnalogEvent() introduced in version 1.87; direct modification of KeysDown[] arrays is deprecated.
  • Navigation sources are tracked separately via g.NavInputSource in imgui.cpp, automatically switching between ImGuiInputSource_Keyboard, ImGuiInputSource_Gamepad, and ImGuiInputSource_Mouse based on recent activity.
  • Backend implementations in imgui_impl_win32.cpp, imgui_impl_glfw.cpp, and imgui_impl_sdl2.cpp demonstrate consistent patterns for mapping platform-specific input to abstract ImGui keys.

Frequently Asked Questions

How does ImGui differentiate between keyboard and gamepad navigation?

ImGui tracks the most recently active navigation source in the g.NavInputSource variable according to the logic in imgui.cpp lines 13948-13957. When nav_gamepad_active detects analog stick or gamepad button input, the source switches to ImGuiInputSource_Gamepad; keyboard activity switches it to ImGuiInputSource_Keyboard. This determines which input method controls focus navigation during the current frame.

Can I use ImGui without a platform backend?

Yes, but you must implement the input translation yourself. Your application must call ImGuiIO methods like AddKeyEvent() and AddMousePosEvent() directly from your windowing system's event callbacks. The backend files (imgui_impl_*.cpp) serve as reference implementations for Win32, GLFW, SDL, and other platforms.

Why does my gamepad not work with ImGui?

Gamepad support requires three conditions: the backend must set ImGuiBackendFlags_HasGamepad in io.BackendFlags, you must enable ImGuiConfigFlags_NavEnableGamepad in io.ConfigFlags, and the backend must poll the gamepad hardware and call AddKeyEvent() or AddKeyAnalogEvent() for gamepad buttons and axes. Check your backend implementation (lines 369 in Win32, line 912 in GLFW, or line 461 in SDL2) to ensure gamepad polling is implemented.

What is the difference between AddKeyEvent and AddKeyAnalogEvent?

AddKeyEvent(ImGuiKey key, bool down) handles digital inputs where a key is either pressed or released, suitable for keyboard keys and gamepad face buttons. AddKeyAnalogEvent(ImGuiKey key, bool down, float value) handles analog inputs like gamepad triggers and thumbsticks, where value represents the analog magnitude (typically 0.0 to 1.0). Both functions are defined in imgui.cpp around lines 1908-1915.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →