SDL: Text input is no longer automatically enabled when initializing video.

From 72fc6f86e5d605a3787222bc7dc18c5379047f4a Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 23 Mar 2024 09:21:28 -0700
Subject: [PATCH] Text input is no longer automatically enabled when
 initializing video.

Fixes https://github.com/libsdl-org/SDL/issues/9309
Fixes https://github.com/libsdl-org/SDL/issues/9268
---
 docs/README-migration.md                    |  2 +
 include/SDL3/SDL_hints.h                    |  5 +-
 src/core/haiku/SDL_BApp.h                   |  2 +-
 src/events/SDL_events.c                     |  8 ---
 src/events/SDL_keyboard.c                   | 10 ++--
 src/video/SDL_sysvideo.h                    |  1 +
 src/video/SDL_video.c                       | 59 ++++++++-------------
 src/video/cocoa/SDL_cocoakeyboard.m         |  2 +-
 src/video/emscripten/SDL_emscriptenevents.c |  2 +-
 src/video/wayland/SDL_waylandevents.c       |  4 +-
 src/video/x11/SDL_x11events.c               |  8 +--
 11 files changed, 41 insertions(+), 62 deletions(-)

diff --git a/docs/README-migration.md b/docs/README-migration.md
index 394c29c1c3562..145ac536e17c6 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -855,6 +855,8 @@ The following symbols have been removed:
 
 ## SDL_keyboard.h
 
+Text input is no longer automatically enabled when initializing video, you should call SDL_StartTextInput() when you want to receive text input and call SDL_StopTextInput() when you are done. Starting text input may shown an input method editor (IME) and cause key up/down events to be skipped, so should only be enabled when the application wants text input.
+
 The following functions have been renamed:
 * SDL_IsScreenKeyboardShown() => SDL_ScreenKeyboardShown()
 * SDL_IsTextInputActive() => SDL_TextInputActive()
diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index a1ae55d4d5275..2939d75e75a29 100644
--- a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -439,10 +439,11 @@ extern "C" {
  * A variable that controls whether the on-screen keyboard should be shown when text input is active
  *
  * The variable can be set to the following values:
+ *   "auto"    - The on-screen keyboard will be shown if there is no physical keyboard attached. (default)
  *   "0"       - Do not show the on-screen keyboard.
- *   "1"       - Show the on-screen keyboard. (default)
+ *   "1"       - Show the on-screen keyboard, if available.
  *
- * This hint must be set before text input is activated.
+ * This hint must be set before SDL_StartTextInput() is called
  */
 #define SDL_HINT_ENABLE_SCREEN_KEYBOARD "SDL_ENABLE_SCREEN_KEYBOARD"
 
diff --git a/src/core/haiku/SDL_BApp.h b/src/core/haiku/SDL_BApp.h
index 484b0f3c8b64c..66d88d1dddc79 100644
--- a/src/core/haiku/SDL_BApp.h
+++ b/src/core/haiku/SDL_BApp.h
@@ -306,7 +306,7 @@ class SDL_BLooper : public BLooper
         HAIKU_SetKeyState(scancode, state);
         SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, state, HAIKU_GetScancodeFromBeKey(scancode));
 
-        if (state == SDL_PRESSED && SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
+        if (state == SDL_PRESSED && SDL_TextInputActive()) {
             const int8 *keyUtf8;
             ssize_t count;
             if (msg->FindData("key-utf8", B_INT8_TYPE, (const void **)&keyUtf8, &count) == B_OK) {
diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c
index d1b2776413c92..968f53c5d4d9f 100644
--- a/src/events/SDL_events.c
+++ b/src/events/SDL_events.c
@@ -729,14 +729,6 @@ int SDL_StartEventLoop(void)
     }
 #endif /* !SDL_THREADS_DISABLED */
 
-    /* Process most event types */
-    SDL_SetEventEnabled(SDL_EVENT_TEXT_INPUT, SDL_FALSE);
-    SDL_SetEventEnabled(SDL_EVENT_TEXT_EDITING, SDL_FALSE);
-#if 0 /* Leave these events enabled so apps can respond to items being dragged onto them at startup */
-    SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, SDL_FALSE);
-    SDL_SetEventEnabled(SDL_EVENT_DROP_TEXT, SDL_FALSE);
-#endif
-
     SDL_EventQ.active = SDL_TRUE;
     SDL_UnlockMutex(SDL_EventQ.lock);
     return 0;
diff --git a/src/events/SDL_keyboard.c b/src/events/SDL_keyboard.c
index 3ef19e6506712..871d45a737b49 100644
--- a/src/events/SDL_keyboard.c
+++ b/src/events/SDL_keyboard.c
@@ -912,11 +912,10 @@ int SDL_SetKeyboardFocus(SDL_Window *window)
             SDL_assert(!(keyboard->focus->flags & SDL_WINDOW_MOUSE_CAPTURE));
         }
 
-        SDL_SendWindowEvent(keyboard->focus, SDL_EVENT_WINDOW_FOCUS_LOST,
-                            0, 0);
+        SDL_SendWindowEvent(keyboard->focus, SDL_EVENT_WINDOW_FOCUS_LOST, 0, 0);
 
         /* Ensures IME compositions are committed */
-        if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
+        if (SDL_TextInputActive()) {
             if (video && video->StopTextInput) {
                 video->StopTextInput(video);
             }
@@ -926,10 +925,9 @@ int SDL_SetKeyboardFocus(SDL_Window *window)
     keyboard->focus = window;
 
     if (keyboard->focus) {
-        SDL_SendWindowEvent(keyboard->focus, SDL_EVENT_WINDOW_FOCUS_GAINED,
-                            0, 0);
+        SDL_SendWindowEvent(keyboard->focus, SDL_EVENT_WINDOW_FOCUS_GAINED, 0, 0);
 
-        if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
+        if (SDL_TextInputActive()) {
             if (video && video->StartTextInput) {
                 video->StartTextInput(video);
             }
diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index f96b42837dc41..61e1c8ceaa444 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -365,6 +365,7 @@ struct SDL_VideoDevice
     SDL_bool setting_display_mode;
     Uint32 device_caps;
     SDL_SystemTheme system_theme;
+    SDL_bool text_input_active;
 
     /* * * */
     /* Data used by the GL drivers */
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index eff8e398e4138..d3c3369903d12 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -562,22 +562,6 @@ int SDL_VideoInit(const char *driver_name)
         SDL_DisableScreenSaver();
     }
 
-#if !defined(SDL_VIDEO_DRIVER_N3DS)
-    {
-        /* In the initial state we don't want to pop up an on-screen keyboard,
-         * but we do want to allow text input from other mechanisms.
-         */
-        const char *hint = SDL_GetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD);
-        if (!hint) {
-            SDL_SetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD, "0");
-        }
-        SDL_StartTextInput();
-        if (!hint) {
-            SDL_SetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD, NULL);
-        }
-    }
-#endif /* !SDL_VIDEO_DRIVER_N3DS */
-
     SDL_PostInitMouse();
 
     /* We're ready to go! */
