SDL: Added SDL_EVENT_SCREEN_KEYBOARD_SHOWN and SDL_EVENT_SCREEN_KEYBOARD_HIDDEN

From 1871b998cd0417d3d98259cd0c42a716ed644fe4 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 9 Oct 2025 23:53:02 -0700
Subject: [PATCH] Added SDL_EVENT_SCREEN_KEYBOARD_SHOWN and
 SDL_EVENT_SCREEN_KEYBOARD_HIDDEN

Fixes https://github.com/libsdl-org/SDL/issues/13049
---
 android-project/app/proguard-rules.pro        |  1 -
 .../main/java/org/libsdl/app/SDLActivity.java | 27 ++++----------
 include/SDL3/SDL_events.h                     |  2 ++
 src/core/android/SDL_android.c                | 33 +++++++++++------
 src/core/android/SDL_android.h                |  1 -
 src/events/SDL_events.c                       |  4 +++
 src/events/SDL_keyboard.c                     |  6 ++++
 src/video/SDL_sysvideo.h                      |  5 ++-
 src/video/SDL_video.c                         | 35 +++++++++++++++++--
 src/video/android/SDL_androidevents.c         |  5 ---
 src/video/android/SDL_androidkeyboard.c       | 13 ++-----
 src/video/android/SDL_androidkeyboard.h       |  3 +-
 src/video/android/SDL_androidvideo.c          |  1 -
 src/video/gdk/SDL_gdktextinput.cpp            | 11 +++---
 src/video/gdk/SDL_gdktextinput.h              |  1 -
 src/video/openvr/SDL_openvrvideo.c            | 11 ++----
 src/video/openvr/SDL_openvrvideo.h            |  1 -
 src/video/psp/SDL_pspvideo.c                  | 10 +++---
 src/video/psp/SDL_pspvideo.h                  |  1 -
 src/video/uikit/SDL_uikitvideo.m              |  1 -
 src/video/uikit/SDL_uikitviewcontroller.h     |  1 -
 src/video/uikit/SDL_uikitviewcontroller.m     | 23 ++++++------
 src/video/vita/SDL_vitavideo.c                | 19 ++++------
 src/video/vita/SDL_vitavideo.h                |  1 -
 src/video/windows/SDL_windowsvideo.c          |  1 -
 src/video/x11/SDL_x11keyboard.c               | 11 ++----
 src/video/x11/SDL_x11keyboard.h               |  1 -
 src/video/x11/SDL_x11video.c                  |  1 -
 src/video/x11/SDL_x11video.h                  |  1 -
 29 files changed, 110 insertions(+), 121 deletions(-)

diff --git a/android-project/app/proguard-rules.pro b/android-project/app/proguard-rules.pro
index 41c2a0380d724..28fb4b2357bee 100644
--- a/android-project/app/proguard-rules.pro
+++ b/android-project/app/proguard-rules.pro
@@ -30,7 +30,6 @@
     boolean isAndroidTV();
     boolean isChromebook();
     boolean isDeXMode();
