From 8a3929d48cccd8a3ae01950bd0281d05d0acb19e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sat, 27 Jul 2024 17:14:13 +0200 Subject: [PATCH] Support IME composition strings of arbitrary length Previously, IME composition strings were limited to at most 32 bytes, e.g. 10 Hiragana characters. Now, `SDL_TextEditingExtEvent` (`SDL_TEXTEDITING_EXT`) is supported, which uses heap-allocated composition strings of arbitrary length. On the other hand, the buffer for input event text was larger than necessary, as SDL text events only contain at most 32 bytes (`SDL_TEXTINPUTEVENT_TEXT_SIZE` is not used directly to avoid the include). With the hint for extended IME handling enabled, long composition text will be sent as multiple text events (without breaking UTF-8). --- src/engine/client/client.cpp | 5 ++++ src/engine/client/input.cpp | 56 +++++++++++++++++++----------------- src/engine/client/input.h | 10 +++---- src/engine/input.h | 13 +-------- 4 files changed, 41 insertions(+), 43 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index a10a6f157..fe077a9d9 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -4562,6 +4562,11 @@ int main(int argc, const char **argv) SDL_SetHint("SDL_TOUCH_MOUSE_EVENTS", "0"); SDL_SetHint("SDL_MOUSE_TOUCH_EVENTS", "0"); + // Support longer IME composition strings (enables SDL_TEXTEDITING_EXT). +#if SDL_VERSION_ATLEAST(2, 0, 22) + SDL_SetHint(SDL_HINT_IME_SUPPORT_EXTENDED_TEXT, "1"); +#endif + #if defined(CONF_PLATFORM_MACOS) // Hints will not be set if there is an existing override hint or environment variable that takes precedence. // So this respects cli environment overrides. diff --git a/src/engine/client/input.cpp b/src/engine/client/input.cpp index dd5fe2ba6..8e3da5c42 100644 --- a/src/engine/client/input.cpp +++ b/src/engine/client/input.cpp @@ -70,7 +70,6 @@ CInput::CInput() m_pClipboardText = nullptr; - m_CompositionLength = COMP_LENGTH_INACTIVE; m_CompositionCursor = 0; m_CandidateSelectedIndex = -1; @@ -328,9 +327,8 @@ void CInput::StopTextInput() SDL_StopTextInput(); // disable system messages for performance SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE); - m_CompositionLength = COMP_LENGTH_INACTIVE; + m_CompositionString = ""; m_CompositionCursor = 0; - m_aComposition[0] = '\0'; m_vCandidates.clear(); } @@ -574,6 +572,26 @@ void CInput::HandleTouchMotionEvent(const SDL_TouchFingerEvent &Event) } } +void CInput::HandleTextEditingEvent(const char *pText, int Start, int Length) +{ + if(pText[0] != '\0') + { + m_CompositionString = pText; + m_CompositionCursor = 0; + for(int i = 0; i < Start; i++) + { + m_CompositionCursor = str_utf8_forward(m_CompositionString.c_str(), m_CompositionCursor); + } + // Length is currently unused on Windows and will always be 0, so we don't support selecting composition text + AddTextEvent(""); + } + else + { + m_CompositionString = ""; + m_CompositionCursor = 0; + } +} + void CInput::SetCompositionWindowPosition(float X, float Y, float H) { SDL_Rect Rect; @@ -658,29 +676,18 @@ int CInput::Update() break; case SDL_TEXTEDITING: - { - m_CompositionLength = str_length(Event.edit.text); - if(m_CompositionLength) - { - str_copy(m_aComposition, Event.edit.text); - m_CompositionCursor = 0; - for(int i = 0; i < Event.edit.start; i++) - m_CompositionCursor = str_utf8_forward(m_aComposition, m_CompositionCursor); - // Event.edit.length is currently unused on Windows and will always be 0, so we don't support selecting composition text - AddTextEvent(""); - } - else - { - m_aComposition[0] = '\0'; - m_CompositionLength = 0; - m_CompositionCursor = 0; - } + HandleTextEditingEvent(Event.edit.text, Event.edit.start, Event.edit.length); break; - } + +#if SDL_VERSION_ATLEAST(2, 0, 22) + case SDL_TEXTEDITING_EXT: + HandleTextEditingEvent(Event.editExt.text, Event.editExt.start, Event.editExt.length); + SDL_free(Event.editExt.text); + break; +#endif case SDL_TEXTINPUT: - m_aComposition[0] = '\0'; - m_CompositionLength = COMP_LENGTH_INACTIVE; + m_CompositionString = ""; m_CompositionCursor = 0; AddTextEvent(Event.text.text); break; @@ -861,9 +868,6 @@ int CInput::Update() } } - if(m_CompositionLength == 0) - m_CompositionLength = COMP_LENGTH_INACTIVE; - return 0; } diff --git a/src/engine/client/input.h b/src/engine/client/input.h index e53ec3b4c..bc94ad707 100644 --- a/src/engine/client/input.h +++ b/src/engine/client/input.h @@ -84,9 +84,8 @@ private: #endif // IME support - char m_aComposition[MAX_COMPOSITION_ARRAY_SIZE]; + std::string m_CompositionString; int m_CompositionCursor; - int m_CompositionLength; std::vector m_vCandidates; int m_CandidateSelectedIndex; @@ -113,6 +112,7 @@ private: void HandleTouchDownEvent(const SDL_TouchFingerEvent &Event); void HandleTouchUpEvent(const SDL_TouchFingerEvent &Event); void HandleTouchMotionEvent(const SDL_TouchFingerEvent &Event); + void HandleTextEditingEvent(const char *pText, int Start, int Length); char m_aDropFile[IO_MAX_PATH_LENGTH]; @@ -155,10 +155,10 @@ public: void StartTextInput() override; void StopTextInput() override; - const char *GetComposition() const override { return m_aComposition; } - bool HasComposition() const override { return m_CompositionLength != COMP_LENGTH_INACTIVE; } + const char *GetComposition() const override { return m_CompositionString.c_str(); } + bool HasComposition() const override { return !m_CompositionString.empty(); } int GetCompositionCursor() const override { return m_CompositionCursor; } - int GetCompositionLength() const override { return m_CompositionLength; } + int GetCompositionLength() const override { return m_CompositionString.length(); } const char *GetCandidate(int Index) const override { return m_vCandidates[Index].c_str(); } int GetCandidateCount() const override { return m_vCandidates.size(); } int GetCandidateSelectedIndex() const override { return m_CandidateSelectedIndex; } diff --git a/src/engine/input.h b/src/engine/input.h index efaec84f2..f80a210f4 100644 --- a/src/engine/input.h +++ b/src/engine/input.h @@ -19,18 +19,13 @@ class IInput : public IInterface { MACRO_INTERFACE("input") public: - enum - { - INPUT_TEXT_SIZE = 32 * UTF8_BYTE_LENGTH + 1, - }; - class CEvent { public: int m_Flags; int m_Key; uint32_t m_InputCount; - char m_aText[INPUT_TEXT_SIZE]; + char m_aText[32]; // SDL_TEXTINPUTEVENT_TEXT_SIZE }; enum @@ -45,12 +40,6 @@ public: CURSOR_MOUSE, CURSOR_JOYSTICK, }; - enum - { - MAX_COMPOSITION_ARRAY_SIZE = 32, // SDL2 limitation - - COMP_LENGTH_INACTIVE = -1, - }; // events virtual void ConsumeEvents(std::function Consumer) const = 0;