@@ -4808,24 +4792,25 @@ void SDL_WM_SetIcon(SDL_Surface *icon, Uint8 *mask)
 
 void SDL_StartTextInput(void)
 {
-    SDL_Window *window;
-
-    /* First, enable text events */
-    SDL_SetEventEnabled(SDL_EVENT_TEXT_INPUT, SDL_TRUE);
-    SDL_SetEventEnabled(SDL_EVENT_TEXT_EDITING, SDL_TRUE);
+    if (!_this) {
+        return;
+    }
 
-    /* Then show the on-screen keyboard, if any */
-    if (SDL_GetHintBoolean(SDL_HINT_ENABLE_SCREEN_KEYBOARD, SDL_TRUE)) {
-        window = SDL_GetKeyboardFocus();
-        if (window && _this && _this->ShowScreenKeyboard) {
+    /* Show the on-screen keyboard, if desired */
+    const char *hint = SDL_GetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD);
+    if (((!hint || SDL_strcasecmp(hint, "auto")) && !SDL_HasKeyboard()) ||
+        SDL_GetStringBoolean(hint, SDL_FALSE)) {
+        SDL_Window *window = SDL_GetKeyboardFocus();
+        if (window && _this->ShowScreenKeyboard) {
             _this->ShowScreenKeyboard(_this, window);
         }
     }
 
     /* Finally start the text input system */
-    if (_this && _this->StartTextInput) {
+    if (_this->StartTextInput) {
         _this->StartTextInput(_this);
     }
+    _this->text_input_active = SDL_TRUE;
 }
 
 void SDL_ClearComposition(void)
@@ -4846,29 +4831,29 @@ SDL_bool SDL_TextInputShown(void)
 
 SDL_bool SDL_TextInputActive(void)
 {
-    return SDL_EventEnabled(SDL_EVENT_TEXT_INPUT);
+    return _this && _this->text_input_active;
 }
 
 void SDL_StopTextInput(void)
 {
-    SDL_Window *window;
+    if (!_this) {
+        return;
+    }
 
     /* Stop the text input system */
-    if (_this && _this->StopTextInput) {
+    if (_this->StopTextInput) {
         _this->StopTextInput(_this);
     }
 
-    /* Hide the on-screen keyboard, if any */
-    if (SDL_GetHintBoolean(SDL_HINT_ENABLE_SCREEN_KEYBOARD, SDL_TRUE)) {
-        window = SDL_GetKeyboardFocus();
-        if (window && _this && _this->HideScreenKeyboard) {
+    /* Hide the on-screen keyboard, if desired */
+    const char *hint = SDL_GetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD);
+    if (((!hint || SDL_strcasecmp(hint, "auto")) && !SDL_HasKeyboard()) ||
+        SDL_GetStringBoolean(hint, SDL_FALSE)) {
+        SDL_Window *window = SDL_GetKeyboardFocus();
+        if (window && _this->HideScreenKeyboard) {
             _this->HideScreenKeyboard(_this, window);
         }
     }
-
-    /* Finally disable text events */
-    SDL_SetEventEnabled(SDL_EVENT_TEXT_INPUT, SDL_FALSE);
-    SDL_SetEventEnabled(SDL_EVENT_TEXT_EDITING, SDL_FALSE);
 }
 
 int SDL_SetTextInputRect(const SDL_Rect *rect)
diff --git a/src/video/cocoa/SDL_cocoakeyboard.m b/src/video/cocoa/SDL_cocoakeyboard.m
index fc12196c8dd56..61866a413799f 100644
--- a/src/video/cocoa/SDL_cocoakeyboard.m
+++ b/src/video/cocoa/SDL_cocoakeyboard.m
@@ -420,7 +420,7 @@ void Cocoa_HandleKeyEvent(SDL_VideoDevice *_this, NSEvent *event)
             SDL_Log("The key you just pressed is not recognized by SDL. To help get this fixed, report this to the SDL forums/mailing list <https://discourse.libsdl.org/> or to Christian Walther <cwalther@gmx.ch>. Mac virtual key code is %d.\n", scancode);
         }
 #endif
-        if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
+        if (SDL_TextInputActive()) {
             /* FIXME CW 2007-08-16: only send those events to the field editor for which we actually want text events, not e.g. esc or function keys. Arrow keys in particular seem to produce crashes sometimes. */
             [data.fieldEdit interpretKeyEvents:[NSArray arrayWithObject:event]];
 #if 0
diff --git a/src/video/emscripten/SDL_emscriptenevents.c b/src/video/emscripten/SDL_emscriptenevents.c
index 7bf4e76ca3a86..c67a8f3c5155b 100644
--- a/src/video/emscripten/SDL_emscriptenevents.c
+++ b/src/video/emscripten/SDL_emscriptenevents.c
@@ -828,7 +828,7 @@ static EM_BOOL Emscripten_HandleKey(int eventType, const EmscriptenKeyboardEvent
         is_nav_key = SDL_TRUE;
     }
 
-    if ((eventType == EMSCRIPTEN_EVENT_KEYDOWN) && SDL_EventEnabled(SDL_EVENT_TEXT_INPUT) && !is_nav_key) {
+    if ((eventType == EMSCRIPTEN_EVENT_KEYDOWN) && SDL_TextInputActive() && !is_nav_key) {
         prevent_default = SDL_FALSE;
     }
 
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 1d72eaeb415b0..75fbbc3ebe1d8 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -377,7 +377,7 @@ int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS)
     WAYLAND_wl_display_flush(d->display);
 
 #ifdef SDL_USE_IME
-    if (!d->text_input_manager && SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
+    if (!d->text_input_manager && SDL_TextInputActive()) {
         SDL_IME_PumpEvents();
     }
 #endif
@@ -450,7 +450,7 @@ void Wayland_PumpEvents(SDL_VideoDevice *_this)
     int err;
 
 #ifdef SDL_USE_IME
-    if (!d->text_input_manager && SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
+    if (!d->text_input_manager && SDL_TextInputActive()) {
         SDL_IME_PumpEvents();
     }
 #endif
diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c
index c170a59f5b4b7..72ad194c60b95 100644
--- a/src/video/x11/SDL_x11events.c
+++ b/src/video/x11/SDL_x11events.c
@@ -1254,7 +1254,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
 #endif
 
 #ifdef SDL_USE_IME
-        if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
+        if (SDL_TextInputActive()) {
             handled_by_ime = SDL_IME_ProcessKeyEvent(keysym, keycode, (xevent->type == KeyPress ? SDL_PRESSED : SDL_RELEASED));
         }
 #endif
@@ -1355,7 +1355,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
                 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y);
 
 #ifdef SDL_USE_IME
-                if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
+                if (SDL_TextInputActive()) {
                     /* Update IME candidate list position */
                     SDL_IME_UpdateTextRect(NULL);
                 }
@@ -1937,7 +1937,7 @@ int X11_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS)
     X11_DispatchEvent(_this, &xevent);
 
 #ifdef SDL_USE_IME
-    if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
+    if (SDL_TextInputActive()) {
         SDL_IME_PumpEvents();
     }
 #endif
@@ -1998,7 +1998,7 @@ void X11_PumpEvents(SDL_VideoDevice *_this)
     }
 
 #ifdef SDL_USE_IME
-    if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
+    if (SDL_TextInputActive()) {
         SDL_IME_PumpEvents();
     }
 #endif