-    boolean isScreenKeyboardShown();
     boolean isTablet();
     void manualBackButton();
     int messageboxShowMessageBox(int, java.lang.String, java.lang.String, int[], int[], java.lang.String[], int[]);
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
index a49cd940d0c60..796417588aff3 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
@@ -215,7 +215,6 @@ public enum NativeState {
     protected static SDLActivity mSingleton;
     protected static SDLSurface mSurface;
     protected static SDLDummyEdit mTextEdit;
-    protected static boolean mScreenKeyboardShown;
     protected static ViewGroup mLayout;
     protected static SDLClipboardHandler mClipboardHandler;
     protected static Hashtable<Integer, PointerIcon> mCursors;
@@ -951,7 +950,7 @@ public void handleMessage(Message msg) {
                     InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
                     imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
 
-                    mScreenKeyboardShown = false;
+                    onNativeScreenKeyboardHidden();
 
                     mSurface.requestFocus();
                 }
@@ -1070,6 +1069,8 @@ public static native void onNativeTouch(int touchDevId, int pointerFingerId,
     public static native void onNativeSurfaceCreated();
     public static native void onNativeSurfaceChanged();
     public static native void onNativeSurfaceDestroyed();
+    public static native void onNativeScreenKeyboardShown();
+    public static native void onNativeScreenKeyboardHidden();
     public static native String nativeGetHint(String name);
     public static native boolean nativeGetHintBoolean(String name, boolean default_value);
     public static native void nativeSetenv(String name, String value);
@@ -1205,24 +1206,6 @@ public static boolean shouldMinimizeOnFocusLoss() {
         return false;
     }
 
-    /**
-     * This method is called by SDL using JNI.
-     */
-    public static boolean isScreenKeyboardShown()
-    {
-        if (mTextEdit == null) {
-            return false;
-        }
-
-        if (!mScreenKeyboardShown) {
-            return false;
-        }
-
-        InputMethodManager imm = (InputMethodManager)getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-        return imm.isAcceptingText();
-
-    }
-
     /**
      * This method is called by SDL using JNI.
      */
@@ -1442,7 +1425,9 @@ public void run() {
             InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
             imm.showSoftInput(mTextEdit, 0);
 
-            mScreenKeyboardShown = true;
+            if (imm.isAcceptingText()) {
+                onNativeScreenKeyboardShown();
+            }
         }
     }
 
diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h
index 470dd608d1674..a2201c9c21cbc 100644
--- a/include/SDL3/SDL_events.h
+++ b/include/SDL3/SDL_events.h
@@ -176,6 +176,8 @@ typedef enum SDL_EventType
     SDL_EVENT_KEYBOARD_ADDED,          /**< A new keyboard has been inserted into the system */
     SDL_EVENT_KEYBOARD_REMOVED,        /**< A keyboard has been removed */
     SDL_EVENT_TEXT_EDITING_CANDIDATES, /**< Keyboard text editing candidates */
+    SDL_EVENT_SCREEN_KEYBOARD_SHOWN,   /**< The on-screen keyboard has been shown */
+    SDL_EVENT_SCREEN_KEYBOARD_HIDDEN,  /**< The on-screen keyboard has been hidden */
 
     /* Mouse events */
     SDL_EVENT_MOUSE_MOTION    = 0x400, /**< Mouse moved */
diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c
index 33d7815ff46f0..c5c3683e262a2 100644
--- a/src/core/android/SDL_android.c
+++ b/src/core/android/SDL_android.c
@@ -96,6 +96,12 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(
 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(
     JNIEnv *env, jclass jcls);
 
+JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeScreenKeyboardShown)(
+    JNIEnv *env, jclass jcls);
+
+JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeScreenKeyboardHidden)(
+    JNIEnv *env, jclass jcls);
+
 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
     JNIEnv *env, jclass jcls,
     jint keycode);
@@ -208,6 +214,8 @@ static JNINativeMethod SDLActivity_tab[] = {
     { "onNativeSurfaceCreated", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceCreated) },
     { "onNativeSurfaceChanged", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceChanged) },
     { "onNativeSurfaceDestroyed", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed) },
+    { "onNativeScreenKeyboardShown", "()V", SDL_JAVA_INTERFACE(onNativeScreenKeyboardShown) },
+    { "onNativeScreenKeyboardHidden", "()V", SDL_JAVA_INTERFACE(onNativeScreenKeyboardHidden) },
     { "onNativeKeyDown", "(I)V", SDL_JAVA_INTERFACE(onNativeKeyDown) },
     { "onNativeKeyUp", "(I)V", SDL_JAVA_INTERFACE(onNativeKeyUp) },
     { "onNativeSoftReturnKey", "()Z", SDL_JAVA_INTERFACE(onNativeSoftReturnKey) },
@@ -352,7 +360,6 @@ static jmethodID midInitTouch;
 static jmethodID midIsAndroidTV;
 static jmethodID midIsChromebook;
 static jmethodID midIsDeXMode;
-static jmethodID midIsScreenKeyboardShown;
 static jmethodID midIsTablet;
 static jmethodID midManualBackButton;
 static jmethodID midMinimizeWindow;
@@ -642,7 +649,6 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
     midIsAndroidTV = (*env)->GetStaticMethodID(env, mActivityClass, "isAndroidTV", "()Z");
     midIsChromebook = (*env)->GetStaticMethodID(env, mActivityClass, "isChromebook", "()Z");
     midIsDeXMode = (*env)->GetStaticMethodID(env, mActivityClass, "isDeXMode", "()Z");
-    midIsScreenKeyboardShown = (*env)->GetStaticMethodID(env, mActivityClass, "isScreenKeyboardShown", "()Z");
     midIsTablet = (*env)->GetStaticMethodID(env, mActivityClass, "isTablet", "()Z");
     midManualBackButton = (*env)->GetStaticMethodID(env, mActivityClass, "manualBackButton", "()V");
     midMinimizeWindow = (*env)->GetStaticMethodID(env, mActivityClass, "minimizeWindow", "()V");
@@ -675,7 +681,6 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
         !midIsAndroidTV ||
         !midIsChromebook ||
         !midIsDeXMode ||
-        !midIsScreenKeyboardShown ||
         !midIsTablet ||
         !midManualBackButton ||
         !midMinimizeWindow ||
@@ -1242,6 +1247,10 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, j
     }
 #endif
 
+    if (Android_Window) {
+        Android_RestoreScreenKeyboard(SDL_GetVideoDevice(), Android_Window);
+    }
+
     SDL_UnlockMutex(Android_ActivityMutex);
 }
 
@@ -1287,6 +1296,16 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv *env,
     SDL_UnlockMutex(Android_ActivityMutex);
 }
 
+JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeScreenKeyboardShown)(JNIEnv *env, jclass jcls)
+{
+    SDL_SendScreenKeyboardShown();
+}
+
+JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeScreenKeyboardHidden)(JNIEnv *env, jclass jcls)
+{
+    SDL_SendScreenKeyboardHidden();
+}
+
 // Keydown
 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
     JNIEnv *env, jclass jcls,
@@ -2149,14 +2168,6 @@ void Android_JNI_HideScreenKeyboard(void)
     Android_JNI_SendMessage(COMMAND_TEXTEDIT_HIDE, 0);
 }
 
-bool Android_JNI_IsScreenKeyboardShown(void)
-{
-    JNIEnv *env = Android_JNI_GetEnv();
-    jboolean is_shown = 0;
-    is_shown = (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsScreenKeyboardShown);
-    return is_shown;
-}
-
 bool Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
 {
     JNIEnv *env;
diff --git a/src/core/android/SDL_android.h b/src/core/android/SDL_android.h
index e325f7169f544..251a7bbd2e7c5 100644
--- a/src/core/android/SDL_android.h
+++ b/src/core/android/SDL_android.h
@@ -67,7 +67,6 @@ extern bool Android_JNI_ShouldMinimizeOnFocusLoss(void);
 extern bool Android_JNI_GetAccelerometerValues(float values[3]);
 extern void Android_JNI_ShowScreenKeyboard(int input_type, SDL_Rect *inputRect);
 extern void Android_JNI_HideScreenKeyboard(void);
-extern bool Android_JNI_IsScreenKeyboardShown(void);
 extern ANativeWindow *Android_JNI_GetNativeWindow(void);
 
 extern SDL_DisplayOrientation Android_JNI_GetDisplayNaturalOrientation(void);
diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c
index fa1122140a961..adff57e895626 100644
--- a/src/events/SDL_events.c
+++ b/src/events/SDL_events.c
@@ -601,6 +601,10 @@ int SDL_GetEventDescription(const SDL_Event *event, char *buf, int buflen)
         SDL_EVENT_CASE(SDL_EVENT_TEXT_INPUT)
         (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u text='%s')", (uint)event->text.timestamp, (uint)event->text.windowID, event->text.text);
         break;
+        SDL_EVENT_CASE(SDL_EVENT_SCREEN_KEYBOARD_SHOWN)
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_SCREEN_KEYBOARD_HIDDEN)
+        break;
 
 #define PRINT_MOUSEDEV_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%u)", (uint)event->mdevice.timestamp, (uint)event->mdevice.which)
         SDL_EVENT_CASE(SDL_EVENT_MOUSE_ADDED)
