From fa2b318dd6190852a6fe7ebc952b6551e93899e0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 22 Feb 2022 14:36:27 +0100 Subject: [PATCH] IO: Fixed input queue trickling of interleaved keys/chars events when InputText() is not active. (#4921, #4858) --- docs/CHANGELOG.txt | 8 ++++++++ imgui.cpp | 31 ++++++++++++++++++++++++++----- imgui.h | 2 +- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ef59b38c7..21e783b42 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,14 @@ Other Changes: - Direct accesses to io.KeysDown[] with legacy indices didn't work (with new backends). - Direct accesses to io.KeysDown[GetKeyIndex(XXX)] would access invalid data (with old/new backends). - Calling IsKeyDown() didn't have those problems, and is recommended as io.KeysDown[] is obsolete. +- IO: Fixed input queue trickling of interleaved keys/chars events (which are frequent especially + when holding down a key as OS submits chars repeat events) delaying key presses and mouse movements. + In particular, using the input system for fast game-like actions (e.g. WASD camera move) would + typically have been impacted, as well as holding a key while dragging mouse. Constraints have + been lifted and are now only happening when e.g. an InputText() widget is active. (#4921, #4858) + Not that even thought you shouldn't need to disable io.ConfigInputTrickleEventQueue, you can + technically dynamically change its setting based on the context (e.g. disable only when hovering + or interacting with a game/3D view). - Clipper: Fixed a regression in 1.86 when not calling clipper.End() and late destructing the clipper instance. High-level languages (Lua,Rust etc.) would typically be affected. (#4822) - IsItemHovered(): added ImGuiHoveredFlags_NoNavOverride to disable the behavior where the diff --git a/imgui.cpp b/imgui.cpp index fb6b422a7..196f0d19e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7799,8 +7799,18 @@ static const char* GetInputSourceName(ImGuiInputSource source) return input_source_names[source]; } +/*static void DebugLogInputEvent(const char* prefix, const ImGuiInputEvent* e) +{ + if (e->Type == ImGuiInputEventType_MousePos) { IMGUI_DEBUG_LOG("%s: MousePos (%.1f %.1f)\n", prefix, e->MousePos.PosX, e->MousePos.PosY); return; } + if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG("%s: MouseButton %d %s\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up"); return; } + if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG("%s: MouseWheel (%.1f %.1f)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; } + if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG("%s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; } + if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG("%s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; } + if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG("%s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; } +}*/ // Process input queue +// We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'. // - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost) // - trickle_fast_inputs = true : process as many events as possible (successive down/up/down/up will be trickled over several frames so nothing is lost) (new feature in 1.87) void ImGui::UpdateInputEvents(bool trickle_fast_inputs) @@ -7808,7 +7818,12 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; - bool mouse_moved = false, mouse_wheeled = false, key_changed = false, text_inputed = false; + // Only trickle chars<>key when working with InputText() + // FIXME: InputText() could parse event trail? + // FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters) + const bool trickle_interleaved_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1); + + bool mouse_moved = false, mouse_wheeled = false, key_changed = false, text_inputted = false; int mouse_button_changed = 0x00; ImBitArray key_changed_mask; @@ -7824,7 +7839,7 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) if (io.MousePos.x != event_pos.x || io.MousePos.y != event_pos.y) { // Trickling Rule: Stop processing queued events if we already handled a mouse button change - if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputed)) + if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted)) break; io.MousePos = event_pos; mouse_moved = true; @@ -7864,7 +7879,7 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) if (keydata->Down != e->Key.Down || keydata->AnalogValue != e->Key.AnalogValue) { // Trickling Rule: Stop processing queued events if we got multiple action on the same button - if (trickle_fast_inputs && keydata->Down != e->Key.Down && (key_changed_mask.TestBit(keydata_index) || text_inputed || mouse_button_changed != 0)) + if (trickle_fast_inputs && keydata->Down != e->Key.Down && (key_changed_mask.TestBit(keydata_index) || text_inputted || mouse_button_changed != 0)) break; keydata->Down = e->Key.Down; keydata->AnalogValue = e->Key.AnalogValue; @@ -7891,11 +7906,12 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) else if (e->Type == ImGuiInputEventType_Text) { // Trickling Rule: Stop processing queued events if keys/mouse have been interacted with - if (trickle_fast_inputs && (key_changed || mouse_button_changed != 0 || mouse_moved || mouse_wheeled)) + if (trickle_fast_inputs && ((key_changed && trickle_interleaved_keys_and_text) || mouse_button_changed != 0 || mouse_moved || mouse_wheeled)) break; unsigned int c = e->Text.Char; io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID); - text_inputed = true; + if (trickle_interleaved_keys_and_text) + text_inputted = true; } else if (e->Type == ImGuiInputEventType_Focus) { @@ -7914,6 +7930,11 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) for (int n = 0; n < event_n; n++) g.InputEventsTrail.push_back(g.InputEventsQueue[n]); + // [DEBUG] + /*if (event_n != 0) + for (int n = 0; n < g.InputEventsQueue.Size; n++) + DebugLogInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]);*/ + // Remaining events will be processed on the next frame if (event_n == g.InputEventsQueue.Size) g.InputEventsQueue.resize(0); diff --git a/imgui.h b/imgui.h index 2dc891433..20e1bfcf7 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.88 WIP" -#define IMGUI_VERSION_NUM 18708 +#define IMGUI_VERSION_NUM 18709 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE