Merge branch 'thedmd/extra-keys-v1' into docking

# Conflicts:
#	backends/imgui_impl_glfw.cpp
#	backends/imgui_impl_glfw.h
#	backends/imgui_impl_osx.h
#	backends/imgui_impl_osx.mm
#	backends/imgui_impl_sdl.cpp
#	backends/imgui_impl_sdl.h
#	backends/imgui_impl_win32.cpp
#	backends/imgui_impl_win32.h
#	imgui.cpp
#	imgui.h
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 13744f5..a667bd5 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -301,6 +301,18 @@
         EOF
         g++ -I. -Wall -Wformat -o example_single_file example_single_file.cpp
 
+    - name: Build example_null (with IMGUI_DISABLE_OBSOLETE_KEYIO)
+      run: |
+        cat > example_single_file.cpp <<'EOF'
+
+        #define IMGUI_DISABLE_OBSOLETE_KEYIO
+        #define IMGUI_IMPLEMENTATION
+        #include "misc/single_file/imgui_single_file.h"
+        #include "examples/example_null/main.cpp"
+
+        EOF
+        g++ -I. -Wall -Wformat -o example_single_file example_single_file.cpp
+
     - name: Build example_null (with IMGUI_DISABLE_DEMO_WINDOWS and IMGUI_DISABLE_METRICS_WINDOW)
       run: |
         cat > example_single_file.cpp <<'EOF'
diff --git a/LICENSE.txt b/LICENSE.txt
index 780533d..4023e0c 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2014-2021 Omar Cornut
+Copyright (c) 2014-2022 Omar Cornut
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp
index 8eb6361..d807496 100644
--- a/backends/imgui_impl_allegro5.cpp
+++ b/backends/imgui_impl_allegro5.cpp
@@ -3,6 +3,7 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID!
+//  [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 ALLEGRO_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
 //  [X] Platform: Clipboard support (from Allegro 5.1.12)
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 // Issues:
@@ -16,6 +17,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2022-01-XX: Inputs: calling new io.AddKeyEvent() API (1.87+), support for full keys range.
 //  2021-12-08: Renderer: Fixed mishandling of the the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86.
 //  2021-08-17: Calling io.AddFocusEvent() on ALLEGRO_EVENT_DISPLAY_SWITCH_OUT/ALLEGRO_EVENT_DISPLAY_SWITCH_IN events.
 //  2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
@@ -274,6 +276,119 @@
 }
 #endif
 
+static ImGuiKey ImGui_ImplAllegro5_KeyCodeToImGuiKey(int key_code)
+{
+    switch (key_code)
+    {
+        case ALLEGRO_KEY_TAB: return ImGuiKey_Tab;
+        case ALLEGRO_KEY_LEFT: return ImGuiKey_LeftArrow;
+        case ALLEGRO_KEY_RIGHT: return ImGuiKey_RightArrow;
+        case ALLEGRO_KEY_UP: return ImGuiKey_UpArrow;
+        case ALLEGRO_KEY_DOWN: return ImGuiKey_DownArrow;
+        case ALLEGRO_KEY_PGUP: return ImGuiKey_PageUp;
+        case ALLEGRO_KEY_PGDN: return ImGuiKey_PageDown;
+        case ALLEGRO_KEY_HOME: return ImGuiKey_Home;
+        case ALLEGRO_KEY_END: return ImGuiKey_End;
+        case ALLEGRO_KEY_INSERT: return ImGuiKey_Insert;
+        case ALLEGRO_KEY_DELETE: return ImGuiKey_Delete;
+        case ALLEGRO_KEY_BACKSPACE: return ImGuiKey_Backspace;
+        case ALLEGRO_KEY_SPACE: return ImGuiKey_Space;
+        case ALLEGRO_KEY_ENTER: return ImGuiKey_Enter;
+        case ALLEGRO_KEY_ESCAPE: return ImGuiKey_Escape;
+        case ALLEGRO_KEY_QUOTE: return ImGuiKey_Apostrophe;
+        case ALLEGRO_KEY_COMMA: return ImGuiKey_Comma;
+        case ALLEGRO_KEY_MINUS: return ImGuiKey_Minus;
+        case ALLEGRO_KEY_FULLSTOP: return ImGuiKey_Period;
+        case ALLEGRO_KEY_SLASH: return ImGuiKey_Slash;
+        case ALLEGRO_KEY_SEMICOLON: return ImGuiKey_Semicolon;
+        case ALLEGRO_KEY_EQUALS: return ImGuiKey_Equal;
+        case ALLEGRO_KEY_OPENBRACE: return ImGuiKey_LeftBracket;
+        case ALLEGRO_KEY_BACKSLASH: return ImGuiKey_Backslash;
+        case ALLEGRO_KEY_CLOSEBRACE: return ImGuiKey_RightBracket;
+        case ALLEGRO_KEY_TILDE: return ImGuiKey_GraveAccent;
+        case ALLEGRO_KEY_CAPSLOCK: return ImGuiKey_CapsLock;
+        case ALLEGRO_KEY_SCROLLLOCK: return ImGuiKey_ScrollLock;
+        case ALLEGRO_KEY_NUMLOCK: return ImGuiKey_NumLock;
+        case ALLEGRO_KEY_PRINTSCREEN: return ImGuiKey_PrintScreen;
+        case ALLEGRO_KEY_PAUSE: return ImGuiKey_Pause;
+        case ALLEGRO_KEY_PAD_0: return ImGuiKey_Keypad0;
+        case ALLEGRO_KEY_PAD_1: return ImGuiKey_Keypad1;
+        case ALLEGRO_KEY_PAD_2: return ImGuiKey_Keypad2;
+        case ALLEGRO_KEY_PAD_3: return ImGuiKey_Keypad3;
+        case ALLEGRO_KEY_PAD_4: return ImGuiKey_Keypad4;
+        case ALLEGRO_KEY_PAD_5: return ImGuiKey_Keypad5;
+        case ALLEGRO_KEY_PAD_6: return ImGuiKey_Keypad6;
+        case ALLEGRO_KEY_PAD_7: return ImGuiKey_Keypad7;
+        case ALLEGRO_KEY_PAD_8: return ImGuiKey_Keypad8;
+        case ALLEGRO_KEY_PAD_9: return ImGuiKey_Keypad9;
+        case ALLEGRO_KEY_PAD_DELETE: return ImGuiKey_KeypadDecimal;
+        case ALLEGRO_KEY_PAD_SLASH: return ImGuiKey_KeypadDivide;
+        case ALLEGRO_KEY_PAD_ASTERISK: return ImGuiKey_KeypadMultiply;
+        case ALLEGRO_KEY_PAD_MINUS: return ImGuiKey_KeypadSubtract;
+        case ALLEGRO_KEY_PAD_PLUS: return ImGuiKey_KeypadAdd;
+        case ALLEGRO_KEY_PAD_ENTER: return ImGuiKey_KeypadEnter;
+        case ALLEGRO_KEY_PAD_EQUALS: return ImGuiKey_KeypadEqual;
+        case ALLEGRO_KEY_LSHIFT: return ImGuiKey_LeftShift;
+        case ALLEGRO_KEY_LCTRL: return ImGuiKey_LeftControl;
+        case ALLEGRO_KEY_ALT: return ImGuiKey_LeftAlt;
+        case ALLEGRO_KEY_LWIN: return ImGuiKey_LeftSuper;
+        case ALLEGRO_KEY_RSHIFT: return ImGuiKey_RightShift;
+        case ALLEGRO_KEY_RCTRL: return ImGuiKey_RightControl;
+        case ALLEGRO_KEY_ALTGR: return ImGuiKey_RightAlt;
+        case ALLEGRO_KEY_RWIN: return ImGuiKey_RightSuper;
+        case ALLEGRO_KEY_MENU: return ImGuiKey_Menu;
+        case ALLEGRO_KEY_0: return ImGuiKey_0;
+        case ALLEGRO_KEY_1: return ImGuiKey_1;
+        case ALLEGRO_KEY_2: return ImGuiKey_2;
+        case ALLEGRO_KEY_3: return ImGuiKey_3;
+        case ALLEGRO_KEY_4: return ImGuiKey_4;
+        case ALLEGRO_KEY_5: return ImGuiKey_5;
+        case ALLEGRO_KEY_6: return ImGuiKey_6;
+        case ALLEGRO_KEY_7: return ImGuiKey_7;
+        case ALLEGRO_KEY_8: return ImGuiKey_8;
+        case ALLEGRO_KEY_9: return ImGuiKey_9;
+        case ALLEGRO_KEY_A: return ImGuiKey_A;
+        case ALLEGRO_KEY_B: return ImGuiKey_B;
+        case ALLEGRO_KEY_C: return ImGuiKey_C;
+        case ALLEGRO_KEY_D: return ImGuiKey_D;
+        case ALLEGRO_KEY_E: return ImGuiKey_E;
+        case ALLEGRO_KEY_F: return ImGuiKey_F;
+        case ALLEGRO_KEY_G: return ImGuiKey_G;
+        case ALLEGRO_KEY_H: return ImGuiKey_H;
+        case ALLEGRO_KEY_I: return ImGuiKey_I;
+        case ALLEGRO_KEY_J: return ImGuiKey_J;
+        case ALLEGRO_KEY_K: return ImGuiKey_K;
+        case ALLEGRO_KEY_L: return ImGuiKey_L;
+        case ALLEGRO_KEY_M: return ImGuiKey_M;
+        case ALLEGRO_KEY_N: return ImGuiKey_N;
+        case ALLEGRO_KEY_O: return ImGuiKey_O;
+        case ALLEGRO_KEY_P: return ImGuiKey_P;
+        case ALLEGRO_KEY_Q: return ImGuiKey_Q;
+        case ALLEGRO_KEY_R: return ImGuiKey_R;
+        case ALLEGRO_KEY_S: return ImGuiKey_S;
+        case ALLEGRO_KEY_T: return ImGuiKey_T;
+        case ALLEGRO_KEY_U: return ImGuiKey_U;
+        case ALLEGRO_KEY_V: return ImGuiKey_V;
+        case ALLEGRO_KEY_W: return ImGuiKey_W;
+        case ALLEGRO_KEY_X: return ImGuiKey_X;
+        case ALLEGRO_KEY_Y: return ImGuiKey_Y;
+        case ALLEGRO_KEY_Z: return ImGuiKey_Z;
+        case ALLEGRO_KEY_F1: return ImGuiKey_F1;
+        case ALLEGRO_KEY_F2: return ImGuiKey_F2;
+        case ALLEGRO_KEY_F3: return ImGuiKey_F3;
+        case ALLEGRO_KEY_F4: return ImGuiKey_F4;
+        case ALLEGRO_KEY_F5: return ImGuiKey_F5;
+        case ALLEGRO_KEY_F6: return ImGuiKey_F6;
+        case ALLEGRO_KEY_F7: return ImGuiKey_F7;
+        case ALLEGRO_KEY_F8: return ImGuiKey_F8;
+        case ALLEGRO_KEY_F9: return ImGuiKey_F9;
+        case ALLEGRO_KEY_F10: return ImGuiKey_F10;
+        case ALLEGRO_KEY_F11: return ImGuiKey_F11;
+        case ALLEGRO_KEY_F12: return ImGuiKey_F12;
+        default: return ImGuiKey_None;
+    }
+}
+
 bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display)
 {
     ImGuiIO& io = ImGui::GetIO();
@@ -299,28 +414,6 @@
     };
     bd->VertexDecl = al_create_vertex_decl(elems, sizeof(ImDrawVertAllegro));
 
-    io.KeyMap[ImGuiKey_Tab] = ALLEGRO_KEY_TAB;
-    io.KeyMap[ImGuiKey_LeftArrow] = ALLEGRO_KEY_LEFT;
-    io.KeyMap[ImGuiKey_RightArrow] = ALLEGRO_KEY_RIGHT;
-    io.KeyMap[ImGuiKey_UpArrow] = ALLEGRO_KEY_UP;
-    io.KeyMap[ImGuiKey_DownArrow] = ALLEGRO_KEY_DOWN;
-    io.KeyMap[ImGuiKey_PageUp] = ALLEGRO_KEY_PGUP;
-    io.KeyMap[ImGuiKey_PageDown] = ALLEGRO_KEY_PGDN;
-    io.KeyMap[ImGuiKey_Home] = ALLEGRO_KEY_HOME;
-    io.KeyMap[ImGuiKey_End] = ALLEGRO_KEY_END;
-    io.KeyMap[ImGuiKey_Insert] = ALLEGRO_KEY_INSERT;
-    io.KeyMap[ImGuiKey_Delete] = ALLEGRO_KEY_DELETE;
-    io.KeyMap[ImGuiKey_Backspace] = ALLEGRO_KEY_BACKSPACE;
-    io.KeyMap[ImGuiKey_Space] = ALLEGRO_KEY_SPACE;
-    io.KeyMap[ImGuiKey_Enter] = ALLEGRO_KEY_ENTER;
-    io.KeyMap[ImGuiKey_Escape] = ALLEGRO_KEY_ESCAPE;
-    io.KeyMap[ImGuiKey_KeyPadEnter] = ALLEGRO_KEY_PAD_ENTER;
-    io.KeyMap[ImGuiKey_A] = ALLEGRO_KEY_A;
-    io.KeyMap[ImGuiKey_C] = ALLEGRO_KEY_C;
-    io.KeyMap[ImGuiKey_V] = ALLEGRO_KEY_V;
-    io.KeyMap[ImGuiKey_X] = ALLEGRO_KEY_X;
-    io.KeyMap[ImGuiKey_Y] = ALLEGRO_KEY_Y;
-    io.KeyMap[ImGuiKey_Z] = ALLEGRO_KEY_Z;
     io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
 
 #if ALLEGRO_HAS_CLIPBOARD
@@ -395,7 +488,11 @@
     case ALLEGRO_EVENT_KEY_DOWN:
     case ALLEGRO_EVENT_KEY_UP:
         if (ev->keyboard.display == bd->Display)
-            io.KeysDown[ev->keyboard.keycode] = (ev->type == ALLEGRO_EVENT_KEY_DOWN);
+        {
+            ImGuiKey key = ImGui_ImplAllegro5_KeyCodeToImGuiKey(ev->keyboard.keycode);
+            if (key != ImGuiKey_None)
+                io.AddKeyEvent(key, ev->type == ALLEGRO_EVENT_KEY_DOWN, ev->keyboard.keycode);
+        }
         return true;
     case ALLEGRO_EVENT_DISPLAY_SWITCH_OUT:
         if (ev->display.source == bd->Display)
diff --git a/backends/imgui_impl_allegro5.h b/backends/imgui_impl_allegro5.h
index 06c7120..7e97969 100644
--- a/backends/imgui_impl_allegro5.h
+++ b/backends/imgui_impl_allegro5.h
@@ -3,13 +3,14 @@
 
 // Implemented features:
 //  [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID!
+//  [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 ALLEGRO_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
 //  [X] Platform: Clipboard support (from Allegro 5.1.12)
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 // Issues:
 //  [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually.
 //  [ ] Platform: Missing gamepad support.
 
-// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 
+// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // 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
diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp
index aae8e6b..6a454a4 100644
--- a/backends/imgui_impl_android.cpp
+++ b/backends/imgui_impl_android.cpp
@@ -11,7 +11,7 @@
 //  - 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)
 
-// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 
+// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // 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
@@ -139,7 +139,7 @@
     io.KeyMap[ImGuiKey_Space] = AKEYCODE_SPACE;
     io.KeyMap[ImGuiKey_Enter] = AKEYCODE_ENTER;
     io.KeyMap[ImGuiKey_Escape] = AKEYCODE_ESCAPE;
-    io.KeyMap[ImGuiKey_KeyPadEnter] = AKEYCODE_NUMPAD_ENTER;
+    io.KeyMap[ImGuiKey_KeypadEnter] = AKEYCODE_NUMPAD_ENTER;
     io.KeyMap[ImGuiKey_A] = AKEYCODE_A;
     io.KeyMap[ImGuiKey_C] = AKEYCODE_C;
     io.KeyMap[ImGuiKey_V] = AKEYCODE_V;
diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp
index 53954c5..d8b5ca3 100644
--- a/backends/imgui_impl_glfw.cpp
+++ b/backends/imgui_impl_glfw.cpp
@@ -5,9 +5,9 @@
 
 // Implemented features:
 //  [X] Platform: Clipboard support.
+//  [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 GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
 //  [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
-//  [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE).
 //  [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
 
 // Issues:
@@ -21,6 +21,7 @@
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 //  2021-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
+//  2022-01-XX: Inputs: calling new io.AddKeyEvent() API (1.87+), support for full keys range.
 //  2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback().
 //  2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback().
 //  2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
@@ -102,7 +103,7 @@
     GLFWwindow*             MouseWindow;
     bool                    MouseJustPressed[ImGuiMouseButton_COUNT];
     GLFWcursor*             MouseCursors[ImGuiMouseCursor_COUNT];
-    GLFWwindow*             KeyOwnerWindows[512];
+    GLFWwindow*             KeyOwnerWindows[GLFW_KEY_LAST];
     bool                    InstalledCallbacks;
     bool                    WantUpdateMonitors;
 
@@ -146,6 +147,119 @@
     glfwSetClipboardString((GLFWwindow*)user_data, text);
 }
 
+static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key)
+{
+    switch (key)
+    {
+        case GLFW_KEY_TAB: return ImGuiKey_Tab;
+        case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow;
+        case GLFW_KEY_RIGHT: return ImGuiKey_RightArrow;
+        case GLFW_KEY_UP: return ImGuiKey_UpArrow;
+        case GLFW_KEY_DOWN: return ImGuiKey_DownArrow;
+        case GLFW_KEY_PAGE_UP: return ImGuiKey_PageUp;
+        case GLFW_KEY_PAGE_DOWN: return ImGuiKey_PageDown;
+        case GLFW_KEY_HOME: return ImGuiKey_Home;
+        case GLFW_KEY_END: return ImGuiKey_End;
+        case GLFW_KEY_INSERT: return ImGuiKey_Insert;
+        case GLFW_KEY_DELETE: return ImGuiKey_Delete;
+        case GLFW_KEY_BACKSPACE: return ImGuiKey_Backspace;
+        case GLFW_KEY_SPACE: return ImGuiKey_Space;
+        case GLFW_KEY_ENTER: return ImGuiKey_Enter;
+        case GLFW_KEY_ESCAPE: return ImGuiKey_Escape;
+        case GLFW_KEY_APOSTROPHE: return ImGuiKey_Apostrophe;
+        case GLFW_KEY_COMMA: return ImGuiKey_Comma;
+        case GLFW_KEY_MINUS: return ImGuiKey_Minus;
+        case GLFW_KEY_PERIOD: return ImGuiKey_Period;
+        case GLFW_KEY_SLASH: return ImGuiKey_Slash;
+        case GLFW_KEY_SEMICOLON: return ImGuiKey_Semicolon;
+        case GLFW_KEY_EQUAL: return ImGuiKey_Equal;
+        case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket;
+        case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash;
+        case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket;
+        case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent;
+        case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock;
+        case GLFW_KEY_SCROLL_LOCK: return ImGuiKey_ScrollLock;
+        case GLFW_KEY_NUM_LOCK: return ImGuiKey_NumLock;
+        case GLFW_KEY_PRINT_SCREEN: return ImGuiKey_PrintScreen;
+        case GLFW_KEY_PAUSE: return ImGuiKey_Pause;
+        case GLFW_KEY_KP_0: return ImGuiKey_Keypad0;
+        case GLFW_KEY_KP_1: return ImGuiKey_Keypad1;
+        case GLFW_KEY_KP_2: return ImGuiKey_Keypad2;
+        case GLFW_KEY_KP_3: return ImGuiKey_Keypad3;
+        case GLFW_KEY_KP_4: return ImGuiKey_Keypad4;
+        case GLFW_KEY_KP_5: return ImGuiKey_Keypad5;
+        case GLFW_KEY_KP_6: return ImGuiKey_Keypad6;
+        case GLFW_KEY_KP_7: return ImGuiKey_Keypad7;
+        case GLFW_KEY_KP_8: return ImGuiKey_Keypad8;
+        case GLFW_KEY_KP_9: return ImGuiKey_Keypad9;
+        case GLFW_KEY_KP_DECIMAL: return ImGuiKey_KeypadDecimal;
+        case GLFW_KEY_KP_DIVIDE: return ImGuiKey_KeypadDivide;
+        case GLFW_KEY_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;
+        case GLFW_KEY_KP_SUBTRACT: return ImGuiKey_KeypadSubtract;
+        case GLFW_KEY_KP_ADD: return ImGuiKey_KeypadAdd;
+        case GLFW_KEY_KP_ENTER: return ImGuiKey_KeypadEnter;
+        case GLFW_KEY_KP_EQUAL: return ImGuiKey_KeypadEqual;
+        case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift;
+        case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftControl;
+        case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt;
+        case GLFW_KEY_LEFT_SUPER: return ImGuiKey_LeftSuper;
+        case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift;
+        case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightControl;
+        case GLFW_KEY_RIGHT_ALT: return ImGuiKey_RightAlt;
+        case GLFW_KEY_RIGHT_SUPER: return ImGuiKey_RightSuper;
+        case GLFW_KEY_MENU: return ImGuiKey_Menu;
+        case GLFW_KEY_0: return ImGuiKey_0;
+        case GLFW_KEY_1: return ImGuiKey_1;
+        case GLFW_KEY_2: return ImGuiKey_2;
+        case GLFW_KEY_3: return ImGuiKey_3;
+        case GLFW_KEY_4: return ImGuiKey_4;
+        case GLFW_KEY_5: return ImGuiKey_5;
+        case GLFW_KEY_6: return ImGuiKey_6;
+        case GLFW_KEY_7: return ImGuiKey_7;
+        case GLFW_KEY_8: return ImGuiKey_8;
+        case GLFW_KEY_9: return ImGuiKey_9;
+        case GLFW_KEY_A: return ImGuiKey_A;
+        case GLFW_KEY_B: return ImGuiKey_B;
+        case GLFW_KEY_C: return ImGuiKey_C;
+        case GLFW_KEY_D: return ImGuiKey_D;
+        case GLFW_KEY_E: return ImGuiKey_E;
+        case GLFW_KEY_F: return ImGuiKey_F;
+        case GLFW_KEY_G: return ImGuiKey_G;
+        case GLFW_KEY_H: return ImGuiKey_H;
+        case GLFW_KEY_I: return ImGuiKey_I;
+        case GLFW_KEY_J: return ImGuiKey_J;
+        case GLFW_KEY_K: return ImGuiKey_K;
+        case GLFW_KEY_L: return ImGuiKey_L;
+        case GLFW_KEY_M: return ImGuiKey_M;
+        case GLFW_KEY_N: return ImGuiKey_N;
+        case GLFW_KEY_O: return ImGuiKey_O;
+        case GLFW_KEY_P: return ImGuiKey_P;
+        case GLFW_KEY_Q: return ImGuiKey_Q;
+        case GLFW_KEY_R: return ImGuiKey_R;
+        case GLFW_KEY_S: return ImGuiKey_S;
+        case GLFW_KEY_T: return ImGuiKey_T;
+        case GLFW_KEY_U: return ImGuiKey_U;
+        case GLFW_KEY_V: return ImGuiKey_V;
+        case GLFW_KEY_W: return ImGuiKey_W;
+        case GLFW_KEY_X: return ImGuiKey_X;
+        case GLFW_KEY_Y: return ImGuiKey_Y;
+        case GLFW_KEY_Z: return ImGuiKey_Z;
+        case GLFW_KEY_F1: return ImGuiKey_F1;
+        case GLFW_KEY_F2: return ImGuiKey_F2;
+        case GLFW_KEY_F3: return ImGuiKey_F3;
+        case GLFW_KEY_F4: return ImGuiKey_F4;
+        case GLFW_KEY_F5: return ImGuiKey_F5;
+        case GLFW_KEY_F6: return ImGuiKey_F6;
+        case GLFW_KEY_F7: return ImGuiKey_F7;
+        case GLFW_KEY_F8: return ImGuiKey_F8;
+        case GLFW_KEY_F9: return ImGuiKey_F9;
+        case GLFW_KEY_F10: return ImGuiKey_F10;
+        case GLFW_KEY_F11: return ImGuiKey_F11;
+        case GLFW_KEY_F12: return ImGuiKey_F12;
+        default: return ImGuiKey_None;
+    }
+}
+
 void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
 {
     ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
@@ -173,30 +287,16 @@
     if (bd->PrevUserCallbackKey != NULL && window == bd->Window)
         bd->PrevUserCallbackKey(window, key, scancode, action, mods);
 
+    if (action != GLFW_PRESS && action != GLFW_RELEASE)
+        return;
     ImGuiIO& io = ImGui::GetIO();
-    if (key >= 0 && key < IM_ARRAYSIZE(io.KeysDown))
-    {
-        if (action == GLFW_PRESS)
-        {
-            io.KeysDown[key] = true;
-            bd->KeyOwnerWindows[key] = window;
-        }
-        if (action == GLFW_RELEASE)
-        {
-            io.KeysDown[key] = false;
-            bd->KeyOwnerWindows[key] = NULL;
-        }
-    }
 
-    // Modifiers are not reliable across systems
-    io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL];
-    io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT];
-    io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT];
-#ifdef _WIN32
-    io.KeySuper = false;
-#else
-    io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER];
-#endif
+    ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(key);
+    if (imgui_key != ImGuiKey_None)
+    {
+        io.AddKeyEvent(imgui_key, action == GLFW_PRESS, key, scancode);
+        bd->KeyOwnerWindows[key] = (action == GLFW_PRESS) ? window : NULL;
+    }
 }
 
 void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused)
@@ -257,30 +357,6 @@
     bd->Time = 0.0;
     bd->WantUpdateMonitors = true;
 
-    // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array.
-    io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
-    io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
-    io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
-    io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
-    io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
-    io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP;
-    io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN;
-    io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
-    io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
-    io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT;
-    io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
-    io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
-    io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE;
-    io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
-    io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
-    io.KeyMap[ImGuiKey_KeyPadEnter] = GLFW_KEY_KP_ENTER;
-    io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
-    io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
-    io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
-    io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
-    io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
-    io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
-
     io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText;
     io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText;
     io.ClipboardUserData = bd->Window;
@@ -564,6 +640,16 @@
     bd->WantUpdateMonitors = false;
 }
 
+static void ImGui_ImplGlfw_UpdateKeyModifiers()
+{
+    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
+    ImGuiIO& io = ImGui::GetIO();
+    io.KeyShift = ((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS));
+    io.KeyCtrl  = ((glfwGetKey(bd->Window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS));
+    io.KeyAlt   = ((glfwGetKey(bd->Window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS));
+    io.KeySuper = ((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS));
+}
+
 void ImGui_ImplGlfw_NewFrame()
 {
     ImGuiIO& io = ImGui::GetIO();
@@ -586,6 +672,9 @@
     io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f);
     bd->Time = current_time;
 
+    // Update key modifiers
+    ImGui_ImplGlfw_UpdateKeyModifiers();
+
     ImGui_ImplGlfw_UpdateMousePosAndButtons();
     ImGui_ImplGlfw_UpdateMouseCursor();
 
diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h
index bb0ddf3..966c67b 100644
--- a/backends/imgui_impl_glfw.h
+++ b/backends/imgui_impl_glfw.h
@@ -4,8 +4,9 @@
 
 // Implemented features:
 //  [X] Platform: Clipboard support.
+//  [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 GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
 //  [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
-//  [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW.
+//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
 //  [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE).
 //  [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
 
diff --git a/backends/imgui_impl_glut.cpp b/backends/imgui_impl_glut.cpp
index 85bea77..9ccbc2a 100644
--- a/backends/imgui_impl_glut.cpp
+++ b/backends/imgui_impl_glut.cpp
@@ -5,19 +5,22 @@
 // !!! If someone or something is teaching you GLUT today, you are being abused. Please show some resistance. !!!
 // !!! Nowadays, prefer using GLFW or SDL instead!
 
+// Implemented features:
+//  [X] Platform: Partial 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 GLUT values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
 // Issues:
 //  [ ] Platform: GLUT is unable to distinguish e.g. Backspace from CTRL+H or TAB from CTRL+I
 //  [ ] Platform: Missing mouse cursor shape/visibility support.
 //  [ ] Platform: Missing clipboard support (not supported by Glut).
 //  [ ] Platform: Missing gamepad support.
 
-// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 
+// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // 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
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2022-01-XX: Inputs: calling new io.AddKeyEvent() API (1.87+), support for full keys range.
 //  2019-04-03: Misc: Renamed imgui_impl_freeglut.cpp/.h to imgui_impl_glut.cpp/.h.
 //  2019-03-25: Misc: Made io.DeltaTime always above zero.
 //  2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
@@ -37,6 +40,120 @@
 
 static int g_Time = 0;          // Current time, in milliseconds
 
+// Glut has 1 function for characters and one for "special keys". We map the characters in the 0..255 range and the keys above.
+static ImGuiKey ImGui_ImplGLUT_KeyToImGuiKey(int key)
+{
+    switch (key)
+    {
+        case '\t':                      return ImGuiKey_Tab;
+        case 256 + GLUT_KEY_LEFT:       return ImGuiKey_LeftArrow;
+        case 256 + GLUT_KEY_RIGHT:      return ImGuiKey_RightArrow;
+        case 256 + GLUT_KEY_UP:         return ImGuiKey_UpArrow;
+        case 256 + GLUT_KEY_DOWN:       return ImGuiKey_DownArrow;
+        case 256 + GLUT_KEY_PAGE_UP:    return ImGuiKey_PageUp;
+        case 256 + GLUT_KEY_PAGE_DOWN:  return ImGuiKey_PageDown;
+        case 256 + GLUT_KEY_HOME:       return ImGuiKey_Home;
+        case 256 + GLUT_KEY_END:        return ImGuiKey_End;
+        case 256 + GLUT_KEY_INSERT:     return ImGuiKey_Insert;
+        case 127:                       return ImGuiKey_Delete;
+        case 8:                         return ImGuiKey_Backspace;
+        case ' ':                       return ImGuiKey_Space;
+        case 13:                        return ImGuiKey_Enter;
+        case 27:                        return ImGuiKey_Escape;
+        case 39:                        return ImGuiKey_Apostrophe;
+        case 44:                        return ImGuiKey_Comma;
+        case 45:                        return ImGuiKey_Minus;
+        case 46:                        return ImGuiKey_Period;
+        case 47:                        return ImGuiKey_Slash;
+        case 59:                        return ImGuiKey_Semicolon;
+        case 61:                        return ImGuiKey_Equal;
+        case 91:                        return ImGuiKey_LeftBracket;
+        case 92:                        return ImGuiKey_Backslash;
+        case 93:                        return ImGuiKey_RightBracket;
+        case 96:                        return ImGuiKey_GraveAccent;
+        //case 0:                         return ImGuiKey_CapsLock;
+        //case 0:                         return ImGuiKey_ScrollLock;
+        case 256 + 0x006D:              return ImGuiKey_NumLock;
+        //case 0:                         return ImGuiKey_PrintScreen;
+        //case 0:                         return ImGuiKey_Pause;
+        //case '0':                       return ImGuiKey_Keypad0;
+        //case '1':                       return ImGuiKey_Keypad1;
+        //case '2':                       return ImGuiKey_Keypad2;
+        //case '3':                       return ImGuiKey_Keypad3;
+        //case '4':                       return ImGuiKey_Keypad4;
+        //case '5':                       return ImGuiKey_Keypad5;
+        //case '6':                       return ImGuiKey_Keypad6;
+        //case '7':                       return ImGuiKey_Keypad7;
+        //case '8':                       return ImGuiKey_Keypad8;
+        //case '9':                       return ImGuiKey_Keypad9;
+        //case 46:                        return ImGuiKey_KeypadDecimal;
+        //case 47:                        return ImGuiKey_KeypadDivide;
+        case 42:                        return ImGuiKey_KeypadMultiply;
+        //case 45:                        return ImGuiKey_KeypadSubtract;
+        case 43:                        return ImGuiKey_KeypadAdd;
+        //case 13:                        return ImGuiKey_KeypadEnter;
+        //case 0:                         return ImGuiKey_KeypadEqual;
+        case 256 + 0x0070:              return ImGuiKey_LeftShift;
+        case 256 + 0x0072:              return ImGuiKey_LeftControl;
+        case 256 + 0x0074:              return ImGuiKey_LeftAlt;
+        //case 0:                         return ImGuiKey_LeftSuper;
+        case 256 + 0x0071:              return ImGuiKey_RightShift;
+        case 256 + 0x0073:              return ImGuiKey_RightControl;
+        case 256 + 0x0075:              return ImGuiKey_RightAlt;
+        //case 0:                         return ImGuiKey_RightSuper;
+        //case 0:                         return ImGuiKey_Menu;
+        case '0':                       return ImGuiKey_0;
+        case '1':                       return ImGuiKey_1;
+        case '2':                       return ImGuiKey_2;
+        case '3':                       return ImGuiKey_3;
+        case '4':                       return ImGuiKey_4;
+        case '5':                       return ImGuiKey_5;
+        case '6':                       return ImGuiKey_6;
+        case '7':                       return ImGuiKey_7;
+        case '8':                       return ImGuiKey_8;
+        case '9':                       return ImGuiKey_9;
+        case 'A': case 'a':             return ImGuiKey_A;
+        case 'B': case 'b':             return ImGuiKey_B;
+        case 'C': case 'c':             return ImGuiKey_C;
+        case 'D': case 'd':             return ImGuiKey_D;
+        case 'E': case 'e':             return ImGuiKey_E;
+        case 'F': case 'f':             return ImGuiKey_F;
+        case 'G': case 'g':             return ImGuiKey_G;
+        case 'H': case 'h':             return ImGuiKey_H;
+        case 'I': case 'i':             return ImGuiKey_I;
+        case 'J': case 'j':             return ImGuiKey_J;
+        case 'K': case 'k':             return ImGuiKey_K;
+        case 'L': case 'l':             return ImGuiKey_L;
+        case 'M': case 'm':             return ImGuiKey_M;
+        case 'N': case 'n':             return ImGuiKey_N;
+        case 'O': case 'o':             return ImGuiKey_O;
+        case 'P': case 'p':             return ImGuiKey_P;
+        case 'Q': case 'q':             return ImGuiKey_Q;
+        case 'R': case 'r':             return ImGuiKey_R;
+        case 'S': case 's':             return ImGuiKey_S;
+        case 'T': case 't':             return ImGuiKey_T;
+        case 'U': case 'u':             return ImGuiKey_U;
+        case 'V': case 'v':             return ImGuiKey_V;
+        case 'W': case 'w':             return ImGuiKey_W;
+        case 'X': case 'x':             return ImGuiKey_X;
+        case 'Y': case 'y':             return ImGuiKey_Y;
+        case 'Z': case 'z':             return ImGuiKey_Z;
+        case 256 + GLUT_KEY_F1:         return ImGuiKey_F1;
+        case 256 + GLUT_KEY_F2:         return ImGuiKey_F2;
+        case 256 + GLUT_KEY_F3:         return ImGuiKey_F3;
+        case 256 + GLUT_KEY_F4:         return ImGuiKey_F4;
+        case 256 + GLUT_KEY_F5:         return ImGuiKey_F5;
+        case 256 + GLUT_KEY_F6:         return ImGuiKey_F6;
+        case 256 + GLUT_KEY_F7:         return ImGuiKey_F7;
+        case 256 + GLUT_KEY_F8:         return ImGuiKey_F8;
+        case 256 + GLUT_KEY_F9:         return ImGuiKey_F9;
+        case 256 + GLUT_KEY_F10:        return ImGuiKey_F10;
+        case 256 + GLUT_KEY_F11:        return ImGuiKey_F11;
+        case 256 + GLUT_KEY_F12:        return ImGuiKey_F12;
+        default:                        return ImGuiKey_None;
+    }
+}
+
 bool ImGui_ImplGLUT_Init()
 {
     ImGuiIO& io = ImGui::GetIO();
@@ -46,33 +163,8 @@
 #else
     io.BackendPlatformName = "imgui_impl_glut";
 #endif
-
     g_Time = 0;
 
-    // Glut has 1 function for characters and one for "special keys". We map the characters in the 0..255 range and the keys above.
-    io.KeyMap[ImGuiKey_Tab]         = '\t'; // == 9 == CTRL+I
-    io.KeyMap[ImGuiKey_LeftArrow]   = 256 + GLUT_KEY_LEFT;
-    io.KeyMap[ImGuiKey_RightArrow]  = 256 + GLUT_KEY_RIGHT;
-    io.KeyMap[ImGuiKey_UpArrow]     = 256 + GLUT_KEY_UP;
-    io.KeyMap[ImGuiKey_DownArrow]   = 256 + GLUT_KEY_DOWN;
-    io.KeyMap[ImGuiKey_PageUp]      = 256 + GLUT_KEY_PAGE_UP;
-    io.KeyMap[ImGuiKey_PageDown]    = 256 + GLUT_KEY_PAGE_DOWN;
-    io.KeyMap[ImGuiKey_Home]        = 256 + GLUT_KEY_HOME;
-    io.KeyMap[ImGuiKey_End]         = 256 + GLUT_KEY_END;
-    io.KeyMap[ImGuiKey_Insert]      = 256 + GLUT_KEY_INSERT;
-    io.KeyMap[ImGuiKey_Delete]      = 127;
-    io.KeyMap[ImGuiKey_Backspace]   = 8;  // == CTRL+H
-    io.KeyMap[ImGuiKey_Space]       = ' ';
-    io.KeyMap[ImGuiKey_Enter]       = 13; // == CTRL+M
-    io.KeyMap[ImGuiKey_Escape]      = 27;
-    io.KeyMap[ImGuiKey_KeyPadEnter] = 13; // == CTRL+M
-    io.KeyMap[ImGuiKey_A]           = 'A';
-    io.KeyMap[ImGuiKey_C]           = 'C';
-    io.KeyMap[ImGuiKey_V]           = 'V';
-    io.KeyMap[ImGuiKey_X]           = 'X';
-    io.KeyMap[ImGuiKey_Y]           = 'Y';
-    io.KeyMap[ImGuiKey_Z]           = 'Z';
-
     return true;
 }
 
@@ -117,6 +209,7 @@
     io.KeyCtrl = (mods & GLUT_ACTIVE_CTRL) != 0;
     io.KeyShift = (mods & GLUT_ACTIVE_SHIFT) != 0;
     io.KeyAlt = (mods & GLUT_ACTIVE_ALT) != 0;
+    io.KeySuper = false;
 }
 
 void ImGui_ImplGLUT_KeyboardFunc(unsigned char c, int x, int y)
@@ -126,17 +219,9 @@
     ImGuiIO& io = ImGui::GetIO();
     if (c >= 32)
         io.AddInputCharacter((unsigned int)c);
-
-    // Store letters in KeysDown[] array as both uppercase and lowercase + Handle GLUT translating CTRL+A..CTRL+Z as 1..26.
-    // This is a hacky mess but GLUT is unable to distinguish e.g. a TAB key from CTRL+I so this is probably the best we can do here.
-    if (c >= 1 && c <= 26)
-        io.KeysDown[c] = io.KeysDown[c - 1 + 'a'] = io.KeysDown[c - 1 + 'A'] = true;
-    else if (c >= 'a' && c <= 'z')
-        io.KeysDown[c] = io.KeysDown[c - 'a' + 'A'] = true;
-    else if (c >= 'A' && c <= 'Z')
-        io.KeysDown[c] = io.KeysDown[c - 'A' + 'a'] = true;
-    else
-        io.KeysDown[c] = true;
+    ImGuiKey key = ImGui_ImplGLUT_KeyToImGuiKey(c);
+    if (key != ImGuiKey_None)
+        io.AddKeyEvent(key, true, c);
     ImGui_ImplGLUT_UpdateKeyboardMods();
     (void)x; (void)y; // Unused
 }
@@ -145,14 +230,9 @@
 {
     //printf("char_up_func %d '%c'\n", c, c);
     ImGuiIO& io = ImGui::GetIO();
-    if (c >= 1 && c <= 26)
-        io.KeysDown[c] = io.KeysDown[c - 1 + 'a'] = io.KeysDown[c - 1 + 'A'] = false;
-    else if (c >= 'a' && c <= 'z')
-        io.KeysDown[c] = io.KeysDown[c - 'a' + 'A'] = false;
-    else if (c >= 'A' && c <= 'Z')
-        io.KeysDown[c] = io.KeysDown[c - 'A' + 'a'] = false;
-    else
-        io.KeysDown[c] = false;
+    ImGuiKey key = ImGui_ImplGLUT_KeyToImGuiKey(c);
+    if (key != ImGuiKey_None)
+        io.AddKeyEvent(key, false, c);
     ImGui_ImplGLUT_UpdateKeyboardMods();
     (void)x; (void)y; // Unused
 }
@@ -161,8 +241,9 @@
 {
     //printf("key_down_func %d\n", key);
     ImGuiIO& io = ImGui::GetIO();
-    if (key + 256 < IM_ARRAYSIZE(io.KeysDown))
-        io.KeysDown[key + 256] = true;
+    ImGuiKey imgui_key = ImGui_ImplGLUT_KeyToImGuiKey(key + 256);
+    if (imgui_key != ImGuiKey_None)
+        io.AddKeyEvent(imgui_key, true, key + 256);
     ImGui_ImplGLUT_UpdateKeyboardMods();
     (void)x; (void)y; // Unused
 }
@@ -171,8 +252,9 @@
 {
     //printf("key_up_func %d\n", key);
     ImGuiIO& io = ImGui::GetIO();
-    if (key + 256 < IM_ARRAYSIZE(io.KeysDown))
-        io.KeysDown[key + 256] = false;
+    ImGuiKey imgui_key = ImGui_ImplGLUT_KeyToImGuiKey(key + 256);
+    if (imgui_key != ImGuiKey_None)
+        io.AddKeyEvent(imgui_key, false, key + 256);
     ImGui_ImplGLUT_UpdateKeyboardMods();
     (void)x; (void)y; // Unused
 }
diff --git a/backends/imgui_impl_glut.h b/backends/imgui_impl_glut.h
index 96c779f..98d4e59 100644
--- a/backends/imgui_impl_glut.h
+++ b/backends/imgui_impl_glut.h
@@ -5,13 +5,15 @@
 // !!! If someone or something is teaching you GLUT today, you are being abused. Please show some resistance. !!!
 // !!! Nowadays, prefer using GLFW or SDL instead!
 
+// Implemented features:
+//  [X] Platform: Partial 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 GLUT values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
 // Issues:
 //  [ ] Platform: GLUT is unable to distinguish e.g. Backspace from CTRL+H or TAB from CTRL+I
 //  [ ] Platform: Missing mouse cursor shape/visibility support.
 //  [ ] Platform: Missing clipboard support (not supported by Glut).
 //  [ ] Platform: Missing gamepad support.
 
-// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 
+// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // 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
diff --git a/backends/imgui_impl_osx.h b/backends/imgui_impl_osx.h
index d553d42..b0f8e33 100644
--- a/backends/imgui_impl_osx.h
+++ b/backends/imgui_impl_osx.h
@@ -4,9 +4,9 @@
 
 // Implemented features:
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+//  [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 kVK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
 //  [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend).
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
-//  [X] Platform: Keyboard arrays indexed using kVK_* codes, e.g. ImGui::IsKeyPressed(kVK_Space).
 // Issues:
 //  [ ] Platform: Multi-viewport / platform windows.
 
diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm
index 0fc156d..df171c9 100644
--- a/backends/imgui_impl_osx.mm
+++ b/backends/imgui_impl_osx.mm
@@ -4,9 +4,9 @@
 
 // Implemented features:
 //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
+//  [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 kVK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
 //  [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend).
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
-//  [X] Platform: Keyboard arrays indexed using kVK_* codes, e.g. ImGui::IsKeyPressed(kVK_Space).
 // Issues:
 //  [ ] Platform: Multi-viewport / platform windows.
 
@@ -24,6 +24,7 @@
 
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
+//  2022-01-XX: Inputs: calling new io.AddKeyEvent() API (1.87+), support for full keys range.
 //  2021-12-13: *BREAKING CHANGE* Add NSView parameter to ImGui_ImplOSX_Init(). Generally fix keyboard support. Using kVK_* codes for keyboard keys.
 //  2021-12-13: Add game controller support.
 //  2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards.
@@ -52,6 +53,7 @@
 static bool                 g_MouseCursorHidden = false;
 static bool                 g_MouseJustPressed[ImGuiMouseButton_COUNT] = {};
 static bool                 g_MouseDown[ImGuiMouseButton_COUNT] = {};
+static ImGuiKeyModFlags     g_KeyModifiers = ImGuiKeyModFlags_None;
 static ImFocusObserver*     g_FocusObserver = nil;
 static KeyEventResponder*   g_KeyEventResponder = nil;
 
@@ -75,13 +77,6 @@
     return (double)mach_absolute_time() * g_HostClockPeriod;
 }
 
-static void resetKeys()
-{
-    ImGuiIO& io = ImGui::GetIO();
-    memset(io.KeysDown, 0, sizeof(io.KeysDown));
-    io.KeyCtrl = io.KeyShift = io.KeyAlt = io.KeySuper = false;
-}
-
 /**
  KeyEventResponder implements the NSTextInputClient protocol as is required by the macOS text input manager.
 
@@ -107,10 +102,17 @@
 
 - (void)keyDown:(NSEvent*)event
 {
+    ImGui_ImplOSX_HandleEvent(event, self);
+
     // Call to the macOS input manager system.
     [self interpretKeyEvents:@[event]];
 }
 
+- (void)keyUp:(NSEvent*)event
+{
+    ImGui_ImplOSX_HandleEvent(event, self);
+}
+
 - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
 {
     ImGuiIO& io = ImGui::GetIO();
@@ -197,16 +199,134 @@
 {
     ImGuiIO& io = ImGui::GetIO();
     io.AddFocusEvent(false);
-
-    // Unfocused applications do not receive input events, therefore we must manually
-    // release any pressed keys when application loses focus, otherwise they would remain
-    // stuck in a pressed state. https://github.com/ocornut/imgui/issues/3832
-    resetKeys();
 }
 
 @end
 
 // Functions
+static ImGuiKey ImGui_ImplOSX_KeyCodeToImGuiKey(int key_code)
+{
+    switch (key_code)
+    {
+        case kVK_ANSI_A: return ImGuiKey_A;
+        case kVK_ANSI_S: return ImGuiKey_S;
+        case kVK_ANSI_D: return ImGuiKey_D;
+        case kVK_ANSI_F: return ImGuiKey_F;
+        case kVK_ANSI_H: return ImGuiKey_H;
+        case kVK_ANSI_G: return ImGuiKey_G;
+        case kVK_ANSI_Z: return ImGuiKey_Z;
+        case kVK_ANSI_X: return ImGuiKey_X;
+        case kVK_ANSI_C: return ImGuiKey_C;
+        case kVK_ANSI_V: return ImGuiKey_V;
+        case kVK_ANSI_B: return ImGuiKey_B;
+        case kVK_ANSI_Q: return ImGuiKey_Q;
+        case kVK_ANSI_W: return ImGuiKey_W;
+        case kVK_ANSI_E: return ImGuiKey_E;
+        case kVK_ANSI_R: return ImGuiKey_R;
+        case kVK_ANSI_Y: return ImGuiKey_Y;
+        case kVK_ANSI_T: return ImGuiKey_T;
+        case kVK_ANSI_1: return ImGuiKey_1;
+        case kVK_ANSI_2: return ImGuiKey_2;
+        case kVK_ANSI_3: return ImGuiKey_3;
+        case kVK_ANSI_4: return ImGuiKey_4;
+        case kVK_ANSI_6: return ImGuiKey_6;
+        case kVK_ANSI_5: return ImGuiKey_5;
+        case kVK_ANSI_Equal: return ImGuiKey_Equal;
+        case kVK_ANSI_9: return ImGuiKey_9;
+        case kVK_ANSI_7: return ImGuiKey_7;
+        case kVK_ANSI_Minus: return ImGuiKey_Minus;
+        case kVK_ANSI_8: return ImGuiKey_8;
+        case kVK_ANSI_0: return ImGuiKey_0;
+        case kVK_ANSI_RightBracket: return ImGuiKey_RightBracket;
+        case kVK_ANSI_O: return ImGuiKey_O;
+        case kVK_ANSI_U: return ImGuiKey_U;
+        case kVK_ANSI_LeftBracket: return ImGuiKey_LeftBracket;
+        case kVK_ANSI_I: return ImGuiKey_I;
+        case kVK_ANSI_P: return ImGuiKey_P;
+        case kVK_ANSI_L: return ImGuiKey_L;
+        case kVK_ANSI_J: return ImGuiKey_J;
+        case kVK_ANSI_Quote: return ImGuiKey_Apostrophe;
+        case kVK_ANSI_K: return ImGuiKey_K;
+        case kVK_ANSI_Semicolon: return ImGuiKey_Semicolon;
+        case kVK_ANSI_Backslash: return ImGuiKey_Backslash;
+        case kVK_ANSI_Comma: return ImGuiKey_Comma;
+        case kVK_ANSI_Slash: return ImGuiKey_Slash;
+        case kVK_ANSI_N: return ImGuiKey_N;
+        case kVK_ANSI_M: return ImGuiKey_M;
+        case kVK_ANSI_Period: return ImGuiKey_Period;
+        case kVK_ANSI_Grave: return ImGuiKey_GraveAccent;
+        case kVK_ANSI_KeypadDecimal: return ImGuiKey_KeypadDecimal;
+        case kVK_ANSI_KeypadMultiply: return ImGuiKey_KeypadMultiply;
+        case kVK_ANSI_KeypadPlus: return ImGuiKey_KeypadAdd;
+        case kVK_ANSI_KeypadClear: return ImGuiKey_NumLock;
+        case kVK_ANSI_KeypadDivide: return ImGuiKey_KeypadDivide;
+        case kVK_ANSI_KeypadEnter: return ImGuiKey_KeypadEnter;
+        case kVK_ANSI_KeypadMinus: return ImGuiKey_KeypadSubtract;
+        case kVK_ANSI_KeypadEquals: return ImGuiKey_KeypadEqual;
+        case kVK_ANSI_Keypad0: return ImGuiKey_Keypad0;
+        case kVK_ANSI_Keypad1: return ImGuiKey_Keypad1;
+        case kVK_ANSI_Keypad2: return ImGuiKey_Keypad2;
+        case kVK_ANSI_Keypad3: return ImGuiKey_Keypad3;
+        case kVK_ANSI_Keypad4: return ImGuiKey_Keypad4;
+        case kVK_ANSI_Keypad5: return ImGuiKey_Keypad5;
+        case kVK_ANSI_Keypad6: return ImGuiKey_Keypad6;
+        case kVK_ANSI_Keypad7: return ImGuiKey_Keypad7;
+        case kVK_ANSI_Keypad8: return ImGuiKey_Keypad8;
+        case kVK_ANSI_Keypad9: return ImGuiKey_Keypad9;
+        case kVK_Return: return ImGuiKey_Enter;
+        case kVK_Tab: return ImGuiKey_Tab;
+        case kVK_Space: return ImGuiKey_Space;
+        case kVK_Delete: return ImGuiKey_Backspace;
+        case kVK_Escape: return ImGuiKey_Escape;
+        case kVK_Command: return ImGuiKey_LeftSuper;
+        case kVK_Shift: return ImGuiKey_LeftShift;
+        case kVK_CapsLock: return ImGuiKey_CapsLock;
+        case kVK_Option: return ImGuiKey_LeftAlt;
+        case kVK_Control: return ImGuiKey_LeftControl;
+        case kVK_RightCommand: return ImGuiKey_RightSuper;
+        case kVK_RightShift: return ImGuiKey_RightShift;
+        case kVK_RightOption: return ImGuiKey_RightAlt;
+        case kVK_RightControl: return ImGuiKey_RightControl;
+//      case kVK_Function: return ImGuiKey_;
+//      case kVK_F17: return ImGuiKey_;
+//      case kVK_VolumeUp: return ImGuiKey_;
+//      case kVK_VolumeDown: return ImGuiKey_;
+//      case kVK_Mute: return ImGuiKey_;
+//      case kVK_F18: return ImGuiKey_;
+//      case kVK_F19: return ImGuiKey_;
+//      case kVK_F20: return ImGuiKey_;
+        case kVK_F5: return ImGuiKey_F5;
+        case kVK_F6: return ImGuiKey_F6;
+        case kVK_F7: return ImGuiKey_F7;
+        case kVK_F3: return ImGuiKey_F3;
+        case kVK_F8: return ImGuiKey_F8;
+        case kVK_F9: return ImGuiKey_F9;
+        case kVK_F11: return ImGuiKey_F11;
+        case kVK_F13: return ImGuiKey_PrintScreen;
+//      case kVK_F16: return ImGuiKey_;
+//      case kVK_F14: return ImGuiKey_;
+        case kVK_F10: return ImGuiKey_F10;
+        case 0x6E: return ImGuiKey_Menu;
+        case kVK_F12: return ImGuiKey_F12;
+//      case kVK_F15: return ImGuiKey_;
+        case kVK_Help: return ImGuiKey_Insert;
+        case kVK_Home: return ImGuiKey_Home;
+        case kVK_PageUp: return ImGuiKey_PageUp;
+        case kVK_ForwardDelete: return ImGuiKey_Delete;
+        case kVK_F4: return ImGuiKey_F4;
+        case kVK_End: return ImGuiKey_End;
+        case kVK_F2: return ImGuiKey_F2;
+        case kVK_PageDown: return ImGuiKey_PageDown;
+        case kVK_F1: return ImGuiKey_F1;
+        case kVK_LeftArrow: return ImGuiKey_LeftArrow;
+        case kVK_RightArrow: return ImGuiKey_RightArrow;
+        case kVK_DownArrow: return ImGuiKey_DownArrow;
+        case kVK_UpArrow: return ImGuiKey_UpArrow;
+        default: return ImGuiKey_None;
+    }
+}
+
+
 bool ImGui_ImplOSX_Init(NSView* view)
 {
     ImGuiIO& io = ImGui::GetIO();
@@ -218,30 +338,6 @@
     //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy)
     io.BackendPlatformName = "imgui_impl_osx";
 
-    // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeyDown[] array.
-    io.KeyMap[ImGuiKey_Tab]             = kVK_Tab;
-    io.KeyMap[ImGuiKey_LeftArrow]       = kVK_LeftArrow;
-    io.KeyMap[ImGuiKey_RightArrow]      = kVK_RightArrow;
-    io.KeyMap[ImGuiKey_UpArrow]         = kVK_UpArrow;
-    io.KeyMap[ImGuiKey_DownArrow]       = kVK_DownArrow;
-    io.KeyMap[ImGuiKey_PageUp]          = kVK_PageUp;
-    io.KeyMap[ImGuiKey_PageDown]        = kVK_PageDown;
-    io.KeyMap[ImGuiKey_Home]            = kVK_Home;
-    io.KeyMap[ImGuiKey_End]             = kVK_End;
-    io.KeyMap[ImGuiKey_Insert]          = kVK_F13;
-    io.KeyMap[ImGuiKey_Delete]          = kVK_ForwardDelete;
-    io.KeyMap[ImGuiKey_Backspace]       = kVK_Delete;
-    io.KeyMap[ImGuiKey_Space]           = kVK_Space;
-    io.KeyMap[ImGuiKey_Enter]           = kVK_Return;
-    io.KeyMap[ImGuiKey_Escape]          = kVK_Escape;
-    io.KeyMap[ImGuiKey_KeyPadEnter]     = kVK_ANSI_KeypadEnter;
-    io.KeyMap[ImGuiKey_A]               = kVK_ANSI_A;
-    io.KeyMap[ImGuiKey_C]               = kVK_ANSI_C;
-    io.KeyMap[ImGuiKey_V]               = kVK_ANSI_V;
-    io.KeyMap[ImGuiKey_X]               = kVK_ANSI_X;
-    io.KeyMap[ImGuiKey_Y]               = kVK_ANSI_Y;
-    io.KeyMap[ImGuiKey_Z]               = kVK_ANSI_Z;
-
     // Load cursors. Some of them are undocumented.
     g_MouseCursorHidden = false;
     g_MouseCursors[ImGuiMouseCursor_Arrow] = [NSCursor arrowCursor];
@@ -298,6 +394,15 @@
     g_KeyEventResponder = [[KeyEventResponder alloc] initWithFrame:NSZeroRect];
     [view addSubview:g_KeyEventResponder];
 
+    // Some events do not raise callbacks of AppView in some circumstances (for example when CMD key is held down).
+    // This monitor taps into global event stream and captures these events.
+    NSEventMask eventMask = NSEventMaskFlagsChanged;
+    [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^NSEvent * _Nullable(NSEvent *event)
+    {
+        ImGui_ImplOSX_HandleEvent(event, g_KeyEventResponder);
+        return event;
+    }];
+
     return true;
 }
 
@@ -346,7 +451,7 @@
     }
 }
 
-void ImGui_ImplOSX_UpdateGamepads()
+static void ImGui_ImplOSX_UpdateGamepads()
 {
     ImGuiIO& io = ImGui::GetIO();
     memset(io.NavInputs, 0, sizeof(io.NavInputs));
@@ -390,6 +495,15 @@
     io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
 }
 
+static void ImGui_ImplOSX_UpdateKeyModifiers()
+{
+    ImGuiIO& io = ImGui::GetIO();
+    io.KeyCtrl  = (g_KeyModifiers & ImGuiKeyModFlags_Ctrl)  != 0;
+    io.KeyShift = (g_KeyModifiers & ImGuiKeyModFlags_Shift) != 0;
+    io.KeyAlt   = (g_KeyModifiers & ImGuiKeyModFlags_Alt)   != 0;
+    io.KeySuper = (g_KeyModifiers & ImGuiKeyModFlags_Super) != 0;
+}
+
 void ImGui_ImplOSX_NewFrame(NSView* view)
 {
     // Setup display size
@@ -411,28 +525,11 @@
     io.DeltaTime = (float)(current_time - g_Time);
     g_Time = current_time;
 
+    ImGui_ImplOSX_UpdateKeyModifiers();
     ImGui_ImplOSX_UpdateMouseCursorAndButtons();
     ImGui_ImplOSX_UpdateGamepads();
 }
 
-NSString* NSStringFromPhase(NSEventPhase phase)
-{
-    static NSString* strings[] =
-    {
-        @"none",
-        @"began",
-        @"stationary",
-        @"changed",
-        @"ended",
-        @"cancelled",
-        @"mayBegin",
-    };
-
-    int pos = phase == NSEventPhaseNone ? 0 : __builtin_ctzl((NSUInteger)phase) + 1;
-
-    return strings[pos];
-}
-
 bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view)
 {
     ImGuiIO& io = ImGui::GetIO();
@@ -499,8 +596,6 @@
             wheel_dy = [event deltaY];
         }
 
-        //NSLog(@"dx=%0.3ff, dy=%0.3f, phase=%@", wheel_dx, wheel_dy, NSStringFromPhase(event.phase));
-
         if (fabs(wheel_dx) > 0.0)
             io.MouseWheelH += (float)wheel_dx * 0.1f;
         if (fabs(wheel_dy) > 0.0)
@@ -510,35 +605,66 @@
 
     if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp)
     {
-        unsigned short code = event.keyCode;
-        IM_ASSERT(code >= 0 && code < IM_ARRAYSIZE(io.KeysDown));
-        io.KeysDown[code] = event.type == NSEventTypeKeyDown;
-        NSEventModifierFlags flags = event.modifierFlags;
-        io.KeyCtrl  = (flags & NSEventModifierFlagControl) != 0;
-        io.KeyShift = (flags & NSEventModifierFlagShift) != 0;
-        io.KeyAlt   = (flags & NSEventModifierFlagOption) != 0;
-        io.KeySuper = (flags & NSEventModifierFlagCommand) != 0;
+        if ([event isARepeat])
+            return io.WantCaptureKeyboard;
+
+        unsigned short key_code = [event keyCode];
+
+        ImGuiKey key = ImGui_ImplOSX_KeyCodeToImGuiKey(key_code);
+        if (key != ImGuiKey_None)
+            io.AddKeyEvent(key, event.type == NSEventTypeKeyDown, key_code);
+
         return io.WantCaptureKeyboard;
     }
 
     if (event.type == NSEventTypeFlagsChanged)
     {
-        NSEventModifierFlags flags = event.modifierFlags;
-        switch (event.keyCode)
+        unsigned short key_code = [event keyCode];
+        unsigned int flags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
+
+        ImGuiKeyModFlags imgui_flags = ImGuiKeyModFlags_None;
+        if (flags & NSEventModifierFlagShift)
+            imgui_flags |= ImGuiKeyModFlags_Shift;
+        if (flags & NSEventModifierFlagControl)
+            imgui_flags |= ImGuiKeyModFlags_Ctrl;
+        if (flags & NSEventModifierFlagOption)
+            imgui_flags |= ImGuiKeyModFlags_Alt;
+        if (flags & NSEventModifierFlagCommand)
+            imgui_flags |= ImGuiKeyModFlags_Super;
+
+        g_KeyModifiers = imgui_flags;
+
+        ImGuiKey key = ImGui_ImplOSX_KeyCodeToImGuiKey(key_code);
+        if (key != ImGuiKey_None)
         {
-        case kVK_Control:
-            io.KeyCtrl = (flags & NSEventModifierFlagControl) != 0;
-            break;
-        case kVK_Shift:
-            io.KeyShift = (flags & NSEventModifierFlagShift) != 0;
-            break;
-        case kVK_Option:
-            io.KeyAlt = (flags & NSEventModifierFlagOption) != 0;
-            break;
-        case kVK_Command:
-            io.KeySuper = (flags & NSEventModifierFlagCommand) != 0;
-            break;
+            // macOS does not generate down/up event for modifiers. We're trying
+            // to use hardware dependent masks to extract that information.
+            // 'imgui_mask' is left as a fallback.
+            NSEventModifierFlags mask = 0;
+            ImGuiKeyModFlags imgui_mask = ImGuiKeyModFlags_None;
+            switch (key_code)
+            {
+                case kVK_Control:      mask = 0x0001; imgui_mask = ImGuiKeyModFlags_Ctrl; break;
+                case kVK_RightControl: mask = 0x2000; imgui_mask = ImGuiKeyModFlags_Ctrl; break;
+                case kVK_Shift:        mask = 0x0002; imgui_mask = ImGuiKeyModFlags_Shift; break;
+                case kVK_RightShift:   mask = 0x0004; imgui_mask = ImGuiKeyModFlags_Shift; break;
+                case kVK_Command:      mask = 0x0008; imgui_mask = ImGuiKeyModFlags_Super; break;
+                case kVK_RightCommand: mask = 0x0010; imgui_mask = ImGuiKeyModFlags_Super; break;
+                case kVK_Option:       mask = 0x0020; imgui_mask = ImGuiKeyModFlags_Alt; break;
+                case kVK_RightOption:  mask = 0x0040; imgui_mask = ImGuiKeyModFlags_Alt; break;
+            }
+
+            if (mask)
+            {
+                NSEventModifierFlags modifier_flags = [event modifierFlags];
+                io.AddKeyEvent(key, (modifier_flags & mask) != 0, key_code);
+            }
+            else if (imgui_mask)
+            {
+                io.AddKeyEvent(key, (imgui_flags & imgui_mask) != 0, key_code);
+            }
         }
+
         return io.WantCaptureKeyboard;
     }
 
diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp
index f87393f..99511b6 100644
--- a/backends/imgui_impl_sdl.cpp
+++ b/backends/imgui_impl_sdl.cpp
@@ -4,10 +4,10 @@
 // (Prefer SDL 2.0.5+ for full feature support.)
 
 // Implemented features:
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 //  [X] Platform: Clipboard support.
-//  [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE).
+//  [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 SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
+//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 //  [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
 // Missing features:
 //  [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
@@ -21,6 +21,7 @@
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 //  2021-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
+//  2022-01-XX: Inputs: calling new io.AddKeyEvent() API (1.87+), support for full keys range.
 //  2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST.
 //  2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using SDL_GetMouseFocus() + SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, requires SDL 2.0.5+)
 //  2021-06:29: *BREAKING CHANGE* Removed 'SDL_Window* window' parameter to ImGui_ImplSDL2_NewFrame() which was unnecessary.
@@ -122,6 +123,119 @@
     SDL_SetClipboardText(text);
 }
 
+static ImGuiKey ImGui_ImplSDL2_ScancodeToImGuiKey(int scancode)
+{
+    switch (scancode)
+    {
+        case SDL_SCANCODE_TAB: return ImGuiKey_Tab;
+        case SDL_SCANCODE_LEFT: return ImGuiKey_LeftArrow;
+        case SDL_SCANCODE_RIGHT: return ImGuiKey_RightArrow;
+        case SDL_SCANCODE_UP: return ImGuiKey_UpArrow;
+        case SDL_SCANCODE_DOWN: return ImGuiKey_DownArrow;
+        case SDL_SCANCODE_PAGEUP: return ImGuiKey_PageUp;
+        case SDL_SCANCODE_PAGEDOWN: return ImGuiKey_PageDown;
+        case SDL_SCANCODE_HOME: return ImGuiKey_Home;
+        case SDL_SCANCODE_END: return ImGuiKey_End;
+        case SDL_SCANCODE_INSERT: return ImGuiKey_Insert;
+        case SDL_SCANCODE_DELETE: return ImGuiKey_Delete;
+        case SDL_SCANCODE_BACKSPACE: return ImGuiKey_Backspace;
+        case SDL_SCANCODE_SPACE: return ImGuiKey_Space;
+        case SDL_SCANCODE_RETURN: return ImGuiKey_Enter;
+        case SDL_SCANCODE_ESCAPE: return ImGuiKey_Escape;
+        case SDL_SCANCODE_APOSTROPHE: return ImGuiKey_Apostrophe;
+        case SDL_SCANCODE_COMMA: return ImGuiKey_Comma;
+        case SDL_SCANCODE_MINUS: return ImGuiKey_Minus;
+        case SDL_SCANCODE_PERIOD: return ImGuiKey_Period;
+        case SDL_SCANCODE_SLASH: return ImGuiKey_Slash;
+        case SDL_SCANCODE_SEMICOLON: return ImGuiKey_Semicolon;
+        case SDL_SCANCODE_EQUALS: return ImGuiKey_Equal;
+        case SDL_SCANCODE_LEFTBRACKET: return ImGuiKey_LeftBracket;
+        case SDL_SCANCODE_BACKSLASH: return ImGuiKey_Backslash;
+        case SDL_SCANCODE_RIGHTBRACKET: return ImGuiKey_RightBracket;
+        case SDL_SCANCODE_GRAVE: return ImGuiKey_GraveAccent;
+        case SDL_SCANCODE_CAPSLOCK: return ImGuiKey_CapsLock;
+        case SDL_SCANCODE_SCROLLLOCK: return ImGuiKey_ScrollLock;
+        case SDL_SCANCODE_NUMLOCKCLEAR: return ImGuiKey_NumLock;
+        case SDL_SCANCODE_PRINTSCREEN: return ImGuiKey_PrintScreen;
+        case SDL_SCANCODE_PAUSE: return ImGuiKey_Pause;
+        case SDL_SCANCODE_KP_0: return ImGuiKey_Keypad0;
+        case SDL_SCANCODE_KP_1: return ImGuiKey_Keypad1;
+        case SDL_SCANCODE_KP_2: return ImGuiKey_Keypad2;
+        case SDL_SCANCODE_KP_3: return ImGuiKey_Keypad3;
+        case SDL_SCANCODE_KP_4: return ImGuiKey_Keypad4;
+        case SDL_SCANCODE_KP_5: return ImGuiKey_Keypad5;
+        case SDL_SCANCODE_KP_6: return ImGuiKey_Keypad6;
+        case SDL_SCANCODE_KP_7: return ImGuiKey_Keypad7;
+        case SDL_SCANCODE_KP_8: return ImGuiKey_Keypad8;
+        case SDL_SCANCODE_KP_9: return ImGuiKey_Keypad9;
+        case SDL_SCANCODE_KP_PERIOD: return ImGuiKey_KeypadDecimal;
+        case SDL_SCANCODE_KP_DIVIDE: return ImGuiKey_KeypadDivide;
+        case SDL_SCANCODE_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;
+        case SDL_SCANCODE_KP_MINUS: return ImGuiKey_KeypadSubtract;
+        case SDL_SCANCODE_KP_PLUS: return ImGuiKey_KeypadAdd;
+        case SDL_SCANCODE_KP_ENTER: return ImGuiKey_KeypadEnter;
+        case SDL_SCANCODE_KP_EQUALS: return ImGuiKey_KeypadEqual;
+        case SDL_SCANCODE_LSHIFT: return ImGuiKey_LeftShift;
+        case SDL_SCANCODE_LCTRL: return ImGuiKey_LeftControl;
+        case SDL_SCANCODE_LALT: return ImGuiKey_LeftAlt;
+        case SDL_SCANCODE_LGUI: return ImGuiKey_LeftSuper;
+        case SDL_SCANCODE_RSHIFT: return ImGuiKey_RightShift;
+        case SDL_SCANCODE_RCTRL: return ImGuiKey_RightControl;
+        case SDL_SCANCODE_RALT: return ImGuiKey_RightAlt;
+        case SDL_SCANCODE_RGUI: return ImGuiKey_RightSuper;
+        case SDL_SCANCODE_MENU: return ImGuiKey_Menu;
+        case SDL_SCANCODE_0: return ImGuiKey_0;
+        case SDL_SCANCODE_1: return ImGuiKey_1;
+        case SDL_SCANCODE_2: return ImGuiKey_2;
+        case SDL_SCANCODE_3: return ImGuiKey_3;
+        case SDL_SCANCODE_4: return ImGuiKey_4;
+        case SDL_SCANCODE_5: return ImGuiKey_5;
+        case SDL_SCANCODE_6: return ImGuiKey_6;
+        case SDL_SCANCODE_7: return ImGuiKey_7;
+        case SDL_SCANCODE_8: return ImGuiKey_8;
+        case SDL_SCANCODE_9: return ImGuiKey_9;
+        case SDL_SCANCODE_A: return ImGuiKey_A;
+        case SDL_SCANCODE_B: return ImGuiKey_B;
+        case SDL_SCANCODE_C: return ImGuiKey_C;
+        case SDL_SCANCODE_D: return ImGuiKey_D;
+        case SDL_SCANCODE_E: return ImGuiKey_E;
+        case SDL_SCANCODE_F: return ImGuiKey_F;
+        case SDL_SCANCODE_G: return ImGuiKey_G;
+        case SDL_SCANCODE_H: return ImGuiKey_H;
+        case SDL_SCANCODE_I: return ImGuiKey_I;
+        case SDL_SCANCODE_J: return ImGuiKey_J;
+        case SDL_SCANCODE_K: return ImGuiKey_K;
+        case SDL_SCANCODE_L: return ImGuiKey_L;
+        case SDL_SCANCODE_M: return ImGuiKey_M;
+        case SDL_SCANCODE_N: return ImGuiKey_N;
+        case SDL_SCANCODE_O: return ImGuiKey_O;
+        case SDL_SCANCODE_P: return ImGuiKey_P;
+        case SDL_SCANCODE_Q: return ImGuiKey_Q;
+        case SDL_SCANCODE_R: return ImGuiKey_R;
+        case SDL_SCANCODE_S: return ImGuiKey_S;
+        case SDL_SCANCODE_T: return ImGuiKey_T;
+        case SDL_SCANCODE_U: return ImGuiKey_U;
+        case SDL_SCANCODE_V: return ImGuiKey_V;
+        case SDL_SCANCODE_W: return ImGuiKey_W;
+        case SDL_SCANCODE_X: return ImGuiKey_X;
+        case SDL_SCANCODE_Y: return ImGuiKey_Y;
+        case SDL_SCANCODE_Z: return ImGuiKey_Z;
+        case SDL_SCANCODE_F1: return ImGuiKey_F1;
+        case SDL_SCANCODE_F2: return ImGuiKey_F2;
+        case SDL_SCANCODE_F3: return ImGuiKey_F3;
+        case SDL_SCANCODE_F4: return ImGuiKey_F4;
+        case SDL_SCANCODE_F5: return ImGuiKey_F5;
+        case SDL_SCANCODE_F6: return ImGuiKey_F6;
+        case SDL_SCANCODE_F7: return ImGuiKey_F7;
+        case SDL_SCANCODE_F8: return ImGuiKey_F8;
+        case SDL_SCANCODE_F9: return ImGuiKey_F9;
+        case SDL_SCANCODE_F10: return ImGuiKey_F10;
+        case SDL_SCANCODE_F11: return ImGuiKey_F11;
+        case SDL_SCANCODE_F12: return ImGuiKey_F12;
+        default: return ImGuiKey_None;
+    }
+}
+
 // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
 // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
 // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
@@ -157,17 +271,9 @@
     case SDL_KEYDOWN:
     case SDL_KEYUP:
         {
-            int key = event->key.keysym.scancode;
-            IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown));
-            io.KeysDown[key] = (event->type == SDL_KEYDOWN);
-            io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
-            io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
-            io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
-#ifdef _WIN32
-            io.KeySuper = false;
-#else
-            io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
-#endif
+            ImGuiKey key = ImGui_ImplSDL2_ScancodeToImGuiKey(event->key.keysym.scancode);
+            if (key != ImGuiKey_None)
+                io.AddKeyEvent(key, event->type == SDL_KEYDOWN, event->key.keysym.sym, event->key.keysym.scancode);
             return true;
         }
     // Multi-viewport support
@@ -221,30 +327,6 @@
     bd->Window = window;
     bd->MouseCanUseGlobalState = mouse_can_use_global_state;
 
-    // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array.
-    io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB;
-    io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT;
-    io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT;
-    io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP;
-    io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN;
-    io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP;
-    io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN;
-    io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME;
-    io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END;
-    io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT;
-    io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE;
-    io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE;
-    io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE;
-    io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN;
-    io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE;
-    io.KeyMap[ImGuiKey_KeyPadEnter] = SDL_SCANCODE_KP_ENTER;
-    io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A;
-    io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C;
-    io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V;
-    io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X;
-    io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y;
-    io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z;
-
     io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;
     io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText;
     io.ClipboardUserData = NULL;
@@ -507,6 +589,17 @@
     }
 }
 
+static void ImGui_ImplSDL2_UpdateKeyModifiers()
+{
+    SDL_Keymod keymod = SDL_GetModState();
+
+    ImGuiIO& io = ImGui::GetIO();
+    io.KeyShift = (keymod & KMOD_SHIFT) != 0;
+    io.KeyCtrl  = (keymod & KMOD_CTRL) != 0;
+    io.KeyAlt   = (keymod & KMOD_ALT) != 0;
+    io.KeySuper = (keymod & KMOD_GUI) != 0;
+}
+
 void ImGui_ImplSDL2_NewFrame()
 {
     ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
@@ -530,6 +623,9 @@
     io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f);
     bd->Time = current_time;
 
+    // Update key modifiers
+    ImGui_ImplSDL2_UpdateKeyModifiers();
+
     ImGui_ImplSDL2_UpdateMousePosAndButtons();
     ImGui_ImplSDL2_UpdateMouseCursor();
 
diff --git a/backends/imgui_impl_sdl.h b/backends/imgui_impl_sdl.h
index 18bb7b2..62d5001 100644
--- a/backends/imgui_impl_sdl.h
+++ b/backends/imgui_impl_sdl.h
@@ -3,10 +3,10 @@
 // (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
 
 // Implemented features:
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 //  [X] Platform: Clipboard support.
-//  [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE).
+//  [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 SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
+//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 //  [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
 // Missing features:
 //  [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp
index 5d7173e..d3bded5 100644
--- a/backends/imgui_impl_win32.cpp
+++ b/backends/imgui_impl_win32.cpp
@@ -3,9 +3,9 @@
 
 // Implemented features:
 //  [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
-//  [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
+//  [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 VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
+//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 //  [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
@@ -35,6 +35,7 @@
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 //  2021-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
+//  2022-01-XX: Inputs: calling new io.AddKeyEvent() API (1.87+), support for full keys range.
 //  2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness.
 //  2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages.
 //  2021-08-02: Inputs: Fixed keyboard modifiers being reported when host window doesn't have focus.
@@ -139,30 +140,6 @@
     if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
         ImGui_ImplWin32_InitPlatformInterface();
 
-    // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime.
-    io.KeyMap[ImGuiKey_Tab] = VK_TAB;
-    io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
-    io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;
-    io.KeyMap[ImGuiKey_UpArrow] = VK_UP;
-    io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN;
-    io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR;
-    io.KeyMap[ImGuiKey_PageDown] = VK_NEXT;
-    io.KeyMap[ImGuiKey_Home] = VK_HOME;
-    io.KeyMap[ImGuiKey_End] = VK_END;
-    io.KeyMap[ImGuiKey_Insert] = VK_INSERT;
-    io.KeyMap[ImGuiKey_Delete] = VK_DELETE;
-    io.KeyMap[ImGuiKey_Backspace] = VK_BACK;
-    io.KeyMap[ImGuiKey_Space] = VK_SPACE;
-    io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
-    io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
-    io.KeyMap[ImGuiKey_KeyPadEnter] = VK_RETURN;
-    io.KeyMap[ImGuiKey_A] = 'A';
-    io.KeyMap[ImGuiKey_C] = 'C';
-    io.KeyMap[ImGuiKey_V] = 'V';
-    io.KeyMap[ImGuiKey_X] = 'X';
-    io.KeyMap[ImGuiKey_Y] = 'Y';
-    io.KeyMap[ImGuiKey_Z] = 'Z';
-
     // Dynamically load XInput library
 #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
     const char* xinput_dll_names[] =
@@ -238,6 +215,37 @@
     return true;
 }
 
+static bool IsVkDown(int vk)
+{
+    return (::GetKeyState(vk) & 0x8000) != 0;
+}
+
+static void ImGui_ImplWin32_ProcessKeyEventsWorkarounds()
+{
+    ImGuiIO& io = ImGui::GetIO();
+
+    // Left & right Shift keys: when both are pressed together, Windows tend to not generate the WM_KEYUP event for the first released one.
+    if (ImGui::IsKeyDown(ImGuiKey_LeftShift) && !IsVkDown(VK_LSHIFT))
+        io.AddKeyEvent(ImGuiKey_LeftShift, false);
+    if (ImGui::IsKeyDown(ImGuiKey_RightShift) && !IsVkDown(VK_RSHIFT))
+        io.AddKeyEvent(ImGuiKey_RightShift, false);
+
+    // Sometimes WM_KEYUP for Win key is not passed down to the app (e.g. for Win+V on some setups, according to GLFW).
+    if (ImGui::IsKeyDown(ImGuiKey_LeftSuper) && !IsVkDown(VK_LWIN))
+        io.AddKeyEvent(ImGuiKey_LeftSuper, false);
+    if (ImGui::IsKeyDown(ImGuiKey_RightSuper) && !IsVkDown(VK_RWIN))
+        io.AddKeyEvent(ImGuiKey_RightSuper, false);
+}
+
+static void ImGui_ImplWin32_UpdateKeyModifiers()
+{
+    ImGuiIO& io = ImGui::GetIO();
+    io.KeyShift = IsVkDown(VK_LSHIFT) || IsVkDown(VK_RSHIFT);
+    io.KeyCtrl  = IsVkDown(VK_LCONTROL) || IsVkDown(VK_RCONTROL);
+    io.KeyAlt   = IsVkDown(VK_LMENU) || IsVkDown(VK_RMENU);
+    io.KeySuper = IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN);
+}
+
 // This code supports multi-viewports (multiple OS Windows mapped into different Dear ImGui viewports)
 // Because of that, it is a little more complicated than your typical single-viewport binding code!
 static void ImGui_ImplWin32_UpdateMousePos()
@@ -399,6 +407,12 @@
     io.DeltaTime = (float)(current_time - bd->Time) / bd->TicksPerSecond;
     bd->Time = current_time;
 
+    // Process workarounds for known Windows key handling issues
+    ImGui_ImplWin32_ProcessKeyEventsWorkarounds();
+
+    // Update key modifiers
+    ImGui_ImplWin32_UpdateKeyModifiers();
+
     // Update OS mouse position
     ImGui_ImplWin32_UpdateMousePos();
 
@@ -414,6 +428,122 @@
     ImGui_ImplWin32_UpdateGamepads();
 }
 
+// There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED, we assign it an arbitrary value to make code more readable (VK_ codes go up to 255)
+#define IM_VK_KEYPAD_ENTER      (VK_RETURN + 256)
+
+// Map VK_xxx to ImGuiKey_xxx.
+static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam)
+{
+    switch (wParam)
+    {
+        case VK_TAB: return ImGuiKey_Tab;
+        case VK_LEFT: return ImGuiKey_LeftArrow;
+        case VK_RIGHT: return ImGuiKey_RightArrow;
+        case VK_UP: return ImGuiKey_UpArrow;
+        case VK_DOWN: return ImGuiKey_DownArrow;
+        case VK_PRIOR: return ImGuiKey_PageUp;
+        case VK_NEXT: return ImGuiKey_PageDown;
+        case VK_HOME: return ImGuiKey_Home;
+        case VK_END: return ImGuiKey_End;
+        case VK_INSERT: return ImGuiKey_Insert;
+        case VK_DELETE: return ImGuiKey_Delete;
+        case VK_BACK: return ImGuiKey_Backspace;
+        case VK_SPACE: return ImGuiKey_Space;
+        case VK_RETURN: return ImGuiKey_Enter;
+        case VK_ESCAPE: return ImGuiKey_Escape;
+        case VK_OEM_7: return ImGuiKey_Apostrophe;
+        case VK_OEM_COMMA: return ImGuiKey_Comma;
+        case VK_OEM_MINUS: return ImGuiKey_Minus;
+        case VK_OEM_PERIOD: return ImGuiKey_Period;
+        case VK_OEM_2: return ImGuiKey_Slash;
+        case VK_OEM_1: return ImGuiKey_Semicolon;
+        case VK_OEM_PLUS: return ImGuiKey_Equal;
+        case VK_OEM_4: return ImGuiKey_LeftBracket;
+        case VK_OEM_5: return ImGuiKey_Backslash;
+        case VK_OEM_6: return ImGuiKey_RightBracket;
+        case VK_OEM_3: return ImGuiKey_GraveAccent;
+        case VK_CAPITAL: return ImGuiKey_CapsLock;
+        case VK_SCROLL: return ImGuiKey_ScrollLock;
+        case VK_NUMLOCK: return ImGuiKey_NumLock;
+        case VK_SNAPSHOT: return ImGuiKey_PrintScreen;
+        case VK_PAUSE: return ImGuiKey_Pause;
+        case VK_NUMPAD0: return ImGuiKey_Keypad0;
+        case VK_NUMPAD1: return ImGuiKey_Keypad1;
+        case VK_NUMPAD2: return ImGuiKey_Keypad2;
+        case VK_NUMPAD3: return ImGuiKey_Keypad3;
+        case VK_NUMPAD4: return ImGuiKey_Keypad4;
+        case VK_NUMPAD5: return ImGuiKey_Keypad5;
+        case VK_NUMPAD6: return ImGuiKey_Keypad6;
+        case VK_NUMPAD7: return ImGuiKey_Keypad7;
+        case VK_NUMPAD8: return ImGuiKey_Keypad8;
+        case VK_NUMPAD9: return ImGuiKey_Keypad9;
+        case VK_DECIMAL: return ImGuiKey_KeypadDecimal;
+        case VK_DIVIDE: return ImGuiKey_KeypadDivide;
+        case VK_MULTIPLY: return ImGuiKey_KeypadMultiply;
+        case VK_SUBTRACT: return ImGuiKey_KeypadSubtract;
+        case VK_ADD: return ImGuiKey_KeypadAdd;
+        case IM_VK_KEYPAD_ENTER: return ImGuiKey_KeypadEnter;
+        case VK_LSHIFT: return ImGuiKey_LeftShift;
+        case VK_LCONTROL: return ImGuiKey_LeftControl;
+        case VK_LMENU: return ImGuiKey_LeftAlt;
+        case VK_LWIN: return ImGuiKey_LeftSuper;
+        case VK_RSHIFT: return ImGuiKey_RightShift;
+        case VK_RCONTROL: return ImGuiKey_RightControl;
+        case VK_RMENU: return ImGuiKey_RightAlt;
+        case VK_RWIN: return ImGuiKey_RightSuper;
+        case VK_APPS: return ImGuiKey_Menu;
+        case '0': return ImGuiKey_0;
+        case '1': return ImGuiKey_1;
+        case '2': return ImGuiKey_2;
+        case '3': return ImGuiKey_3;
+        case '4': return ImGuiKey_4;
+        case '5': return ImGuiKey_5;
+        case '6': return ImGuiKey_6;
+        case '7': return ImGuiKey_7;
+        case '8': return ImGuiKey_8;
+        case '9': return ImGuiKey_9;
+        case 'A': return ImGuiKey_A;
+        case 'B': return ImGuiKey_B;
+        case 'C': return ImGuiKey_C;
+        case 'D': return ImGuiKey_D;
+        case 'E': return ImGuiKey_E;
+        case 'F': return ImGuiKey_F;
+        case 'G': return ImGuiKey_G;
+        case 'H': return ImGuiKey_H;
+        case 'I': return ImGuiKey_I;
+        case 'J': return ImGuiKey_J;
+        case 'K': return ImGuiKey_K;
+        case 'L': return ImGuiKey_L;
+        case 'M': return ImGuiKey_M;
+        case 'N': return ImGuiKey_N;
+        case 'O': return ImGuiKey_O;
+        case 'P': return ImGuiKey_P;
+        case 'Q': return ImGuiKey_Q;
+        case 'R': return ImGuiKey_R;
+        case 'S': return ImGuiKey_S;
+        case 'T': return ImGuiKey_T;
+        case 'U': return ImGuiKey_U;
+        case 'V': return ImGuiKey_V;
+        case 'W': return ImGuiKey_W;
+        case 'X': return ImGuiKey_X;
+        case 'Y': return ImGuiKey_Y;
+        case 'Z': return ImGuiKey_Z;
+        case VK_F1: return ImGuiKey_F1;
+        case VK_F2: return ImGuiKey_F2;
+        case VK_F3: return ImGuiKey_F3;
+        case VK_F4: return ImGuiKey_F4;
+        case VK_F5: return ImGuiKey_F5;
+        case VK_F6: return ImGuiKey_F6;
+        case VK_F7: return ImGuiKey_F7;
+        case VK_F8: return ImGuiKey_F8;
+        case VK_F9: return ImGuiKey_F9;
+        case VK_F10: return ImGuiKey_F10;
+        case VK_F11: return ImGuiKey_F11;
+        case VK_F12: return ImGuiKey_F12;
+        default: return ImGuiKey_None;
+    }
+}
+
 // Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions.
 #ifndef WM_MOUSEHWHEEL
 #define WM_MOUSEHWHEEL 0x020E
@@ -500,26 +630,39 @@
     case WM_SYSKEYDOWN:
     case WM_SYSKEYUP:
     {
-        bool down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN);
+        const bool is_key_down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN);
+
         if (wParam < 256)
-            io.KeysDown[wParam] = down;
-        if (wParam == VK_CONTROL)
         {
-            io.KeysDown[VK_LCONTROL] = ((::GetKeyState(VK_LCONTROL) & 0x8000) != 0);
-            io.KeysDown[VK_RCONTROL] = ((::GetKeyState(VK_RCONTROL) & 0x8000) != 0);
-            io.KeyCtrl = io.KeysDown[VK_LCONTROL] || io.KeysDown[VK_RCONTROL];
-        }
-        if (wParam == VK_SHIFT)
-        {
-            io.KeysDown[VK_LSHIFT] = ((::GetKeyState(VK_LSHIFT) & 0x8000) != 0);
-            io.KeysDown[VK_RSHIFT] = ((::GetKeyState(VK_RSHIFT) & 0x8000) != 0);
-            io.KeyShift            = io.KeysDown[VK_LSHIFT] || io.KeysDown[VK_RSHIFT];
-        }
-        if (wParam == VK_MENU)
-        {
-            io.KeysDown[VK_LMENU] = ((::GetKeyState(VK_LMENU) & 0x8000) != 0);
-            io.KeysDown[VK_RMENU] = ((::GetKeyState(VK_RMENU) & 0x8000) != 0);
-            io.KeyAlt             = io.KeysDown[VK_LMENU] || io.KeysDown[VK_RMENU];
+            // Obtain virtual key code
+            // (keypad enter doesn't have its own... VK_RETURN with KF_EXTENDED flag means keypad enter, see IM_VK_KEYPAD_ENTER definition for details, it is mapped to ImGuiKey_KeyPadEnter.)
+            int vk = (int)wParam;
+            if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED))
+                vk = IM_VK_KEYPAD_ENTER;
+
+            // Submit key event
+            const ImGuiKey imgui_key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk);
+            const int scancode = (int)LOBYTE(HIWORD(lParam));
+            if (imgui_key != ImGuiKey_None)
+                io.AddKeyEvent(imgui_key, is_key_down, vk, scancode);
+
+            // Submit individual left/right modifier events
+            if (vk == VK_SHIFT)
+            {
+                // Important: Shift keys tend to get stuck when pressed together, missing key-up events are corrected in ImGui_ImplWin32_ProcessKeyEventsWorkarounds()
+                if (IsVkDown(VK_LSHIFT) == is_key_down) { io.AddKeyEvent(ImGuiKey_LeftShift, is_key_down, VK_LSHIFT, scancode); }
+                if (IsVkDown(VK_RSHIFT) == is_key_down) { io.AddKeyEvent(ImGuiKey_RightShift, is_key_down, VK_RSHIFT, scancode); }
+            }
+            else if (vk == VK_CONTROL)
+            {
+                if (IsVkDown(VK_LCONTROL) == is_key_down) { io.AddKeyEvent(ImGuiKey_LeftControl, is_key_down, VK_LCONTROL, scancode); }
+                if (IsVkDown(VK_RCONTROL) == is_key_down) { io.AddKeyEvent(ImGuiKey_RightControl, is_key_down, VK_RCONTROL, scancode); }
+            }
+            else if (vk == VK_MENU)
+            {
+                if (IsVkDown(VK_LMENU) == is_key_down) { io.AddKeyEvent(ImGuiKey_LeftAlt, is_key_down, VK_LMENU, scancode); }
+                if (IsVkDown(VK_RMENU) == is_key_down) { io.AddKeyEvent(ImGuiKey_RightAlt, is_key_down, VK_RMENU, scancode); }
+            }
         }
         return 0;
     }
diff --git a/backends/imgui_impl_win32.h b/backends/imgui_impl_win32.h
index 7a9437a..3b000d0 100644
--- a/backends/imgui_impl_win32.h
+++ b/backends/imgui_impl_win32.h
@@ -3,12 +3,12 @@
 
 // Implemented features:
 //  [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui)
-//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
-//  [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
+//  [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 VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
 //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
+//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
 //  [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
 
-// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 
+// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // 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
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index fb5df30..b499afc 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -103,6 +103,47 @@
  VERSION 1.87 WIP (In Progress)
 -----------------------------------------------------------------------
 
+Breaking Changes:
+
+- Reworked IO keyboard input system. (#2625, #3724) [@thedmd]
+  - Added io.AddKeyEvent() function obsoleting writing directly to io.KeyMap[], io.KeysDown[] arrays.
+  - Added full range of key enums in ImGuiKey (e.g. ImGuiKey_F1).
+  - Added GetKeyName() helper function.
+  - Obsoleted GetKeyIndex(): it is now unnecessary and will now return the same value.
+  - All keyboard related functions taking 'int user_key_index' now take 'ImGuiKey key':
+    - IsKeyDown(), IsKeyPressed(), IsKeyReleased(), GetKeyPressedAmount().
+  - All backends were updated to use io.AddKeyEvent().
+  - Backward compatibility:
+    - Old backends populating those arrays will still work for a while.
+    - Calling e.g. IsKeyPressed(MY_NATIVE_KEY_XXX) will still work for a while.
+    - Those legacy arrays will only be disabled if '#define IMGUI_DISABLE_OBSOLETE_KEYIO' is set in your imconfig.
+      In a few versions, IMGUI_DISABLE_OBSOLETE_FUNCTIONS will automatically enable IMGUI_DISABLE_OBSOLETE_KEYIO,
+      so this will be moved into the regular obsolescence path.
+  - Transition guide:
+     - IsKeyPressed(MY_NATIVE_KEY_XXX)           -> use IsKeyPressed(ImGuiKey_XXX)
+     - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX))   -> use IsKeyPressed(ImGuiKey_XXX)
+     - Backend writing to io.KeyMap[],KeysDown[] -> backend should call io.AddKeyEvent()
+     - Basically the trick we took advantage of is that we previously only supported native keycode from 0 to 511,
+       so ImGuiKey values can still express a legacy native keycode, and new named keys are all >= 512.
+  - This will enable a few things in the future:
+     - Access to portable keys allows for backed-agnostic keyboard input code. Until now it was difficult to
+       share code using keyboard accross project because of this gap. (#2625, #3724)
+     - Access to full key ranges will allow us to develop a proper keyboard shortcut system. (#456)
+     - io.AddKeyEvent() include native keycode/scancode which will later be exposed to access translated/untranslated keys. (#3141, #2959)
+     - io.AddKeyEvent() will later be turned into a trickling IO queue (for all inputs) to handle very low framerate better. (#2525, #2787, #3383)
+- Renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. (#2625)
+- Commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019)
+  - ImGui::SetNextTreeNodeOpen()        -> use ImGui::SetNextItemOpen()
+  - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x
+  - ImGui::TreeAdvanceToLabelPos()      -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing());
+  - ImFontAtlas::CustomRect             -> use ImFontAtlasCustomRect
+  - ImGuiColorEditFlags_RGB/HSV/HEX     -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex
+
+Other Changes:
+
+- Tables, ImDrawListSplitter: Fixed erroneously stripping trailing ImDrawList::AddCallback() when submitted in
+  last column or last channel and when there are no other drawing operation. (#4843, #4844) [@hoffstadt]
+- Backends: Allegro5, GLFW, GLUT, SDL, OSX, Win32: Updated to use io.AddKeyEvent() with complete key range. (#2625) [@thedmd]
 - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk]
   It would generally not have noticeable side-effect at runtime but would be detected by runtime checkers.
 - Backends: Metal: Added Apple Metal C++ API support. (#4824, #4746) [@luigifcruz]
diff --git a/docs/README.md b/docs/README.md
index 3190f7d..6569f45 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -132,7 +132,7 @@
 
 ### Upcoming Changes
 
-Some of the goals for 2021 are:
+Some of the goals for 2022 are:
 - Work on Docking (see [#2109](https://github.com/ocornut/imgui/issues/2109), in public [docking](https://github.com/ocornut/imgui/tree/docking) branch)
 - Work on Multi-Viewport / Multiple OS windows. (see [#1542](https://github.com/ocornut/imgui/issues/1542), in public [docking](https://github.com/ocornut/imgui/tree/docking) branch looking for feedback)
 - Work on gamepad/keyboard controls. (see [#787](https://github.com/ocornut/imgui/issues/787))
diff --git a/examples/example_allegro5/main.cpp b/examples/example_allegro5/main.cpp
index 6306fdc..08733df 100644
--- a/examples/example_allegro5/main.cpp
+++ b/examples/example_allegro5/main.cpp
@@ -102,6 +102,11 @@
 
             ImGui::Begin("Hello, world!");                          // Create a window called "Hello, world!" and append into it.
 
+            ImGui::Text("IsKeyDown(ALLEGRO_KEY_SPACE) = %d", ImGui::IsKeyDown(ALLEGRO_KEY_SPACE));
+            ImGui::Text("IsKeyDown(ALLEGRO_KEY_P) = %d", ImGui::IsKeyDown(ALLEGRO_KEY_P));
+            ImGui::Text("IsKeyDown(ImGuiKey_Space) = %d", ImGui::IsKeyDown(ImGuiKey_Space));
+            ImGui::Text("IsKeyDown(ImGuiKey_P) = %d", ImGui::IsKeyDown(ImGuiKey_P));
+
             ImGui::Text("This is some useful text.");               // Display some text (you can use a format strings too)
             ImGui::Checkbox("Demo Window", &show_demo_window);      // Edit bools storing our window open/close state
             ImGui::Checkbox("Another Window", &show_another_window);
diff --git a/examples/example_apple_metal/main.mm b/examples/example_apple_metal/main.mm
index bbe51d3..5c05fee 100644
--- a/examples/example_apple_metal/main.mm
+++ b/examples/example_apple_metal/main.mm
@@ -107,18 +107,6 @@
                                                                userInfo:nil];
     [self.view addTrackingArea:trackingArea];
 
-    // If we want to receive key events, we either need to be in the responder chain of the key view,
-    // or else we can install a local monitor. The consequence of this heavy-handed approach is that
-    // we receive events for all controls, not just Dear ImGui widgets. If we had native controls in our
-    // window, we'd want to be much more careful than just ingesting the complete event stream.
-    // To match the behavior of other backends, we pass every event down to the OS.
-    NSEventMask eventMask = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged;
-    [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^NSEvent * _Nullable(NSEvent *event)
-    {
-        ImGui_ImplOSX_HandleEvent(event, self.view);
-        return event;
-    }];
-
     ImGui_ImplOSX_Init(self.view);
 
 #endif
@@ -223,8 +211,7 @@
 
 #if TARGET_OS_OSX
 
-// Forward Mouse/Keyboard events to Dear ImGui OSX backend.
-// Other events are registered via addLocalMonitorForEventsMatchingMask()
+// Forward Mouse events to Dear ImGui OSX backend.
 -(void)mouseDown:(NSEvent *)event           { ImGui_ImplOSX_HandleEvent(event, self.view); }
 -(void)rightMouseDown:(NSEvent *)event      { ImGui_ImplOSX_HandleEvent(event, self.view); }
 -(void)otherMouseDown:(NSEvent *)event      { ImGui_ImplOSX_HandleEvent(event, self.view); }
diff --git a/examples/example_apple_opengl2/main.mm b/examples/example_apple_opengl2/main.mm
index 73b5d8d..8d0ddd1 100644
--- a/examples/example_apple_opengl2/main.mm
+++ b/examples/example_apple_opengl2/main.mm
@@ -36,15 +36,6 @@
 
 -(void)initialize
 {
-    // Some events do not raise callbacks of AppView in some circumstances (for example when CMD key is held down).
-    // This monitor taps into global event stream and captures these events.
-    NSEventMask eventMask = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged;
-    [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^NSEvent * _Nullable(NSEvent *event)
-    {
-        ImGui_ImplOSX_HandleEvent(event, self);
-        return event;
-    }];
-
     // Setup Dear ImGui context
     // FIXME: This example doesn't have proper cleanup...
     IMGUI_CHECKVERSION();
diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp
index f6f1907..ec7246b 100644
--- a/examples/example_glfw_opengl3/main.cpp
+++ b/examples/example_glfw_opengl3/main.cpp
@@ -134,6 +134,11 @@
 
             ImGui::Begin("Hello, world!");                          // Create a window called "Hello, world!" and append into it.
 
+            ImGui::Text("IsKeyDown(GLFW_KEY_SPACE) = %d", ImGui::IsKeyDown(GLFW_KEY_SPACE));
+            ImGui::Text("IsKeyDown(GLFW_KEY_P) = %d", ImGui::IsKeyDown(GLFW_KEY_P));
+            ImGui::Text("IsKeyDown(ImGuiKey_Space) = %d", ImGui::IsKeyDown(ImGuiKey_Space));
+            ImGui::Text("IsKeyDown(ImGuiKey_P) = %d", ImGui::IsKeyDown(ImGuiKey_P));
+
             ImGui::Text("This is some useful text.");               // Display some text (you can use a format strings too)
             ImGui::Checkbox("Demo Window", &show_demo_window);      // Edit bools storing our window open/close state
             ImGui::Checkbox("Another Window", &show_another_window);
diff --git a/examples/example_glut_opengl2/main.cpp b/examples/example_glut_opengl2/main.cpp
index faf5512..f28ebd9 100644
--- a/examples/example_glut_opengl2/main.cpp
+++ b/examples/example_glut_opengl2/main.cpp
@@ -45,6 +45,12 @@
 
         ImGui::Begin("Hello, world!");                          // Create a window called "Hello, world!" and append into it.
 
+        ImGui::Text("IsKeyDown(' ') = %d", ImGui::IsKeyDown(' '));
+        ImGui::Text("IsKeyDown('p') = %d", ImGui::IsKeyDown('p'));
+        ImGui::Text("IsKeyDown('P') = %d", ImGui::IsKeyDown('P'));
+        ImGui::Text("IsKeyDown(ImGuiKey_Space) = %d", ImGui::IsKeyDown(ImGuiKey_Space));
+        ImGui::Text("IsKeyDown(ImGuiKey_P) = %d", ImGui::IsKeyDown(ImGuiKey_P));
+
         ImGui::Text("This is some useful text.");               // Display some text (you can use a format strings too)
         ImGui::Checkbox("Demo Window", &show_demo_window);      // Edit bools storing our window open/close state
         ImGui::Checkbox("Another Window", &show_another_window);
diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp
index e88d942..00cfc2b 100644
--- a/examples/example_sdl_opengl3/main.cpp
+++ b/examples/example_sdl_opengl3/main.cpp
@@ -142,6 +142,11 @@
 
             ImGui::Begin("Hello, world!");                          // Create a window called "Hello, world!" and append into it.
 
+            ImGui::Text("IsKeyDown(SDLK_SPACE) = %d", ImGui::IsKeyDown(SDLK_SPACE));
+            ImGui::Text("IsKeyDown(SDLK_p) = %d", ImGui::IsKeyDown(SDLK_p));
+            ImGui::Text("IsKeyDown(ImGuiKey_Space) = %d", ImGui::IsKeyDown(ImGuiKey_Space));
+            ImGui::Text("IsKeyDown(ImGuiKey_P) = %d", ImGui::IsKeyDown(ImGuiKey_P));
+
             ImGui::Text("This is some useful text.");               // Display some text (you can use a format strings too)
             ImGui::Checkbox("Demo Window", &show_demo_window);      // Edit bools storing our window open/close state
             ImGui::Checkbox("Another Window", &show_another_window);
diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp
index d6378d8..d23387d 100644
--- a/examples/example_win32_directx11/main.cpp
+++ b/examples/example_win32_directx11/main.cpp
@@ -130,6 +130,11 @@
 
             ImGui::Begin("Hello, world!");                          // Create a window called "Hello, world!" and append into it.
 
+            ImGui::Text("IsKeyDown(VK_SPACE) = %d", ImGui::IsKeyDown(VK_SPACE));
+            ImGui::Text("IsKeyDown(VK_P) = %d", ImGui::IsKeyDown('P'));
+            ImGui::Text("IsKeyDown(ImGuiKey_Space) = %d", ImGui::IsKeyDown(ImGuiKey_Space));
+            ImGui::Text("IsKeyDown(ImGuiKey_P) = %d", ImGui::IsKeyDown(ImGuiKey_P));
+
             ImGui::Text("This is some useful text.");               // Display some text (you can use a format strings too)
             ImGui::Checkbox("Demo Window", &show_demo_window);      // Edit bools storing our window open/close state
             ImGui::Checkbox("Another Window", &show_another_window);
diff --git a/imconfig.h b/imconfig.h
index 7082c55..774b8f0 100644
--- a/imconfig.h
+++ b/imconfig.h
@@ -28,6 +28,7 @@
 
 //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
 //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+//#define IMGUI_DISABLE_OBSOLETE_KEYIO                      // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.
 
 //---- Disable all of Dear ImGui or don't implement standard windows.
 // It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp.
diff --git a/imgui.cpp b/imgui.cpp
index 5c415ae..83409d5 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -343,7 +343,7 @@
  - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable.
  - Keyboard:
     - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable.
-      NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
+      NewFrame() will automatically fill io.NavInputs[] based on your io.AddKeyEvent() calls.
     - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag
       will be set. For more advanced uses, you may want to read from:
        - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
@@ -390,6 +390,14 @@
  - 2021/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api.
 
 
+ - 2022/01/xx (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum.
+ - 2022/03/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019)
+                        - ImGui::SetNextTreeNodeOpen()        -> use ImGui::SetNextItemOpen()
+                        - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x
+                        - ImGui::TreeAdvanceToLabelPos()      -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing());
+                        - ImFontAtlas::CustomRect             -> use ImFontAtlasCustomRect
+                        - ImGuiColorEditFlags_RGB/HSV/HEX     -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex
+>>>>>>> thedmd/extra-keys-v1
  - 2021/12/20 (1.86) - backends: removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings
  - 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function.
  - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful.
@@ -954,6 +962,7 @@
 
 // Misc
 static void             UpdateSettings();
+static void             UpdateKeyboardInputs();
 static void             UpdateMouseInputs();
 static void             UpdateMouseWheel();
 static bool             UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
@@ -1119,8 +1128,10 @@
     LogFilename = "imgui_log.txt";
     MouseDoubleClickTime = 0.30f;
     MouseDoubleClickMaxDist = 6.0f;
+#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
     for (int i = 0; i < ImGuiKey_COUNT; i++)
         KeyMap[i] = -1;
+#endif
     KeyRepeatDelay = 0.275f;
     KeyRepeatRate = 0.050f;
     UserData = NULL;
@@ -1167,8 +1178,9 @@
     MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
     MouseDragThreshold = 6.0f;
     for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
-    for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i]  = KeysDownDurationPrev[i] = -1.0f;
+    for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }
     for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f;
+    BackendUsingLegacyKeyArrays = (ImS8)-1;
 }
 
 // Pass in translated ASCII characters for text input.
@@ -1234,15 +1246,48 @@
 
 void ImGuiIO::ClearInputKeys()
 {
+#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
     memset(KeysDown, 0, sizeof(KeysDown));
-    for (int n = 0; n < IM_ARRAYSIZE(KeysDownDuration); n++)
-        KeysDownDuration[n] = KeysDownDurationPrev[n] = -1.0f;
+#endif
+    for (int n = 0; n < IM_ARRAYSIZE(KeysData); n++)
+    {
+        KeysData[n].Down             = false;
+        KeysData[n].DownDuration     = -1.0f;
+        KeysData[n].DownDurationPrev = -1.0f;
+    }
     KeyCtrl = KeyShift = KeyAlt = KeySuper = false;
     KeyMods = KeyModsPrev = ImGuiKeyModFlags_None;
     for (int n = 0; n < IM_ARRAYSIZE(NavInputsDownDuration); n++)
         NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f;
 }
 
+// FIXME: In the current version this is setting key data immediately. This will evolve into a trickling queue.
+void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down, int native_keycode, int native_scancode)
+{
+    IM_UNUSED(native_keycode);
+    IM_UNUSED(native_scancode);
+    IM_ASSERT(ImGui::IsNamedKey(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API.
+
+    // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data.
+#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
+    IM_ASSERT((BackendUsingLegacyKeyArrays == -1 || BackendUsingLegacyKeyArrays == 0) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
+    if (BackendUsingLegacyKeyArrays == -1)
+        for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)
+            IM_ASSERT(KeyMap[n] == -1 && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
+#endif
+    BackendUsingLegacyKeyArrays = 0;
+
+    // Write key
+    int keydata_index = (key - ImGuiKey_KeysData_OFFSET);
+    KeysData[keydata_index].Down = down;
+
+    // Build native->imgui map so old user code can still call key functions with native 0..511 values.
+#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
+    if (native_keycode >= 0 && native_keycode < ImGuiKey_LegacyNativeKey_END)
+        KeyMap[native_keycode] = key;
+#endif
+}
+
 void ImGuiIO::AddFocusEvent(bool focused)
 {
     // We intentionally overwrite this and process in NewFrame(), in order to give a chance
@@ -3254,7 +3299,7 @@
     g.ActiveIdUsingMouseWheel = false;
     g.ActiveIdUsingNavDirMask = 0x00;
     g.ActiveIdUsingNavInputMask = 0x00;
-    g.ActiveIdUsingKeyInputMask = 0x00;
+    g.ActiveIdUsingKeyInputMask.ClearAllBits();
 }
 
 void ImGui::ClearActiveID()
@@ -3870,6 +3915,57 @@
     return (window->Active) && (!window->Hidden);
 }
 
+static void ImGui::UpdateKeyboardInputs()
+{
+    ImGuiContext& g = *GImGui;
+    ImGuiIO& io = g.IO;
+
+    // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
+    io.KeyMods = GetMergedKeyModFlags();
+
+    // Import legacy keys or verify they are not used
+#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
+    if (io.BackendUsingLegacyKeyArrays == 0)
+    {
+        // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written too.
+        for (int n = 0; n < IM_ARRAYSIZE(io.KeysDown); n++)
+            IM_ASSERT(io.KeysDown[n] == false && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!");
+    }
+    else
+    {
+        if (g.FrameCount == 0)
+            for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++)
+                IM_ASSERT(g.IO.KeyMap[n] == -1 && "Backend is not allowed to write to io.KeyMap[0..511]!");
+
+        // Build reverse KeyMap (Named -> Legacy)
+        for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)
+            if (io.KeyMap[n] != -1)
+            {
+                IM_ASSERT(IsLegacyKey((ImGuiKey)io.KeyMap[n]));
+                io.KeyMap[io.KeyMap[n]] = n;
+            }
+
+        // Import legacy keys into new ones
+        for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++)
+            if (io.KeysDown[n] || io.BackendUsingLegacyKeyArrays == 1)
+            {
+                const ImGuiKey key = (ImGuiKey)(io.KeyMap[n] != -1 ? io.KeyMap[n] : n);
+                IM_ASSERT(io.KeyMap[n] == -1 || IsNamedKey(key));
+                io.KeysData[key].Down = io.KeysDown[n];
+                io.BackendUsingLegacyKeyArrays = 1;
+            }
+    }
+#endif
+
+    // Update keys
+    for (int i = 0; i < IM_ARRAYSIZE(io.KeysData); i++)
+    {
+        ImGuiKeyData& key_data = io.KeysData[i];
+        key_data.DownDurationPrev = key_data.DownDuration;
+        key_data.DownDuration = key_data.Down ? (key_data.DownDuration < 0.0f ? 0.0f : key_data.DownDuration + io.DeltaTime) : -1.0f;
+    }
+}
+
 static void ImGui::UpdateMouseInputs()
 {
     ImGuiContext& g = *GImGui;
@@ -4222,7 +4318,7 @@
     {
         g.ActiveIdUsingNavDirMask = 0x00;
         g.ActiveIdUsingNavInputMask = 0x00;
-        g.ActiveIdUsingKeyInputMask = 0x00;
+        g.ActiveIdUsingKeyInputMask.ClearAllBits();
     }
 
     // Drag and drop
@@ -4246,11 +4342,7 @@
     }
 
     // Update keyboard input state
-    // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools
-    g.IO.KeyMods = GetMergedKeyModFlags();
-    memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration));
-    for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++)
-        g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f;
+    UpdateKeyboardInputs();
 
     // Update gamepad/keyboard navigation
     NavUpdate();
@@ -5021,22 +5113,78 @@
     return true;
 }
 
-int ImGui::GetKeyIndex(ImGuiKey imgui_key)
+const ImGuiKeyData* ImGui::GetKeyData(ImGuiKey key)
 {
-    IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT);
     ImGuiContext& g = *GImGui;
-    return g.IO.KeyMap[imgui_key];
+    int index = -1;
+#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
+    IM_ASSERT(key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_NamedKey_END);
+    if (IsLegacyKey(key))
+        index = (g.IO.KeyMap[key] != -1) ? g.IO.KeyMap[key] : key; // Remap native->imgui or imgui->native
+    else
+        index = key;
+#else
+    IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code.");
+    index = key - ImGuiKey_NamedKey_BEGIN;
+#endif
+    return index != -1 ? &g.IO.KeysData[index] : NULL;
 }
 
-// Note that dear imgui doesn't know the semantic of each entry of io.KeysDown[]!
-// Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]!
-bool ImGui::IsKeyDown(int user_key_index)
+#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
+int ImGui::GetKeyIndex(ImGuiKey key)
 {
-    if (user_key_index < 0)
-        return false;
     ImGuiContext& g = *GImGui;
-    IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
-    return g.IO.KeysDown[user_key_index];
+    IM_ASSERT(IsNamedKey(key));
+    const ImGuiKeyData* key_data = GetKeyData(key);
+    return key_data ? (int)(key_data - g.IO.KeysData) : -1;
+}
+#endif
+
+// Those names a provided for debugging purpose and are not meant to be saved persistently not compared.
+static const char* const GKeyNames[] =
+{
+    "Tab", "Left Arrow", "Right Arrow", "Up Arrow", "Down Arrow", "Page Up", "Page Down",
+    "Home", "End", "Insert", "Delete", "Backspace", "Space", "Enter", "Escape",
+    "Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "Left Bracket",
+    "Backslash", "Right Bracket", "Grave Accent", "Caps Lock", "Scroll Lock", "Num Lock", "Print Screen",
+    "Pause", "Keypad 0", "Keypad 1", "Keypad 2", "Keypad 3", "Keypad 4", "Keypad 5", "Keypad 6",
+    "Keypad 7", "Keypad 8", "Keypad 9", "Keypad Decimal", "Keypad Divide", "Keypad Multiply",
+    "Keypad Subtract", "Keypad Add", "Keypad Enter", "Keypad Equal", "Left Shift", "Left Control",
+    "Left Alt", "Left Super", "Right Shift", "Right Control", "Right Alt", "Right Super", "Menu",
+    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
+    "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
+    "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12"
+};
+IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames));
+
+const char* ImGui::GetKeyName(ImGuiKey key)
+{
+#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
+    IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code.");
+#else
+    if (IsLegacyKey(key))
+    {
+        ImGuiIO& io = GetIO();
+        if (io.KeyMap[key] == -1)
+            return "N/A";
+        IM_ASSERT(IsNamedKey((ImGuiKey)io.KeyMap[key]));
+        key = (ImGuiKey)io.KeyMap[key];
+    }
+#endif
+    if (key == ImGuiKey_None)
+        return "None";
+
+    return GKeyNames[key - ImGuiKey_NamedKey_BEGIN];
+}
+
+// Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes.
+// Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87)
+bool ImGui::IsKeyDown(ImGuiKey key)
+{
+    const ImGuiKeyData* key_data = GetKeyData(key);
+    if (key_data == NULL)
+        return false;
+    return key_data->Down;
 }
 
 // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
@@ -5057,36 +5205,36 @@
     return count;
 }
 
-int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate)
+int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate)
 {
-    ImGuiContext& g = *GImGui;
-    if (key_index < 0)
+    const ImGuiKeyData* key_data = GetKeyData(key);
+    if (key_data == NULL)
         return 0;
-    IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown));
-    const float t = g.IO.KeysDownDuration[key_index];
+    ImGuiContext& g = *GImGui;
+    const float t = key_data->DownDuration;
     return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
 }
 
-bool ImGui::IsKeyPressed(int user_key_index, bool repeat)
+bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat)
 {
-    ImGuiContext& g = *GImGui;
-    if (user_key_index < 0)
+    const ImGuiKeyData* key_data = GetKeyData(key);
+    if (key_data == NULL)
         return false;
-    IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
-    const float t = g.IO.KeysDownDuration[user_key_index];
+    ImGuiContext& g = *GImGui;
+    const float t = key_data->DownDuration;
     if (t == 0.0f)
         return true;
     if (repeat && t > g.IO.KeyRepeatDelay)
-        return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
+        return GetKeyPressedAmount(key, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
     return false;
 }
 
-bool ImGui::IsKeyReleased(int user_key_index)
+bool ImGui::IsKeyReleased(ImGuiKey key)
 {
-    ImGuiContext& g = *GImGui;
-    if (user_key_index < 0) return false;
-    IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown));
-    return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index];
+    const ImGuiKeyData* key_data = GetKeyData(key);
+    if (key_data == NULL)
+        return false;
+    return key_data->DownDurationPrev >= 0.0f && !key_data->Down;
 }
 
 bool ImGui::IsMouseDown(ImGuiMouseButton button)
@@ -5358,7 +5506,7 @@
     IM_ASSERT(g.ActiveId != 0);
     g.ActiveIdUsingNavDirMask = ~(ImU32)0;
     g.ActiveIdUsingNavInputMask = ~(ImU32)0;
-    g.ActiveIdUsingKeyInputMask = ~(ImU64)0;
+    g.ActiveIdUsingKeyInputMask.SetAllBits();
     NavMoveRequestCancel();
 }
 
@@ -8079,12 +8227,14 @@
     IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f            && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations
     IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
     IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
-    for (int n = 0; n < ImGuiKey_COUNT; n++)
-        IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)");
+#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
+    for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_COUNT; n++)
+        IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..511, or -1 for unmapped key)");
 
     // Check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only added in 1.60 WIP)
     if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
         IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");
+#endif
 
     // Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
     if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
@@ -10188,7 +10338,7 @@
 {
     ImVec2 delta(0.0f, 0.0f);
     if (dir_sources & ImGuiNavDirSourceFlags_RawKeyboard)
-        delta += ImVec2((float)IsKeyDown(GetKeyIndex(ImGuiKey_RightArrow)) - (float)IsKeyDown(GetKeyIndex(ImGuiKey_LeftArrow)), (float)IsKeyDown(GetKeyIndex(ImGuiKey_DownArrow)) - (float)IsKeyDown(GetKeyIndex(ImGuiKey_UpArrow)));
+        delta += ImVec2((float)IsKeyDown(ImGuiKey_RightArrow) - (float)IsKeyDown(ImGuiKey_LeftArrow), (float)IsKeyDown(ImGuiKey_DownArrow) - (float)IsKeyDown(ImGuiKey_UpArrow));
     if (dir_sources & ImGuiNavDirSourceFlags_Keyboard)
         delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode)   - GetNavInputAmount(ImGuiNavInput_KeyLeft_,   mode), GetNavInputAmount(ImGuiNavInput_KeyDown_,   mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_,   mode));
     if (dir_sources & ImGuiNavDirSourceFlags_PadDPad)
@@ -10224,7 +10374,7 @@
     // Update Keyboard->Nav inputs mapping
     if (nav_keyboard_active)
     {
-        #define NAV_MAP_KEY(_KEY, _NAV_INPUT)  do { if (IsKeyDown(io.KeyMap[_KEY])) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_Keyboard; } } while (0)
+        #define NAV_MAP_KEY(_KEY, _NAV_INPUT)  do { if (IsKeyDown(_KEY)) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_Keyboard; } } while (0)
         NAV_MAP_KEY(ImGuiKey_Space,     ImGuiNavInput_Activate );
         NAV_MAP_KEY(ImGuiKey_Enter,     ImGuiNavInput_Input    );
         NAV_MAP_KEY(ImGuiKey_Escape,    ImGuiNavInput_Cancel   );
@@ -10442,7 +10592,7 @@
 
     // [DEBUG] Always send a request
 #if IMGUI_DEBUG_NAV_SCORING
-    if (io.KeyCtrl && IsKeyPressedMap(ImGuiKey_C))
+    if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C))
         g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3);
     if (io.KeyCtrl && g.NavMoveDir == ImGuiDir_None)
     {
@@ -10506,7 +10656,7 @@
     if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs))
         return;
 
-    const bool tab_pressed = IsKeyPressedMap(ImGuiKey_Tab, true) && !IsActiveIdUsingKey(ImGuiKey_Tab) && !g.IO.KeyCtrl && !g.IO.KeyAlt;
+    const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, true) && !IsActiveIdUsingKey(ImGuiKey_Tab) && !g.IO.KeyCtrl && !g.IO.KeyAlt;
     if (!tab_pressed)
         return;
 
@@ -10667,16 +10817,14 @@
 static float ImGui::NavUpdatePageUpPageDown()
 {
     ImGuiContext& g = *GImGui;
-    ImGuiIO& io = g.IO;
-
     ImGuiWindow* window = g.NavWindow;
     if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL)
         return 0.0f;
 
-    const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
-    const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
-    const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home);
-    const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End);
+    const bool page_up_held = IsKeyDown(ImGuiKey_PageUp) && !IsActiveIdUsingKey(ImGuiKey_PageUp);
+    const bool page_down_held = IsKeyDown(ImGuiKey_PageDown) && !IsActiveIdUsingKey(ImGuiKey_PageDown);
+    const bool home_pressed = IsKeyPressed(ImGuiKey_Home) && !IsActiveIdUsingKey(ImGuiKey_Home);
+    const bool end_pressed = IsKeyPressed(ImGuiKey_End) && !IsActiveIdUsingKey(ImGuiKey_End);
     if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
         return 0.0f;
 
@@ -10686,9 +10834,9 @@
     if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll)
     {
         // Fallback manual-scroll when window has no navigable item
-        if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
+        if (IsKeyPressed(ImGuiKey_PageUp, true))
             SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
-        else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
+        else if (IsKeyPressed(ImGuiKey_PageDown, true))
             SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
         else if (home_pressed)
             SetScrollY(window, 0.0f);
@@ -10700,14 +10848,14 @@
         ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
         const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());
         float nav_scoring_rect_offset_y = 0.0f;
-        if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true))
+        if (IsKeyPressed(ImGuiKey_PageUp, true))
         {
             nav_scoring_rect_offset_y = -page_offset_y;
             g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item)
             g.NavMoveClipDir = ImGuiDir_Up;
             g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet;
         }
-        else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true))
+        else if (IsKeyPressed(ImGuiKey_PageDown, true))
         {
             nav_scoring_rect_offset_y = +page_offset_y;
             g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item)
@@ -10872,7 +11020,7 @@
 
     // Start CTRL+Tab or Square+L/R window selection
     const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed);
-    const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab);
+    const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressed(ImGuiKey_Tab);
     if (start_windowing_with_gamepad || start_windowing_with_keyboard)
         if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
         {
@@ -10914,7 +11062,7 @@
     {
         // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
         g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
-        if (IsKeyPressedMap(ImGuiKey_Tab, true))
+        if (IsKeyPressed(ImGuiKey_Tab, true))
             NavUpdateWindowingHighlightWindow(io.KeyShift ? +1 : -1);
         if (!io.KeyCtrl)
             apply_focus_window = g.NavWindowingTarget;
@@ -17382,7 +17530,11 @@
         Indent();
         Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]);
         Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
-        Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %llX", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, g.ActiveIdUsingKeyInputMask);
+
+        int active_id_using_key_input_count = 0;
+        for (int n = 0; n < g.ActiveIdUsingKeyInputMask.Size; n++)
+            active_id_using_key_input_count += g.ActiveIdUsingKeyInputMask.TestBit(n) ? 1 : 0;
+        Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %d key(s)", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, active_id_using_key_input_count);
         Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
         Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
         Unindent();
@@ -18018,7 +18170,7 @@
 
     const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
     SetMouseCursor(ImGuiMouseCursor_Hand);
-    if (IsKeyPressedMap(ImGuiKey_Escape))
+    if (IsKeyPressed(ImGuiKey_Escape))
         g.DebugItemPickerActive = false;
     if (IsMouseClicked(0) && hovered_id)
     {
diff --git a/imgui.h b/imgui.h
index bfc3c33..c5ae874 100644
--- a/imgui.h
+++ b/imgui.h
@@ -177,7 +177,7 @@
 typedef int ImGuiCond;              // -> enum ImGuiCond_            // Enum: A condition for many Set*() functions
 typedef int ImGuiDataType;          // -> enum ImGuiDataType_        // Enum: A primary data type
 typedef int ImGuiDir;               // -> enum ImGuiDir_             // Enum: A cardinal direction
-typedef int ImGuiKey;               // -> enum ImGuiKey_             // Enum: A key identifier (ImGui-side enum)
+typedef int ImGuiKey;               // -> enum ImGuiKey_             // Enum: A key identifier
 typedef int ImGuiNavInput;          // -> enum ImGuiNavInput_        // Enum: An input identifier for navigation
 typedef int ImGuiMouseButton;       // -> enum ImGuiMouseButton_     // Enum: A mouse button identifier (0=left, 1=right, 2=middle)
 typedef int ImGuiMouseCursor;       // -> enum ImGuiMouseCursor_     // Enum: A mouse cursor identifier
@@ -915,13 +915,16 @@
     IMGUI_API void          ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b);
 
     // Inputs Utilities: Keyboard
-    // - For 'int user_key_index' you can use your own indices/enums according to how your backend/engine stored them in io.KeysDown[].
-    // - We don't know the meaning of those value. You can use GetKeyIndex() to map a ImGuiKey_ value into the user index.
-    IMGUI_API int           GetKeyIndex(ImGuiKey imgui_key);                                    // map ImGuiKey_* values into user's key index. == io.KeyMap[key]
-    IMGUI_API bool          IsKeyDown(int user_key_index);                                      // is key being held. == io.KeysDown[user_key_index].
-    IMGUI_API bool          IsKeyPressed(int user_key_index, bool repeat = true);               // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate
-    IMGUI_API bool          IsKeyReleased(int user_key_index);                                  // was key released (went from Down to !Down)?
-    IMGUI_API int           GetKeyPressedAmount(int key_index, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate
+    // Without IMGUI_DISABLE_OBSOLETE_KEYIO: (legacy support)
+    //   - For 'ImGuiKey key' you can still use your legacy native/user indices according to how your backend/engine stored them in io.KeysDown[].
+    // With IMGUI_DISABLE_OBSOLETE_KEYIO: (this is the way forward)
+    //   - Any use of 'ImGuiKey' will assert when key < 512 will be passed, previously reserved as native/user keys indices
+    //   - GetKeyIndex() is pass-through and therefore deprecated (gone if IMGUI_DISABLE_OBSOLETE_KEYIO is defined)
+    IMGUI_API bool          IsKeyDown(ImGuiKey key);                                            // is key being held.
+    IMGUI_API bool          IsKeyPressed(ImGuiKey key, bool repeat = true);                     // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate
+    IMGUI_API bool          IsKeyReleased(ImGuiKey key);                                        // was key released (went from Down to !Down)?
+    IMGUI_API int           GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float rate);  // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate
+    IMGUI_API const char*   GetKeyName(ImGuiKey key);                                           // [DEBUG] returns English name of the key. Those names a provided for debugging purpose and are not meant to be saved persistently not compared.
     IMGUI_API void          CaptureKeyboardFromApp(bool want_capture_keyboard_value = true);    // attention: misleading name! manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application to handle). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard_value"; after the next NewFrame() call.
 
     // Inputs Utilities: Mouse
@@ -1411,10 +1414,10 @@
     ImGuiSortDirection_Descending   = 2     // Descending = 9->0, Z->A etc.
 };
 
-// User fill ImGuiIO.KeyMap[] array with indices into the ImGuiIO.KeysDown[512] array
 enum ImGuiKey_
 {
-    ImGuiKey_Tab,
+    ImGuiKey_None           = 0,
+    ImGuiKey_Tab            = 512,          // == ImGuiKey_NamedKey_BEGIN
     ImGuiKey_LeftArrow,
     ImGuiKey_RightArrow,
     ImGuiKey_UpArrow,
@@ -1429,14 +1432,116 @@
     ImGuiKey_Space,
     ImGuiKey_Enter,
     ImGuiKey_Escape,
-    ImGuiKey_KeyPadEnter,
-    ImGuiKey_A,                 // for text edit CTRL+A: select all
-    ImGuiKey_C,                 // for text edit CTRL+C: copy
-    ImGuiKey_V,                 // for text edit CTRL+V: paste
-    ImGuiKey_X,                 // for text edit CTRL+X: cut
-    ImGuiKey_Y,                 // for text edit CTRL+Y: redo
-    ImGuiKey_Z,                 // for text edit CTRL+Z: undo
-    ImGuiKey_COUNT
+    ImGuiKey_Apostrophe,    // '
+    ImGuiKey_Comma,         // ,
+    ImGuiKey_Minus,         // -
+    ImGuiKey_Period,        // .
+    ImGuiKey_Slash,         // /
+    ImGuiKey_Semicolon,     // ;
+    ImGuiKey_Equal,         // =
+    ImGuiKey_LeftBracket,   // [
+    ImGuiKey_Backslash,     // \ (this text inhibit multiline comment caused by backlash)
+    ImGuiKey_RightBracket,  // ]
+    ImGuiKey_GraveAccent,   // `
+    ImGuiKey_CapsLock,
+    ImGuiKey_ScrollLock,
+    ImGuiKey_NumLock,
+    ImGuiKey_PrintScreen,
+    ImGuiKey_Pause,
+    ImGuiKey_Keypad0,
+    ImGuiKey_Keypad1,
+    ImGuiKey_Keypad2,
+    ImGuiKey_Keypad3,
+    ImGuiKey_Keypad4,
+    ImGuiKey_Keypad5,
+    ImGuiKey_Keypad6,
+    ImGuiKey_Keypad7,
+    ImGuiKey_Keypad8,
+    ImGuiKey_Keypad9,
+    ImGuiKey_KeypadDecimal,
+    ImGuiKey_KeypadDivide,
+    ImGuiKey_KeypadMultiply,
+    ImGuiKey_KeypadSubtract,
+    ImGuiKey_KeypadAdd,
+    ImGuiKey_KeypadEnter,
+    ImGuiKey_KeypadEqual,
+    ImGuiKey_LeftShift,
+    ImGuiKey_LeftControl,
+    ImGuiKey_LeftAlt,
+    ImGuiKey_LeftSuper,
+    ImGuiKey_RightShift,
+    ImGuiKey_RightControl,
+    ImGuiKey_RightAlt,
+    ImGuiKey_RightSuper,
+    ImGuiKey_Menu,
+    ImGuiKey_0,
+    ImGuiKey_1,
+    ImGuiKey_2,
+    ImGuiKey_3,
+    ImGuiKey_4,
+    ImGuiKey_5,
+    ImGuiKey_6,
+    ImGuiKey_7,
+    ImGuiKey_8,
+    ImGuiKey_9,
+    ImGuiKey_A,
+    ImGuiKey_B,
+    ImGuiKey_C,
+    ImGuiKey_D,
+    ImGuiKey_E,
+    ImGuiKey_F,
+    ImGuiKey_G,
+    ImGuiKey_H,
+    ImGuiKey_I,
+    ImGuiKey_J,
+    ImGuiKey_K,
+    ImGuiKey_L,
+    ImGuiKey_M,
+    ImGuiKey_N,
+    ImGuiKey_O,
+    ImGuiKey_P,
+    ImGuiKey_Q,
+    ImGuiKey_R,
+    ImGuiKey_S,
+    ImGuiKey_T,
+    ImGuiKey_U,
+    ImGuiKey_V,
+    ImGuiKey_W,
+    ImGuiKey_X,
+    ImGuiKey_Y,
+    ImGuiKey_Z,
+    ImGuiKey_F1,
+    ImGuiKey_F2,
+    ImGuiKey_F3,
+    ImGuiKey_F4,
+    ImGuiKey_F5,
+    ImGuiKey_F6,
+    ImGuiKey_F7,
+    ImGuiKey_F8,
+    ImGuiKey_F9,
+    ImGuiKey_F10,
+    ImGuiKey_F11,
+    ImGuiKey_F12,
+    ImGuiKey_COUNT,         // No valid ImGuiKey is ever greater than this value
+
+    // Legacy range used by legacy io.KeyMap[]. Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index.
+    // We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE)
+    ImGuiKey_LegacyNativeKey_BEGIN  = 0,
+    ImGuiKey_LegacyNativeKey_END    = 512,  // First index after valid range
+    ImGuiKey_NamedKey_BEGIN         = 512,
+    ImGuiKey_NamedKey_END           = ImGuiKey_COUNT,
+    ImGuiKey_NamedKey_COUNT         = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN,
+#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
+    ImGuiKey_KeysData_SIZE = ImGuiKey_NamedKey_COUNT,           // Size of KeysData[]: only hold named keys
+    ImGuiKey_KeysData_OFFSET = ImGuiKey_NamedKey_BEGIN          // First key stored in KeysData[0]
+#else
+    ImGuiKey_KeysData_SIZE = ImGuiKey_COUNT,                    // Size of KeysData[]: hold legacy 0..512 keycodes + named keys
+    ImGuiKey_KeysData_OFFSET = ImGuiKey_LegacyNativeKey_BEGIN   // First key stored in KeysData[0]
+#endif
+
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+    , ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter   // Renamed in 1.87
+#endif
 };
 
 // To test io.KeyMods (which is a combination of individual fields io.KeyCtrl, io.KeyShift, io.KeyAlt set by user/backend)
@@ -1450,7 +1555,7 @@
 };
 
 // Gamepad/Keyboard navigation
-// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
+// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.AddKeyEvent() calls.
 // Gamepad:  Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Backend: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame().
 // Read instructions in imgui.cpp for more details. Download PNG/PSD at http://dearimgui.org/controls_sheets.
 enum ImGuiNavInput_
@@ -1474,7 +1579,7 @@
     ImGuiNavInput_TweakFast,     // faster tweaks                                // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch)
 
     // [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them.
-    // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from io.KeysDown[] instead of io.NavInputs[].
+    // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from keyboard keys instead of io.NavInputs[].
     ImGuiNavInput_KeyLeft_,      // move left                                    // = Arrow keys
     ImGuiNavInput_KeyRight_,     // move right
     ImGuiNavInput_KeyUp_,        // move up
@@ -1487,7 +1592,7 @@
 enum ImGuiConfigFlags_
 {
     ImGuiConfigFlags_None                   = 0,
-    ImGuiConfigFlags_NavEnableKeyboard      = 1 << 0,   // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeysDown[].
+    ImGuiConfigFlags_NavEnableKeyboard      = 1 << 0,   // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.AddKeyEvent() calls
     ImGuiConfigFlags_NavEnableGamepad       = 1 << 1,   // Master gamepad navigation enable flag. This is mostly to instruct your imgui backend to fill io.NavInputs[]. Backend also needs to set ImGuiBackendFlags_HasGamepad.
     ImGuiConfigFlags_NavEnableSetMousePos   = 1 << 2,   // Instruct navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantSetMousePos=true. If enabled you MUST honor io.WantSetMousePos requests in your backend, otherwise ImGui will react as if the mouse is jumping around back and forth.
     ImGuiConfigFlags_NavNoCaptureKeyboard   = 1 << 3,   // Instruct navigation to not set the io.WantCaptureKeyboard flag when io.NavActive is set.
@@ -1676,9 +1781,7 @@
     ImGuiColorEditFlags_InputMask_      = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV
 
     // Obsolete names (will be removed)
-#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
-    , ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex  // [renamed in 1.69]
-#endif
+    // ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex  // [renamed in 1.69]
 };
 
 // Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.
@@ -1894,6 +1997,15 @@
 // Access via ImGui::GetIO(). Read 'Programmer guide' section in .cpp file for general usage.
 //-----------------------------------------------------------------------------
 
+// [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions.
+// If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and not io.KeysData[key]->DownDuration.
+struct ImGuiKeyData
+{
+    bool  Down;             // True for if key is down
+    float DownDuration;     // Duration the key has been down (<0.0f: not pressed, 0.0f: just pressed, >0.0f: time held)
+    float DownDurationPrev; // Last frame duration the key has been down
+};
+
 struct ImGuiIO
 {
     //------------------------------------------------------------------
@@ -1910,7 +2022,6 @@
     float       MouseDoubleClickTime;           // = 0.30f          // Time for a double-click, in seconds.
     float       MouseDoubleClickMaxDist;        // = 6.0f           // Distance threshold to stay in to validate a double-click, in pixels.
     float       MouseDragThreshold;             // = 6.0f           // Distance threshold before considering we are dragging.
-    int         KeyMap[ImGuiKey_COUNT];         // <unset>          // Map of indices into the KeysDown[512] entries array which represent your "native" keyboard state.
     float       KeyRepeatDelay;                 // = 0.250f         // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.).
     float       KeyRepeatRate;                  // = 0.050f         // When holding a key/button, rate at which it repeats, in seconds.
     void*       UserData;                       // = NULL           // Store your own data for retrieval by callbacks.
@@ -1973,17 +2084,24 @@
     bool        KeyShift;                       // Keyboard modifier pressed: Shift
     bool        KeyAlt;                         // Keyboard modifier pressed: Alt
     bool        KeySuper;                       // Keyboard modifier pressed: Cmd/Super/Windows
-    bool        KeysDown[512];                  // Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys).
     float       NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame().
 
     // Functions
     IMGUI_API void  AddInputCharacter(unsigned int c);          // Queue new character input
     IMGUI_API void  AddInputCharacterUTF16(ImWchar16 c);        // Queue new character input from an UTF-16 character, it can be a surrogate
     IMGUI_API void  AddInputCharactersUTF8(const char* str);    // Queue new characters input from an UTF-8 string
+    IMGUI_API void  AddKeyEvent(ImGuiKey key, bool down, int native_keycode = -1, int native_scancode = -1); // Notifies Dear ImGui about key down/up event
     IMGUI_API void  AddFocusEvent(bool focused);                // Notifies Dear ImGui when hosting platform windows lose or gain input focus
     IMGUI_API void  ClearInputCharacters();                     // [Internal] Clear the text input buffer manually
     IMGUI_API void  ClearInputKeys();                           // [Internal] Release all keys
 
+    // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame.
+    // This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent().
+#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
+    int         KeyMap[ImGuiKey_COUNT];         // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512.
+    bool        KeysDown[512];                  // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys).
+#endif
+
     //------------------------------------------------------------------
     // Output - Updated by NewFrame() or EndFrame()/Render()
     // (when reading from the io.WantCaptureMouse, io.WantCaptureKeyboard flags to dispatch your inputs, it is
@@ -2026,12 +2144,12 @@
     float       MouseDownDurationPrev[5];       // Previous time the mouse button has been down
     ImVec2      MouseDragMaxDistanceAbs[5];     // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point
     float       MouseDragMaxDistanceSqr[5];     // Squared maximum distance of how much mouse has traveled from the clicking point
-    float       KeysDownDuration[512];          // Duration the keyboard key has been down (0.0f == just pressed)
-    float       KeysDownDurationPrev[512];      // Previous duration the key has been down
+    ImGuiKeyData KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys.
     float       NavInputsDownDuration[ImGuiNavInput_COUNT];
     float       NavInputsDownDurationPrev[ImGuiNavInput_COUNT];
     float       PenPressure;                    // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui.
     bool        AppFocusLost;
+    ImS8        BackendUsingLegacyKeyArrays;    // -1: unknown, 0: using AddKeyEvent(), 1: using legacy io.KeysDown[]
     ImWchar16   InputQueueSurrogate;            // For AddInputCharacterUTF16
     ImVector<ImWchar> InputQueueCharacters;     // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper.
 
@@ -2604,8 +2722,8 @@
     inline    void  PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col)         { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } // Write vertex with unique index
 
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
-    inline    void  AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); }
-    inline    void  PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); }
+    inline    void  AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); } // OBSOLETED in 1.80 (Jan 2021)
+    inline    void  PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); } // OBSOLETED in 1.80 (Jan 2021)
 #endif
 
     // [Internal helpers]
@@ -2835,10 +2953,9 @@
     int                         PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors
     int                         PackIdLines;        // Custom texture rectangle ID for baked anti-aliased lines
 
-#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
-    typedef ImFontAtlasCustomRect    CustomRect;         // OBSOLETED in 1.72+
+    // [Obsolete]
+    //typedef ImFontAtlasCustomRect    CustomRect;         // OBSOLETED in 1.72+
     //typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+
-#endif
 };
 
 // Font runtime data and rendering
@@ -3084,6 +3201,15 @@
 // Please keep your copy of dear imgui up to date! Occasionally set '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in imconfig.h to stay ahead.
 //-----------------------------------------------------------------------------
 
+namespace ImGui
+{
+#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
+    IMGUI_API int       GetKeyIndex(ImGuiKey key);  // map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]
+#else
+    static inline int   GetKeyIndex(ImGuiKey key)   { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "ImGuiKey and native_index was merged together and native_index is disabled by IMGUI_DISABLE_OBSOLETE_KEYIO. Please switch to ImGuiKey."); return key; }
+#endif
+}
+
 #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
 namespace ImGui
 {
@@ -3114,24 +3240,21 @@
     static inline bool  SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power)              { return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); }
     // OBSOLETED in 1.77 (from June 2020)
     static inline bool  BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mb, bool over_items) { return BeginPopupContextWindow(str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); }
-    // OBSOLETED in 1.72 (from April 2019)
-    static inline void  TreeAdvanceToLabelPos()             { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); }
-    // OBSOLETED in 1.71 (from June 2019)
-    static inline void  SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); }
-    // OBSOLETED in 1.70 (from May 2019)
-    static inline float GetContentRegionAvailWidth()        { return GetContentRegionAvail().x; }
 
     // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE)
-    //static inline ImDrawList* GetOverlayDrawList()            { return GetForegroundDrawList(); }                         // OBSOLETED in 1.69 (from Mar 2019)
-    //static inline void  SetScrollHere(float ratio = 0.5f)     { SetScrollHereY(ratio); }                                  // OBSOLETED in 1.66 (from Nov 2018)
-    //static inline bool  IsItemDeactivatedAfterChange()        { return IsItemDeactivatedAfterEdit(); }                    // OBSOLETED in 1.63 (from Aug 2018)
-    //static inline bool  IsAnyWindowFocused()                  { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); }    // OBSOLETED in 1.60 (from Apr 2018)
-    //static inline bool  IsAnyWindowHovered()                  { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); }    // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018)
-    //static inline void  ShowTestWindow()                      { return ShowDemoWindow(); }                                // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
-    //static inline bool  IsRootWindowFocused()                 { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); }   // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
-    //static inline bool  IsRootWindowOrAnyChildFocused()       { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
-    //static inline void  SetNextWindowContentWidth(float w)    { SetNextWindowContentSize(ImVec2(w, 0.0f)); }              // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
-    //static inline float GetItemsLineHeightWithSpacing()       { return GetFrameHeightWithSpacing(); }                     // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
+    //static inline void  TreeAdvanceToLabelPos()               { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); }   // OBSOLETED in 1.72 (from July 2019)
+    //static inline void  SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); }                       // OBSOLETED in 1.71 (from June 2019)
+    //static inline float GetContentRegionAvailWidth()          { return GetContentRegionAvail().x; }                               // OBSOLETED in 1.70 (from May 2019)
+    //static inline ImDrawList* GetOverlayDrawList()            { return GetForegroundDrawList(); }                                 // OBSOLETED in 1.69 (from Mar 2019)
+    //static inline void  SetScrollHere(float ratio = 0.5f)     { SetScrollHereY(ratio); }                                          // OBSOLETED in 1.66 (from Nov 2018)
+    //static inline bool  IsItemDeactivatedAfterChange()        { return IsItemDeactivatedAfterEdit(); }                            // OBSOLETED in 1.63 (from Aug 2018)
+    //static inline bool  IsAnyWindowFocused()                  { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); }            // OBSOLETED in 1.60 (from Apr 2018)
+    //static inline bool  IsAnyWindowHovered()                  { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); }            // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018)
+    //static inline void  ShowTestWindow()                      { return ShowDemoWindow(); }                                        // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
+    //static inline bool  IsRootWindowFocused()                 { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); }           // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
+    //static inline bool  IsRootWindowOrAnyChildFocused()       { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); }  // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
+    //static inline void  SetNextWindowContentWidth(float w)    { SetNextWindowContentSize(ImVec2(w, 0.0f)); }                      // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
+    //static inline float GetItemsLineHeightWithSpacing()       { return GetFrameHeightWithSpacing(); }                             // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
 }
 
 // OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect()
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index 4805be6..f66a81b 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -471,7 +471,7 @@
                     ImGui::SameLine();
                     ImGui::Text("<<PRESS SPACE TO DISABLE>>");
                 }
-                if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Space)))
+                if (ImGui::IsKeyPressed(ImGuiKey_Space))
                     io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse;
             }
             ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", &io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange);
@@ -5712,6 +5712,8 @@
     ImGui::TreePop();
 }
 
+namespace ImGui { extern const ImGuiKeyData* GetKeyData(ImGuiKey key); }
+
 static void ShowDemoWindowMisc()
 {
     IMGUI_DEMO_MARKER("Filtering");
@@ -5733,6 +5735,7 @@
     }
 
     IMGUI_DEMO_MARKER("Inputs, Navigation & Focus");
+    ImGui::SetNextItemOpen(true);
     if (ImGui::CollapsingHeader("Inputs, Navigation & Focus"))
     {
         ImGuiIO& io = ImGui::GetIO();
@@ -5766,11 +5769,20 @@
 
         // Display Keyboard/Mouse state
         IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Keyboard & Navigation State");
+        ImGui::SetNextItemOpen(true);
         if (ImGui::TreeNode("Keyboard & Navigation State"))
         {
-            ImGui::Text("Keys down:");          for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyDown(i))        { ImGui::SameLine(); ImGui::Text("%d (0x%X) (%.02f secs)", i, i, io.KeysDownDuration[i]); }
-            ImGui::Text("Keys pressed:");       for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyPressed(i))     { ImGui::SameLine(); ImGui::Text("%d (0x%X)", i, i); }
-            ImGui::Text("Keys release:");       for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyReleased(i))    { ImGui::SameLine(); ImGui::Text("%d (0x%X)", i, i); }
+            // We iterate both legacy native range and named ImGuiKey ranges, which is a little odd but this allow displaying the data for old/new backends.
+            // User code should never have to go through such hoops: old code may use native keycodes, new code may use ImGuiKey codes.
+#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
+            struct funcs { static bool IsNativeDupe(ImGuiKey) { return false; } };
+#else
+            struct funcs { static bool IsNativeDupe(ImGuiKey key) { return key < ImGuiKey_LegacyNativeKey_END && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array
+#endif
+            //ImGui::Text("Raw down:");         for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)i; if (io.KeysData[i].Down) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X) (%.02f secs)", ImGui::GetKeyName(key), key, key, ImGui::GetKeyData(key)->DownDuration); } }
+            ImGui::Text("Keys down:");          for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)(i + ImGuiKey_KeysData_OFFSET); if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X) (%.02f secs)", ImGui::GetKeyName(key), key, key, ImGui::GetKeyData(key)->DownDuration); } }
+            ImGui::Text("Keys pressed:");       for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)(i + ImGuiKey_KeysData_OFFSET); if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X)", ImGui::GetKeyName(key), key, key); } }
+            ImGui::Text("Keys released:");      for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) { ImGuiKey key = (ImGuiKey)(i + ImGuiKey_KeysData_OFFSET); if (funcs::IsNativeDupe(key)) continue; if (ImGui::IsKeyReleased(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (0x%X)", ImGui::GetKeyName(key), key, key); } }
             ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
             ImGui::Text("Chars queue:");        for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine();  ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
 
@@ -5941,6 +5953,9 @@
 #ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
         ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS");
 #endif
+#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
+        ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_KEYIO");
+#endif
 #ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
         ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS");
 #endif
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index 3091fea..cead9c9 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -1741,9 +1741,7 @@
     for (int i = 1; i < _Count; i++)
     {
         ImDrawChannel& ch = _Channels[i];
-
-        // Equivalent of PopUnusedDrawCmd() for this channel's cmdbuffer and except we don't need to test for UserCallback.
-        if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0)
+        if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0 && ch._CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd()
             ch._CmdBuffer.pop_back();
 
         if (ch._CmdBuffer.Size > 0 && last_cmd != NULL)
diff --git a/imgui_internal.h b/imgui_internal.h
index 5215d8b..bd40502 100644
--- a/imgui_internal.h
+++ b/imgui_internal.h
@@ -555,6 +555,7 @@
 template<int BITCOUNT>
 struct IMGUI_API ImBitArray
 {
+    static const int Size = BITCOUNT;
     ImU32           Storage[(BITCOUNT + 31) >> 5];
     ImBitArray()                                { ClearAllBits(); }
     void            ClearAllBits()              { memset(Storage, 0, sizeof(Storage)); }
@@ -564,6 +565,8 @@
     void            ClearBit(int n)             { IM_ASSERT(n < BITCOUNT); ImBitArrayClearBit(Storage, n); }
     void            SetBitRange(int n, int n2)  { ImBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2)
 };
+template<int BITCOUNT>
+const int ImBitArray<BITCOUNT>::Size;
 
 // Helper: ImBitVector
 // Store 1-bit per value.
@@ -1731,7 +1734,7 @@
     bool                    ActiveIdUsingMouseWheel;            // Active widget will want to read mouse wheel. Blocks scrolling the underlying window.
     ImU32                   ActiveIdUsingNavDirMask;            // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it)
     ImU32                   ActiveIdUsingNavInputMask;          // Active widget will want to read those nav inputs.
-    ImU64                   ActiveIdUsingKeyInputMask;          // Active widget will want to read those key inputs. When we grow the ImGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array.
+    ImBitArray<ImGuiKey_NamedKey_COUNT> ActiveIdUsingKeyInputMask; // Active widget will want to read those key inputs. When we grow the ImGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array.
     ImVec2                  ActiveIdClickOffset;                // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior)
     ImGuiWindow*            ActiveIdWindow;
     ImGuiInputSource        ActiveIdSource;                     // Activating with mouse or nav (gamepad/keyboard)
@@ -1981,7 +1984,7 @@
         ActiveIdUsingMouseWheel = false;
         ActiveIdUsingNavDirMask = 0x00;
         ActiveIdUsingNavInputMask = 0x00;
-        ActiveIdUsingKeyInputMask = 0x00;
+        ActiveIdUsingKeyInputMask.ClearAllBits();
         ActiveIdClickOffset = ImVec2(-1, -1);
         ActiveIdWindow = NULL;
         ActiveIdSource = ImGuiInputSource_None;
@@ -2823,16 +2826,22 @@
 
     // Inputs
     // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions.
+    inline bool             IsNamedKey(ImGuiKey key)                                    { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; }
+    inline bool             IsLegacyKey(ImGuiKey key)                                   { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; }
+    IMGUI_API const ImGuiKeyData* GetKeyData(ImGuiKey key);
     IMGUI_API void          SetItemUsingMouseWheel();
     IMGUI_API void          SetActiveIdUsingNavAndKeys();
     inline bool             IsActiveIdUsingNavDir(ImGuiDir dir)                         { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; }
     inline bool             IsActiveIdUsingNavInput(ImGuiNavInput input)                { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavInputMask & (1 << input)) != 0; }
-    inline bool             IsActiveIdUsingKey(ImGuiKey key)                            { ImGuiContext& g = *GImGui; IM_ASSERT(key < 64); return (g.ActiveIdUsingKeyInputMask & ((ImU64)1 << key)) != 0; }
+    inline bool             IsActiveIdUsingKey(ImGuiKey key)                            { IM_ASSERT(IsNamedKey(key)); ImGuiContext& g = *GImGui; return g.ActiveIdUsingKeyInputMask.TestBit(key - ImGuiKey_NamedKey_BEGIN); }
+    inline void             SetActiveIdUsingKey(ImGuiKey key)                           { IM_ASSERT(IsNamedKey(key)); ImGuiContext& g = *GImGui; g.ActiveIdUsingKeyInputMask.SetBit(key - ImGuiKey_NamedKey_BEGIN); }
     IMGUI_API bool          IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f);
-    inline bool             IsKeyPressedMap(ImGuiKey key, bool repeat = true)           { ImGuiContext& g = *GImGui; const int key_index = g.IO.KeyMap[key]; return (key_index >= 0) ? IsKeyPressed(key_index, repeat) : false; }
     inline bool             IsNavInputDown(ImGuiNavInput n)                             { ImGuiContext& g = *GImGui; return g.IO.NavInputs[n] > 0.0f; }
     inline bool             IsNavInputTest(ImGuiNavInput n, ImGuiInputReadMode rm)      { return (GetNavInputAmount(n, rm) > 0.0f); }
     IMGUI_API ImGuiKeyModFlags GetMergedKeyModFlags();
+#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
+    inline bool             IsKeyPressedMap(ImGuiKey key, bool repeat = true)           { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); }
+#endif
 
     // Docking
     // (some functions are only declared in imgui.cpp, see Docking section)
diff --git a/imgui_tables.cpp b/imgui_tables.cpp
index b5c9c2f..88717bd 100644
--- a/imgui_tables.cpp
+++ b/imgui_tables.cpp
@@ -2353,7 +2353,7 @@
 
             // Don't attempt to merge if there are multiple draw calls within the column
             ImDrawChannel* src_channel = &splitter->_Channels[channel_no];
-            if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0)
+            if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0 && src_channel->_CmdBuffer.back().UserCallback != NULL) // Equivalent of PopUnusedDrawCmd()
                 src_channel->_CmdBuffer.pop_back();
             if (src_channel->_CmdBuffer.Size != 1)
                 continue;
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 16e6cf8..1cea8c9 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -4125,11 +4125,17 @@
         if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory))
             g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
         g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);
-        g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_Home) | ((ImU64)1 << ImGuiKey_End);
+        SetActiveIdUsingKey(ImGuiKey_Home);
+        SetActiveIdUsingKey(ImGuiKey_End);
         if (is_multiline)
-            g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_PageUp) | ((ImU64)1 << ImGuiKey_PageDown);
-        if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput))  // Disable keyboard tabbing out as we will use the \t character.
-            g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_Tab);
+        {
+            SetActiveIdUsingKey(ImGuiKey_PageUp);
+            SetActiveIdUsingKey(ImGuiKey_PageDown);
+        }
+        if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character.
+        {
+            SetActiveIdUsingKey(ImGuiKey_Tab);
+        }
     }
 
     // We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function)
@@ -4259,7 +4265,7 @@
         // It is ill-defined whether the backend needs to send a \t character when pressing the TAB keys.
         // Win32 and GLFW naturally do it but not SDL.
         const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper);
-        if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !ignore_char_inputs && !io.KeyShift && !is_readonly)
+        if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressed(ImGuiKey_Tab) && !ignore_char_inputs && !io.KeyShift && !is_readonly)
             if (!io.InputQueueCharacters.contains('\t'))
             {
                 unsigned int c = '\t'; // Insert TAB
@@ -4306,27 +4312,27 @@
         const bool is_shift_key_only = (io.KeyMods == ImGuiKeyModFlags_Shift);
         const bool is_shortcut_key = g.IO.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiKeyModFlags_Super) : (io.KeyMods == ImGuiKeyModFlags_Ctrl);
 
-        const bool is_cut   = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());
-        const bool is_copy  = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only  && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection());
-        const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_readonly;
-        const bool is_undo  = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && !is_readonly && is_undoable);
-        const bool is_redo  = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && !is_readonly && is_undoable;
+        const bool is_cut   = ((is_shortcut_key && IsKeyPressed(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());
+        const bool is_copy  = ((is_shortcut_key && IsKeyPressed(ImGuiKey_C)) || (is_ctrl_key_only  && IsKeyPressed(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection());
+        const bool is_paste = ((is_shortcut_key && IsKeyPressed(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_readonly;
+        const bool is_undo  = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Z)) && !is_readonly && is_undoable);
+        const bool is_redo  = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressed(ImGuiKey_Z))) && !is_readonly && is_undoable;
 
         // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful.
-        const bool is_validate_enter = IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter);
-        const bool is_validate_nav = (IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) && !IsKeyPressedMap(ImGuiKey_Space)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed);
-        const bool is_cancel   = IsKeyPressedMap(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed);
+        const bool is_validate_enter = IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_KeypadEnter);
+        const bool is_validate_nav = (IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) && !IsKeyPressed(ImGuiKey_Space)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed);
+        const bool is_cancel   = IsKeyPressed(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed);
 
-        if (IsKeyPressedMap(ImGuiKey_LeftArrow))                        { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
-        else if (IsKeyPressedMap(ImGuiKey_RightArrow))                  { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
-        else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline)     { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
-        else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline)   { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
-        else if (IsKeyPressedMap(ImGuiKey_PageUp) && is_multiline)      { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; }
-        else if (IsKeyPressedMap(ImGuiKey_PageDown) && is_multiline)    { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; }
-        else if (IsKeyPressedMap(ImGuiKey_Home))                        { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
-        else if (IsKeyPressedMap(ImGuiKey_End))                         { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
-        else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly && !is_cut) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
-        else if (IsKeyPressedMap(ImGuiKey_Backspace) && !is_readonly)
+        if (IsKeyPressed(ImGuiKey_LeftArrow))                        { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
+        else if (IsKeyPressed(ImGuiKey_RightArrow))                  { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
+        else if (IsKeyPressed(ImGuiKey_UpArrow) && is_multiline)     { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
+        else if (IsKeyPressed(ImGuiKey_DownArrow) && is_multiline)   { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
+        else if (IsKeyPressed(ImGuiKey_PageUp) && is_multiline)      { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; }
+        else if (IsKeyPressed(ImGuiKey_PageDown) && is_multiline)    { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; }
+        else if (IsKeyPressed(ImGuiKey_Home))                        { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
+        else if (IsKeyPressed(ImGuiKey_End))                         { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
+        else if (IsKeyPressed(ImGuiKey_Delete) && !is_readonly && !is_cut) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
+        else if (IsKeyPressed(ImGuiKey_Backspace) && !is_readonly)
         {
             if (!state->HasSelection())
             {
@@ -4365,7 +4371,7 @@
             state->OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO);
             state->ClearSelection();
         }
-        else if (is_shortcut_key && IsKeyPressedMap(ImGuiKey_A))
+        else if (is_shortcut_key && IsKeyPressed(ImGuiKey_A))
         {
             state->SelectAll();
             state->CursorFollow = true;
@@ -4471,18 +4477,18 @@
 
                 // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
                 ImGuiInputTextFlags event_flag = 0;
-                ImGuiKey event_key = ImGuiKey_COUNT;
-                if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab))
+                ImGuiKey event_key = ImGuiKey_None;
+                if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressed(ImGuiKey_Tab))
                 {
                     event_flag = ImGuiInputTextFlags_CallbackCompletion;
                     event_key = ImGuiKey_Tab;
                 }
-                else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow))
+                else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_UpArrow))
                 {
                     event_flag = ImGuiInputTextFlags_CallbackHistory;
                     event_key = ImGuiKey_UpArrow;
                 }
-                else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow))
+                else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_DownArrow))
                 {
                     event_flag = ImGuiInputTextFlags_CallbackHistory;
                     event_key = ImGuiKey_DownArrow;