diff --git a/src/events/SDL_keyboard.c b/src/events/SDL_keyboard.c
index e7d5e2f9f0a39..1443ad38ed601 100644
--- a/src/events/SDL_keyboard.c
+++ b/src/events/SDL_keyboard.c
@@ -325,7 +325,9 @@ SDL_Window *SDL_GetKeyboardFocus(void)
 
 bool SDL_SetKeyboardFocus(SDL_Window *window)
 {
+#if !defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_ANDROID)
     SDL_VideoDevice *video = SDL_GetVideoDevice();
+#endif
     SDL_Keyboard *keyboard = &SDL_keyboard;
     SDL_Mouse *mouse = SDL_GetMouse();
 
@@ -357,12 +359,14 @@ bool SDL_SetKeyboardFocus(SDL_Window *window)
     if (keyboard->focus && keyboard->focus != window) {
         SDL_SendWindowEvent(keyboard->focus, SDL_EVENT_WINDOW_FOCUS_LOST, 0, 0);
 
+#if !defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_ANDROID)
         // Ensures IME compositions are committed
         if (SDL_TextInputActive(keyboard->focus)) {
             if (video && video->StopTextInput) {
                 video->StopTextInput(video, keyboard->focus);
             }
         }
+#endif // !SDL_PLATFORM_IOS && !SDL_PLATFORM_ANDROID
     }
 
     keyboard->focus = window;
@@ -370,11 +374,13 @@ bool SDL_SetKeyboardFocus(SDL_Window *window)
     if (keyboard->focus) {
         SDL_SendWindowEvent(keyboard->focus, SDL_EVENT_WINDOW_FOCUS_GAINED, 0, 0);
 
+#if !defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_ANDROID)
         if (SDL_TextInputActive(keyboard->focus)) {
             if (video && video->StartTextInput) {
                 video->StartTextInput(video, keyboard->focus, keyboard->focus->text_input_props);
             }
         }
+#endif // !SDL_PLATFORM_IOS && !SDL_PLATFORM_ANDROID
     }
 
     SDL_UpdateRelativeMouseMode();
diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index 433eb72f153c7..27819d4e7c038 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -372,7 +372,6 @@ struct SDL_VideoDevice
     void (*ShowScreenKeyboard)(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
     void (*HideScreenKeyboard)(SDL_VideoDevice *_this, SDL_Window *window);
     void (*SetTextInputProperties)(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
-    bool (*IsScreenKeyboardShown)(SDL_VideoDevice *_this, SDL_Window *window);
 
     // Clipboard
     const char **(*GetTextMimeTypes)(SDL_VideoDevice *_this, size_t *num_mime_types);
@@ -422,6 +421,7 @@ struct SDL_VideoDevice
     bool setting_display_mode;
     Uint32 device_caps;
     SDL_SystemTheme system_theme;
+    bool screen_keyboard_shown;
 
     /* * * */
     // Data used by the GL drivers
@@ -613,4 +613,7 @@ extern SDL_Capitalization SDL_GetTextInputCapitalization(SDL_PropertiesID props)
 extern bool SDL_GetTextInputAutocorrect(SDL_PropertiesID props);
 extern bool SDL_GetTextInputMultiline(SDL_PropertiesID props);
 
+extern void SDL_SendScreenKeyboardShown(void);
+extern void SDL_SendScreenKeyboardHidden(void);
+
 #endif // SDL_sysvideo_h_
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index a016f0800c8ff..b00efd9b84090 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -5823,10 +5823,39 @@ bool SDL_ScreenKeyboardShown(SDL_Window *window)
 {
     CHECK_WINDOW_MAGIC(window, false);
 
-    if (_this->IsScreenKeyboardShown) {
-        return _this->IsScreenKeyboardShown(_this, window);
+    return _this->screen_keyboard_shown;
+}
+
+void SDL_SendScreenKeyboardShown(void)
+{
+    if (_this->screen_keyboard_shown) {
+        return;
+    }
+
+    _this->screen_keyboard_shown = true;
+
+    if (SDL_EventEnabled(SDL_EVENT_SCREEN_KEYBOARD_SHOWN)) {
+        SDL_Event event;
+        event.type = SDL_EVENT_SCREEN_KEYBOARD_SHOWN;
+        event.common.timestamp = 0;
+        SDL_PushEvent(&event);
+    }
+}
+
+void SDL_SendScreenKeyboardHidden(void)
+{
+    if (!_this->screen_keyboard_shown) {
+        return;
+    }
+
+    _this->screen_keyboard_shown = false;
+
+    if (SDL_EventEnabled(SDL_EVENT_SCREEN_KEYBOARD_HIDDEN)) {
+        SDL_Event event;
+        event.type = SDL_EVENT_SCREEN_KEYBOARD_HIDDEN;
+        event.common.timestamp = 0;
+        SDL_PushEvent(&event);
     }
-    return false;
 }
 
 int SDL_GetMessageBoxCount(void)
diff --git a/src/video/android/SDL_androidevents.c b/src/video/android/SDL_androidevents.c
index e32d2722dacb2..a83643faf5c52 100644
--- a/src/video/android/SDL_androidevents.c
+++ b/src/video/android/SDL_androidevents.c
@@ -156,11 +156,6 @@ static void Android_OnResume(void)
     }
 #endif
 
-    // Make sure SW Keyboard is restored when an app becomes foreground
-    if (Android_Window) {
-        Android_RestoreScreenKeyboardOnResume(SDL_GetVideoDevice(), Android_Window);
-    }
-
     SDL_OnApplicationDidEnterForeground();
 }
 
diff --git a/src/video/android/SDL_androidkeyboard.c b/src/video/android/SDL_androidkeyboard.c
index 03af7c6c831d9..baabb92bc9cb9 100644
--- a/src/video/android/SDL_androidkeyboard.c
+++ b/src/video/android/SDL_androidkeyboard.c
@@ -353,8 +353,6 @@ static SDL_Scancode Android_Keycodes[] = {
     SDL_SCANCODE_PASTE,            // AKEYCODE_PASTE
 };
 
-static bool SDL_screen_keyboard_shown;
-
 static SDL_Scancode TranslateKeycode(int keycode)
 {
     SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
@@ -444,25 +442,18 @@ void Android_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_
         }
     }
     Android_JNI_ShowScreenKeyboard(input_type, &window->text_input_rect);
-    SDL_screen_keyboard_shown = true;
 }
 
 void Android_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
 {
     Android_JNI_HideScreenKeyboard();
-    SDL_screen_keyboard_shown = false;
 }
 
