How Dear ImGui's Input System Works: Keyboard, Mouse, and Gamepad Handling
Dear ImGui receives all user input through the ImGuiIO structure, where backends queue events via Add* functions and ImGui::NewFrame() processes them into state that widgets query each frame.
The ocornut/imgui repository implements a minimal, platform-agnostic input system that unifies keyboard, mouse, and gamepad handling through a single event queue. This design allows the core library to remain OS-independent while backend implementations translate platform-specific windowing events into Dear ImGui's standard input format.
The ImGuiIO Structure: Central Input Hub
All input configuration and state lives in the ImGuiIO structure defined in imgui.h around line 2425. This structure serves as the bridge between your application and Dear ImGui's internal systems.
Key input-related members include:
MousePos,MouseDelta,MouseDown[5],MouseWheel– Mouse state updated each frameKeysDown[]andKeysData[]– Keyboard key states indexed byImGuiKeyvaluesNavInputs[]– Gamepad and navigation controller analog valuesKeyMap[]– Array mappingImGuiKey_*identifiers to native key codesWantCaptureMouse/WantCaptureKeyboard– Flags indicating whether Dear ImGui consumed the input
The backend populates these fields indirectly by calling event addition functions, while the UI queries them directly through public API functions like ImGui::IsKeyPressed() and ImGui::IsMouseClicked().
Input Event Architecture
Dear ImGui uses a queued event system rather than immediate state modification. This ensures thread safety and consistent input handling across different backends.
The Event Queue Flow
Input flows through three distinct stages:
- Platform Event Receipt – The OS delivers raw input (Windows messages, GLFW callbacks, SDL events)
- Backend Translation – Backend code calls
ImGuiIOAdd functions* to queue standardized events - Frame Processing –
ImGui::NewFrame()(implemented inimgui.cpparound line 4808) consumes the queue and updates the live state
The main Add functions* declared in imgui.h around line 2555 include:
AddMousePosEvent(float x, float y)AddMouseButtonEvent(int button, bool down)AddMouseWheelEvent(float wheel_x, float wheel_y)AddKeyEvent(ImGuiKey key, bool down)AddKeyAnalogEvent(ImGuiKey key, bool down, float v)– Used for gamepad triggers and sticksAddInputCharacter(unsigned int c)– For text input
These implementations reside in imgui.cpp around line 4626, where they push events into an internal ring buffer.
Keyboard Input Handling
Keyboard handling relies on the ImGuiKey enum defined around line 3300 in imgui.h, which provides logical identifiers for every key (e.g., ImGuiKey_A, ImGuiKey_LeftArrow, ImGuiKey_LeftCtrl).
Key Mapping Configuration
Backends must populate the io.KeyMap[] array to translate native key codes into Dear ImGui's logical keys. For example, in imgui_impl_glfw.cpp:
ImGuiIO& io = ImGui::GetIO();
io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
Key State Updates
When a key event occurs, the backend calls io.AddKeyEvent():
// In imgui_impl_glfw.cpp key callback
io.AddKeyEvent(ImGuiKey_Escape, (action == GLFW_PRESS));
After NewFrame() processes the queue, you can query key states:
if (ImGui::IsKeyDown(ImGuiKey_Escape))
// Handle escape key held
if (ImGui::IsKeyPressed(ImGuiKey_Enter))
// Handle enter key pressed (with repeat)
The KeysData[] array in imgui_internal.h (around line 2220) stores per-key timing information used by the repeat logic, controlled by io.KeyRepeatDelay and io.KeyRepeatRate.
Mouse Input Handling
Mouse state accumulates through discrete events that NewFrame() reconciles into continuous state.
Position and Delta
The AddMousePosEvent(float x, float y) function updates the internal queue. After processing, io.MousePos contains the latest coordinates, while io.MouseDelta contains the frame-to-frame movement delta.
Button States
Mouse buttons use zero-based indexing: 0 (left), 1 (right), 2 (middle), 3 (extra1), 4 (extra2). Backends call:
io.AddMouseButtonEvent(0, true); // Left button pressed
io.AddMouseButtonEvent(0, false); // Left button released
The resulting state resides in io.MouseDown[], where io.MouseDown[0] indicates whether the left button is currently held.
Scroll and Cursor
Wheel events feed into io.MouseWheel (vertical) and io.MouseWheelH (horizontal) via AddMouseWheelEvent. The io.MouseDrawCursor flag allows Dear ImGui to render its own cursor, while io.WantCaptureMouse tells the host application whether to hide the system cursor or pass events to underlying game logic.
Gamepad Input Handling
Gamepad support integrates with the navigation system through analog-aware key events and dedicated navigation inputs.
Gamepad Keys and Analog Input
Gamepad buttons and axes use the ImGuiKey_Gamepad_* enum values (defined around line 3325 in imgui.h), such as ImGuiKey_Gamepad_DpadLeft or ImGuiKey_Gamepad_FaceLeft.
For analog triggers and sticks, backends use AddKeyAnalogEvent:
// From imgui_impl_sdl2.cpp
io.AddKeyAnalogEvent(ImGuiKey_Gamepad_L2, (gamepad_axis_l2 > 0), gamepad_axis_l2);
io.AddKeyAnalogEvent(ImGuiKey_Gamepad_LStickLeft, (gamepad_axis_left_x < -0.1f), -gamepad_axis_left_x);
Navigation Inputs
The io.NavInputs[] array (indexed by ImGuiNavInput_* enum values defined around line 3340) stores processed analog values for navigation-concerned inputs like ImGuiNavInput_DpadLeft or ImGuiNavInput_LStickLeft. The navigation system consumes these during NewFrame() to determine focus movement.
Enable gamepad navigation by setting:
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
Backend Integration Examples
Dear ImGui provides reference implementations in the backends/ directory demonstrating platform-specific integration.
GLFW Backend
imgui_impl_glfw.cpp (around line 150) demonstrates GLFW callback translation:
void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
{
ImGuiIO& io = ImGui::GetIO();
if (action == GLFW_PRESS)
io.AddMouseButtonEvent(button, true);
else if (action == GLFW_RELEASE)
io.AddMouseButtonEvent(button, false);
}
SDL2 Backend
imgui_impl_sdl2.cpp (around line 380) handles SDL events including gamepad connections:
case SDL_MOUSEMOTION:
io.AddMousePosEvent((float)event.motion.x, (float)event.motion.y);
break;
case SDL_KEYDOWN:
io.AddKeyEvent(ImGui_ImplSDL2_KeycodeToImGuiKey(event.key.keysym.sym), true);
break;
Win32 Backend
imgui_impl_win32.cpp (around line 210) processes Windows messages (WM_KEYDOWN, WM_MOUSEMOVE) and manages IME (Input Method Editor) state for international text input.
Manual Input Implementation
If implementing a custom backend or bypassing existing ones, you can populate the input queue directly:
ImGuiIO& io = ImGui::GetIO();
// Poll your OS or input library
while (PollNativeEvents())
{
// Mouse position
if (event.type == MOUSE_MOVE)
io.AddMousePosEvent(event.x, event.y);
// Mouse buttons
if (event.type == MOUSE_BUTTON)
io.AddMouseButtonEvent(event.button, event.pressed);
// Keyboard
if (event.type == KEY_DOWN)
io.AddKeyEvent(TranslateToImGuiKey(event.key), true);
if (event.type == KEY_UP)
io.AddKeyEvent(TranslateToImGuiKey(event.key), false);
// Text characters
if (event.type == TEXT_INPUT)
io.AddInputCharacter(event.unicode);
}
ImGui::NewFrame(); // Processes all queued events
// ... Draw UI widgets ...
ImGui::Render();
Summary
- Dear ImGui's input system centers on the
ImGuiIOstructure, which acts as a broker between platform-specific backends and the UI framework. - Event queuing via
AddMousePosEvent,AddKeyEvent, andAddKeyAnalogEventensures thread-safe input collection beforeNewFrame()processing. - Backend responsibility involves translating OS windowing events (GLFW, SDL2, Win32) into Dear ImGui's logical key and button identifiers.
- Unified handling treats keyboard, mouse, and gamepad input uniformly through the same
ImGuiKeyenum and navigation system. - Query API functions like
IsKeyPressed()andIsMouseClicked()read the processed state afterNewFrame()updates the internal structures.
Frequently Asked Questions
How do I check if Dear ImGui wants to capture mouse input?
Check the io.WantCaptureMouse boolean after calling ImGui::NewFrame(). If this value is true, Dear ImGui is interacting with a widget (hovering, clicking, or dragging), and your underlying application should not process the mouse input. Similarly, io.WantCaptureKeyboard indicates when text input or navigation is active.
Can I use Dear ImGui with a gamepad only, without a mouse?
Yes. Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad before NewFrame(). Your backend must populate gamepad events using AddKeyEvent and AddKeyAnalogEvent with ImGuiKey_Gamepad_* values. The navigation system will automatically map the D-pad and left stick to move focus between widgets, and face buttons to activate them.
What is the difference between AddKeyEvent and the old KeysDown array?
Modern Dear ImGui versions use AddKeyEvent() to queue changes, which NewFrame() processes into the internal KeysData[] structure (defined in imgui_internal.h). While io.KeysDown[] still exists for legacy compatibility, the event-based API provides better support for key repeat handling, analog values, and thread safety. Backends migrating from direct array manipulation should switch to the Add* functions.
How do I handle high-DPI mouse coordinates in Dear ImGui?
Always pass physical pixel coordinates (not logical display coordinates) to AddMousePosEvent. If your OS or windowing library scales coordinates for high-DPI displays, divide by the scale factor before calling Dear ImGui functions, or set io.DisplayFramebufferScale appropriately so that MousePos values match your actual render target resolution.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →