# How to Implement Gamepad Navigation Input Handlers in Dear ImGui

> Learn how to implement gamepad navigation input handlers in Dear ImGui. This guide explains how to feed events to ImGuiIO for seamless controller support.

- Repository: [omar/imgui](https://github.com/ocornut/imgui)
- Tags: how-to-guide
- Published: 2026-07-16

---

**Dear ImGui consumes gamepad input through the `ImGuiIO` interface when the `ImGuiConfigFlags_NavEnableGamepad` flag is set, requiring the backend to feed button and axis events via `io.AddKeyEvent()` and `io.AddKeyAnalogEvent()` each frame.**

Dear ImGui’s navigation system is built around the **ImGuiIO** interface. When you implement **gamepad navigation input handlers for ImGui**, you translate platform-specific controller events into the `ImGuiKey_Gamepad*` enumeration and submit them before `ImGui::NewFrame()`. This guide walks through the exact implementation steps found in the `ocornut/imgui` source code, referencing core files such as [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) and the official platform backends.

## Enable Gamepad Navigation in ImGuiIO

Before the backend submits any controller data, the application must opt in to gamepad navigation.

### Set the Master Navigation Flag

Set the `ImGuiConfigFlags_NavEnableGamepad` flag in your initialization code:

```cpp
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;

```

### Signal Gamepad Presence with Backend Flags

The backend must tell ImGui that a physical controller is available by setting `ImGuiBackendFlags_HasGamepad`:

```cpp
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;

```

If the controller disconnects, clear this flag so ImGui stops waiting for gamepad input.

## Digital Button Input Handlers for Gamepad Navigation

Digital buttons are submitted through the `ImGuiIO::AddKeyEvent()` API. Dear ImGui does not hard-code a physical button layout; you decide which physical button maps to each `ImGuiKey_Gamepad*` value.

For a standard controller layout, forward face buttons and utility controls like this:

```cpp
io.AddKeyEvent(ImGuiKey_GamepadFaceDown,  down);   // “A” / “Cross”
io.AddKeyEvent(ImGuiKey_GamepadFaceRight, down);  // “B” / “Circle”
io.AddKeyEvent(ImGuiKey_GamepadFaceLeft,  down);  // “X” / “Square”
io.AddKeyEvent(ImGuiKey_GamepadFaceUp,    down);  // “Y” / “Triangle”

io.AddKeyEvent(ImGuiKey_GamepadStart,      down);
io.AddKeyEvent(ImGuiKey_GamepadBack,       down);
io.AddKeyEvent(ImGuiKey_GamepadLShoulder,  down);
io.AddKeyEvent(ImGuiKey_GamepadRShoulder,  down);

```

Directional pad buttons are treated the same way:

```cpp
io.AddKeyEvent(ImGuiKey_GamepadDpadLeft,  down);
io.AddKeyEvent(ImGuiKey_GamepadDpadRight, down);
io.AddKeyEvent(ImGuiKey_GamepadDpadUp,    down);
io.AddKeyEvent(ImGuiKey_GamepadDpadDown,  down);

```

## Analog Axis Input Handlers for Gamepad Navigation

Analog data requires `ImGuiIO::AddKeyAnalogEvent()`, which accepts both a digital `bool` and a normalized `float` magnitude from **0.0 to 1.0**. This is critical for the left stick, which drives directional navigation, and for triggers treated as analog buttons.

Apply a dead-zone threshold to eliminate stick drift, then submit the clamped magnitude:

```cpp
const float dead = 0.2f;

// Left stick X/Y (used for directional navigation)
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickLeft,  leftX < -dead, -leftX);
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickRight, leftX >  dead,  leftX);
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickUp,    leftY < -dead, -leftY);
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickDown,  leftY >  dead,  leftY);

// Triggers (treated as analog buttons)
io.AddKeyAnalogEvent(ImGuiKey_GamepadLTrigger, true, leftTrigger);
io.AddKeyAnalogEvent(ImGuiKey_GamepadRTrigger, true, rightTrigger);

```

## Frame Update Cycle for Gamepad Input

Each frame, the backend must refresh the input state before calling `ImGui::NewFrame()`. According to the reference flow in the `ocornut/imgui` source, clear the previous frame’s state and push the latest gamepad data:

```cpp
ImGuiIO& io = ImGui::GetIO();
io.ClearInputKeys();   // start fresh each frame

// ... submit all gamepad buttons and axes ...

ImGui::NewFrame();

```

## How ImGui Translates Input into Navigation Actions

Once the input reaches Dear ImGui, the **navigation engine** inside [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) handles the rest. The `NavUpdate()` function translates submitted keys into focus movement, activation, and cancellation:

- **ImGuiKey_GamepadFaceDown** triggers **Activate**.
- **ImGuiKey_GamepadFaceRight** triggers **Cancel**.
- **ImGuiKey_GamepadLStick** left, right, up, and down variants move focus between widgets.
- The engine automatically applies **wrapping**, **key repeat**, and **slow-move** behavior when the analog stick is held near the dead-zone threshold.

## Official Backend Reference Implementations

The `ocornut/imgui` repository ships with fully functional gamepad handling in its official backends. Inspect these files to see how platform-specific APIs map to `ImGuiKey_Gamepad*`:

- **Win32** — [`backends/imgui_impl_win32.cpp`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_win32.cpp) maps `XINPUT_GAMEPAD` bits to `ImGuiKey_Gamepad*` via the `MAP_BUTTON` macro around lines 360-398.
- **GLFW** — [`backends/imgui_impl_glfw.cpp`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_glfw.cpp) forwards SDL-style gamepad buttons using the `MAP_BUTTON` macro around lines 904-934.
- **SDL2** — [`backends/imgui_impl_sdl2.cpp`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_sdl2.cpp) implements `ImGui_ImplSDL2_AddGamepadEvent` around lines 770-795.
- **SDL3** — [`backends/imgui_impl_sdl3.cpp`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_sdl3.cpp) contains analogous SDL3 handling around lines 770-798.
- **Android** — [`backends/imgui_impl_android.cpp`](https://github.com/ocornut/imgui/blob/main/backends/imgui_impl_android.cpp) forwards Android gamepad keys around lines 172-190.

## Complete Generic Backend Example

The following pattern works in any custom backend. Call this routine **once per frame** before `ImGui::NewFrame()`:

```cpp
void MyBackendUpdateGamepad()
{
    ImGuiIO& io = ImGui::GetIO();
    io.ClearInputKeys();                     // start fresh each frame

    // Detect connected controller
    if (!myGamepad.IsConnected())
        return;                              // no gamepad → nothing to feed

    io.BackendFlags |= ImGuiBackendFlags_HasGamepad;

    // Face and utility buttons
    io.AddKeyEvent(ImGuiKey_GamepadFaceDown,  myGamepad.ButtonDown(MyGamepad::A));
    io.AddKeyEvent(ImGuiKey_GamepadFaceRight, myGamepad.ButtonDown(MyGamepad::B));
    io.AddKeyEvent(ImGuiKey_GamepadFaceLeft,  myGamepad.ButtonDown(MyGamepad::X));
    io.AddKeyEvent(ImGuiKey_GamepadFaceUp,    myGamepad.ButtonDown(MyGamepad::Y));

    io.AddKeyEvent(ImGuiKey_GamepadLShoulder, myGamepad.ButtonDown(MyGamepad::LB));
    io.AddKeyEvent(ImGuiKey_GamepadRShoulder, myGamepad.ButtonDown(MyGamepad::RB));
    io.AddKeyEvent(ImGuiKey_GamepadStart,    myGamepad.ButtonDown(MyGamepad::Start));
    io.AddKeyEvent(ImGuiKey_GamepadBack,     myGamepad.ButtonDown(MyGamepad::Back));

    // D-Pad
    io.AddKeyEvent(ImGuiKey_GamepadDpadLeft,  myGamepad.ButtonDown(MyGamepad::DPadLeft));
    io.AddKeyEvent(ImGuiKey_GamepadDpadRight, myGamepad.ButtonDown(MyGamepad::DPadRight));
    io.AddKeyEvent(ImGuiKey_GamepadDpadUp,    myGamepad.ButtonDown(MyGamepad::DPadUp));
    io.AddKeyEvent(ImGuiKey_GamepadDpadDown,  myGamepad.ButtonDown(MyGamepad::DPadDown));

    // Analog sticks with dead-zone
    const float dead = 0.2f;
    float lx = myGamepad.Axis(MyGamepad::LeftX);   // -1.0 … +1.0
    float ly = myGamepad.Axis(MyGamepad::LeftY);

    if (lx < -dead) io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickLeft,  true, -lx);
    if (lx >  dead) io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickRight, true,  lx);
    if (ly < -dead) io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickUp,    true, -ly);
    if (ly >  dead) io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickDown,  true,  ly);

    // Triggers
    float lt = myGamepad.Axis(MyGamepad::LT);   // 0.0 … 1.0
    float rt = myGamepad.Axis(MyGamepad::RT);
    io.AddKeyAnalogEvent(ImGuiKey_GamepadLTrigger, true, lt);
    io.AddKeyAnalogEvent(ImGuiKey_GamepadRTrigger, true, rt);
}

```

After this function returns and you invoke `ImGui::NewFrame()`, the Dear ImGui navigation system will automatically move focus, show the navigation cursor, and allow widget activation with the gamepad.

## Important Configuration Details

- **Dead-zone handling** — Keep a small threshold (≈ 0.2) when calling `AddKeyAnalogEvent` to avoid drift from idle sticks.
- **Analog versus digital** — Submit true buttons with `AddKeyEvent`; submit thumb-sticks and triggers with `AddKeyAnalogEvent`.
- **Swap confirm and cancel** — Set `io.ConfigNavSwapGamepadButtons = true` if your target platform uses a Nintendo-style layout where the east face button cancels and the south face button activates.
- **Single controller limit** — Dear ImGui consumes only the first detected controller. To switch active gamepads, manage the connection state in your backend and toggle `ImGuiBackendFlags_HasGamepad` to reflect which controller is currently active.

## Summary

- Gamepad navigation requires setting `ImGuiConfigFlags_NavEnableGamepad` in `ImGuiIO`.
- The backend must set `ImGuiBackendFlags_HasGamepad` when a controller is connected.
- Digital buttons map to `ImGuiKey_Gamepad*` values through `io.AddKeyEvent()`.
- Analog axes and triggers use `io.AddKeyAnalogEvent()` with a **0.0 to 1.0** magnitude and an applied dead-zone.
- Feed input and clear state each frame before `ImGui::NewFrame()` so the `NavUpdate()` routine in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) can resolve focus moves, activation, and cancellation.

## Frequently Asked Questions

### What is the difference between `AddKeyEvent` and `AddKeyAnalogEvent` for gamepad navigation?

`AddKeyEvent` reports a pure digital on/off state for buttons such as the face buttons, shoulders, and D-pad directions. `AddKeyAnalogEvent` reports both a digital crossing state and a normalized **0.0 to 1.0** magnitude for sources like thumb-sticks and triggers, which lets ImGui apply slow-move and repeat behaviors.

### How should I handle analog stick dead-zones when feeding gamepad data to ImGui?

Compare the raw axis value against a small threshold such as **0.2**. Only call `AddKeyAnalogEvent` when the magnitude exceeds that dead-zone, and pass the unsigned residual magnitude as the analog value. This prevents phantom navigation from stick drift.

### Can I swap the confirm and cancel buttons to match a Nintendo controller layout?

Yes. Set `io.ConfigNavSwapGamepadButtons = true` before `NewFrame`. When enabled, Dear ImGui swaps the default activation and cancel assignments so that `ImGuiKey_GamepadFaceRight` activates and `ImGuiKey_GamepadFaceDown` cancels.

### Does Dear ImGui support navigation from multiple gamepads at the same time?

No. The navigation engine in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp) consumes input from a single logical gamepad represented by the `ImGuiBackendFlags_HasGamepad` flag. If you need to support hot-swapping between multiple controllers, manage the connection state in your backend and toggle the flag to reflect which controller is currently active.