-void Android_RestoreScreenKeyboardOnResume(SDL_VideoDevice *_this, SDL_Window *window)
+void Android_RestoreScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
 {
-    if (SDL_screen_keyboard_shown) {
+    if (_this->screen_keyboard_shown) {
         Android_ShowScreenKeyboard(_this, window, window->text_input_props);
     }
 }
 
-bool Android_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window)
-{
-    return Android_JNI_IsScreenKeyboardShown();
-}
-
 #endif // SDL_VIDEO_DRIVER_ANDROID
diff --git a/src/video/android/SDL_androidkeyboard.h b/src/video/android/SDL_androidkeyboard.h
index c192991ac93d6..94bfa873cdcc4 100644
--- a/src/video/android/SDL_androidkeyboard.h
+++ b/src/video/android/SDL_androidkeyboard.h
@@ -28,5 +28,4 @@ extern void Android_OnKeyUp(int keycode);
 extern bool Android_HasScreenKeyboardSupport(SDL_VideoDevice *_this);
 extern void Android_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
 extern void Android_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
-extern void Android_RestoreScreenKeyboardOnResume(SDL_VideoDevice *_this, SDL_Window *window);
-extern bool Android_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window);
+extern void Android_RestoreScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
diff --git a/src/video/android/SDL_androidvideo.c b/src/video/android/SDL_androidvideo.c
index b154ff73086e2..e7bb6642ff136 100644
--- a/src/video/android/SDL_androidvideo.c
+++ b/src/video/android/SDL_androidvideo.c
@@ -142,7 +142,6 @@ static SDL_VideoDevice *Android_CreateDevice(void)
     device->HasScreenKeyboardSupport = Android_HasScreenKeyboardSupport;
     device->ShowScreenKeyboard = Android_ShowScreenKeyboard;
     device->HideScreenKeyboard = Android_HideScreenKeyboard;
-    device->IsScreenKeyboardShown = Android_IsScreenKeyboardShown;
 
     // Clipboard
     device->SetClipboardText = Android_SetClipboardText;
diff --git a/src/video/gdk/SDL_gdktextinput.cpp b/src/video/gdk/SDL_gdktextinput.cpp
index e787871c9cffa..33fd9102f5af5 100644
--- a/src/video/gdk/SDL_gdktextinput.cpp
+++ b/src/video/gdk/SDL_gdktextinput.cpp
@@ -148,6 +148,8 @@ static void CALLBACK GDK_InternalTextEntryCallback(XAsyncBlock *asyncBlock)
     SDL_free(asyncBlock);
     asyncBlock = NULL;
     g_TextBlock = NULL; // once we do this we're fully done with the keyboard
+
+    SDL_SendScreenKeyboardHidden();
 }
 
 void GDK_EnsureHints(void)
@@ -288,13 +290,15 @@ void GDK_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_Prop
     g_TextBlock->queue = g_TextTaskQueue;
     g_TextBlock->context = _this;
     g_TextBlock->callback = GDK_InternalTextEntryCallback;
-    if (FAILED(hR = XGameUiShowTextEntryAsync(
+    if (SUCCEEDED(hR = XGameUiShowTextEntryAsync(
                    g_TextBlock,
                    g_TitleText,
                    g_DescriptionText,
                    g_DefaultText,
                    scope,
                    (uint32_t)g_MaxTextLength))) {
+        SDL_SendScreenKeyboardShown();
+    } else {
         SDL_free(g_TextBlock);
         g_TextBlock = NULL;
         SDL_SetError("XGameUiShowTextEntryAsync failure with HRESULT of %08X", hR);
@@ -309,11 +313,6 @@ void GDK_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
     }
 }
 
-bool GDK_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window)
-{
-    return (g_TextBlock != NULL);
-}
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/video/gdk/SDL_gdktextinput.h b/src/video/gdk/SDL_gdktextinput.h
index c3882ba1e739e..8a9f1090547f6 100644
--- a/src/video/gdk/SDL_gdktextinput.h
+++ b/src/video/gdk/SDL_gdktextinput.h
@@ -40,7 +40,6 @@ bool GDK_ClearComposition(SDL_VideoDevice *_this, SDL_Window *window);
 bool GDK_HasScreenKeyboardSupport(SDL_VideoDevice *_this);
 void GDK_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
 void GDK_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
-bool GDK_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window);
 
 #ifdef __cplusplus
 }
diff --git a/src/video/openvr/SDL_openvrvideo.c b/src/video/openvr/SDL_openvrvideo.c
index d3f471d75efdf..cd9c1bfdb802c 100644
--- a/src/video/openvr/SDL_openvrvideo.c
+++ b/src/video/openvr/SDL_openvrvideo.c
@@ -1272,20 +1272,14 @@ static void OPENVR_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window
     videodata->oOverlay->ShowKeyboardForOverlay(videodata->overlayID,
            input_mode, line_mode,
            EKeyboardFlags_KeyboardFlag_Minimal, "Virtual Keyboard", 128, "", 0);
-    videodata->bKeyboardShown = true;
+    SDL_SendScreenKeyboardShown();
 }
 
 static void OPENVR_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
 {
     SDL_VideoData *videodata = (SDL_VideoData *)_this->internal;
     videodata->oOverlay->HideKeyboard();
-    videodata->bKeyboardShown = false;
-}
-
-static bool OPENVR_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window)
-{
-    SDL_VideoData *videodata = (SDL_VideoData *)_this->internal;
-    return videodata->bKeyboardShown;
+    SDL_SendScreenKeyboardHidden();
 }
 
 static SDL_Cursor *OPENVR_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
@@ -1662,7 +1656,6 @@ static SDL_VideoDevice *OPENVR_CreateDevice(void)
     device->HasScreenKeyboardSupport = OPENVR_HasScreenKeyboardSupport;
     device->ShowScreenKeyboard = OPENVR_ShowScreenKeyboard;
     device->HideScreenKeyboard = OPENVR_HideScreenKeyboard;
-    device->IsScreenKeyboardShown = OPENVR_IsScreenKeyboardShown;
     device->SetWindowIcon = OPENVR_SetWindowIcon;
 
     return device;
diff --git a/src/video/openvr/SDL_openvrvideo.h b/src/video/openvr/SDL_openvrvideo.h
index 6eade3e36c334..73780449f71a8 100644
--- a/src/video/openvr/SDL_openvrvideo.h
+++ b/src/video/openvr/SDL_openvrvideo.h
@@ -76,7 +76,6 @@ struct SDL_VideoData {
 	int input_action_handles_axes_count;
 	VRActionHandle_t input_action_handles_haptics[2];
 
-    bool bKeyboardShown;
     bool bHasShownOverlay;
     int targw, targh;
     int last_targw, last_targh;
diff --git a/src/video/psp/SDL_pspvideo.c b/src/video/psp/SDL_pspvideo.c
index afdb6bbae3b28..66ffa71309712 100644
--- a/src/video/psp/SDL_pspvideo.c
+++ b/src/video/psp/SDL_pspvideo.c
@@ -114,7 +114,6 @@ static SDL_VideoDevice *PSP_Create(void)
     device->HasScreenKeyboardSupport = PSP_HasScreenKeyboardSupport;
     device->ShowScreenKeyboard = PSP_ShowScreenKeyboard;
     device->HideScreenKeyboard = PSP_HideScreenKeyboard;
-    device->IsScreenKeyboardShown = PSP_IsScreenKeyboardShown;
 
     device->PumpEvents = PSP_PumpEvents;
 
@@ -458,6 +457,8 @@ void PSP_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_Prop
 
     sceUtilityOskInitStart(&params);
 
+    SDL_SendScreenKeyboardShown();
+
     while(!done) {
         sceGuStart(GU_DIRECT, list);
         sceGuClearColor(0);
@@ -489,13 +490,12 @@ void PSP_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_Prop
         text_string[i] = outtext[i];
     }
     SDL_SendKeyboardText((const char *) text_string);
+
+    SDL_SendScreenKeyboardHidden();
 }
+
 void PSP_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
 {
 }
-bool PSP_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window)
-{
-    return false;
-}
 
 #endif // SDL_VIDEO_DRIVER_PSP
diff --git a/src/video/psp/SDL_pspvideo.h b/src/video/psp/SDL_pspvideo.h
index 971f44294c997..34258b820d0a1 100644
--- a/src/video/psp/SDL_pspvideo.h
+++ b/src/video/psp/SDL_pspvideo.h
@@ -76,6 +76,5 @@ extern bool PSP_GL_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context)
 extern bool PSP_HasScreenKeyboardSupport(SDL_VideoDevice *_this);
 extern void PSP_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
 extern void PSP_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
-extern bool PSP_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window);
 
 #endif // SDL_pspvideo_h_
diff --git a/src/video/uikit/SDL_uikitvideo.m b/src/video/uikit/SDL_uikitvideo.m
index f5326d83862ca..823f214771c60 100644
--- a/src/video/uikit/SDL_uikitvideo.m
+++ b/src/video/uikit/SDL_uikitvideo.m
@@ -101,7 +101,6 @@ static void UIKit_DeleteDevice(SDL_VideoDevice *device)
         device->StartTextInput = UIKit_StartTextInput;
         device->StopTextInput = UIKit_StopTextInput;
         device->SetTextInputProperties = UIKit_SetTextInputProperties;
-        device->IsScreenKeyboardShown = UIKit_IsScreenKeyboardShown;
         device->UpdateTextInputArea = UIKit_UpdateTextInputArea;
 #endif
 
diff --git a/src/video/uikit/SDL_uikitviewcontroller.h b/src/video/uikit/SDL_uikitviewcontroller.h
index d52e47806aa96..0ed3f04ab742d 100644
--- a/src/video/uikit/SDL_uikitviewcontroller.h
+++ b/src/video/uikit/SDL_uikitviewcontroller.h
@@ -91,6 +91,5 @@ bool UIKit_HasScreenKeyboardSupport(SDL_VideoDevice *_this);
 bool UIKit_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
 bool UIKit_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window);
 void UIKit_SetTextInputProperties(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
-bool UIKit_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window);
 bool UIKit_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window);
 #endif
diff --git a/src/video/uikit/SDL_uikitviewcontroller.m b/src/video/uikit/SDL_uikitviewcontroller.m
index 99a0b67d65992..fadca557491e2 100644
--- a/src/video/uikit/SDL_uikitviewcontroller.m
+++ b/src/video/uikit/SDL_uikitviewcontroller.m
@@ -489,7 +489,11 @@ - (void)setTextFieldProperties:(SDL_PropertiesID) props
  * also shows the onscreen virtual keyboard if no hardware keyboard is attached. */
 - (bool)startTextInput
 {
-    textFieldFocused = YES;
+    if (!textFieldFocused) {
+        textFieldFocused = YES;
+        SDL_SendScreenKeyboardShown();
+    }
+
     if (!textField.window) {
         /* textField has not been added to the view yet,
          * we will try again when that happens. */
@@ -509,7 +513,11 @@ - (bool)startTextInput
  * also hides the onscreen virtual keyboard if no hardware keyboard is attached. */
 - (bool)stopTextInput
 {
-    textFieldFocused = NO;
+    if (textFieldFocused) {
+        textFieldFocused = NO;
+        SDL_SendScreenKeyboardHidden();
+    }
+
     if (!textField.window) {
         /* textField has not been added to the view yet,
          * we will try again when that happens. */
@@ -719,17 +727,6 @@ void UIKit_SetTextInputProperties(SDL_VideoDevice *_this, SDL_Window *window, SD
     }
 }
 
-bool UIKit_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window)
-{
-    @autoreleasepool {
-        SDL_uikitviewcontroller *vc = GetWindowViewController(window);
-        if (vc != nil) {
-            return vc.textFieldFocused;
-        }
-        return false;
-    }
-}
-
 bool UIKit_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window)
 {
     @autoreleasepool {
diff --git a/src/video/vita/SDL_vitavideo.c b/src/video/vita/SDL_vitavideo.c
index 23c4e0bb377b2..4643e1d71a6c3 100644
--- a/src/video/vita/SDL_vitavideo.c
+++ b/src/video/vita/SDL_vitavideo.c
@@ -143,7 +143,6 @@ static SDL_VideoDevice *VITA_Create(void)
     device->HasScreenKeyboardSupport = VITA_HasScreenKeyboardSupport;
     device->ShowScreenKeyboard = VITA_ShowScreenKeyboard;
     device->HideScreenKeyboard = VITA_HideScreenKeyboard;
-    device->IsScreenKeyboardShown = VITA_IsScreenKeyboardShown;
 
     device->PumpEvents = VITA_PumpEvents;
 
@@ -404,10 +403,11 @@ void VITA_ImeEventHandler(void *arg, const SceImeEventData *e)
     case SCE_IME_EVENT_PRESS_CLOSE:
         sceImeClose();
         videodata->ime_active = false;
+        SDL_SendScreenKeyboardHidden();
         break;
     }
 }
-#endif
+#endif // SDL_VIDEO_VITA_PVR
 
 void VITA_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
 {
@@ -506,6 +506,8 @@ void VITA_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_Pro
 #endif
 
     videodata->ime_active = true;
+
+    SDL_SendScreenKeyboardShown();
 }
 
 void VITA_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
@@ -526,17 +528,8 @@ void VITA_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
     }
 
     videodata->ime_active = false;
-#endif
-}
 
-bool VITA_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window)
-{
-#ifdef SDL_VIDEO_VITA_PVR
-    SDL_VideoData *videodata = _this->internal;
-    return videodata->ime_active;
-#else
-    SceCommonDialogStatus dialogStatus = sceImeDialogGetStatus();
-    return dialogStatus == SCE_COMMON_DIALOG_STATUS_RUNNING;
+    SDL_SendScreenKeyboardHidden();
 #endif
 }
 
@@ -580,6 +573,8 @@ void VITA_PumpEvents(SDL_VideoDevice *_this)
             sceImeDialogTerm();
 
             videodata->ime_active = false;
+
+            SDL_SendScreenKeyboardHidden();
         }
     }
 #endif
diff --git a/src/video/vita/SDL_vitavideo.h b/src/video/vita/SDL_vitavideo.h
index c8f7ade08a479..fea213b989cca 100644
--- a/src/video/vita/SDL_vitavideo.h
+++ b/src/video/vita/SDL_vitavideo.h
@@ -114,7 +114,6 @@ extern bool VITA_GLES_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext conte
 extern bool VITA_HasScreenKeyboardSupport(SDL_VideoDevice *_this);
 extern void VITA_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
 extern void VITA_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
-extern bool VITA_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window);
 
 extern void VITA_PumpEvents(SDL_VideoDevice *_this);
 
diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c
index 9cdd143c291cb..e840b0507e6db 100644
--- a/src/video/windows/SDL_windowsvideo.c
+++ b/src/video/windows/SDL_windowsvideo.c
@@ -423,7 +423,6 @@ static SDL_VideoDevice *WIN_CreateDevice(void)
     device->HasScreenKeyboardSupport = GDK_HasScreenKeyboardSupport;
     device->ShowScreenKeyboard = GDK_ShowScreenKeyboard;
     device->HideScreenKeyboard = GDK_HideScreenKeyboard;
-    device->IsScreenKeyboardShown = GDK_IsScreenKeyboardShown;
 #endif
 
     device->free = WIN_DeleteDevice;
diff --git a/src/video/x11/SDL_x11keyboard.c b/src/video/x11/SDL_x11keyboard.c
index 23fe266d0

(Patch may be truncated, please check the link at the top of this post.)