SDL: x11: Add support for the Steam Deck on-screen keyboard

From c4b9f621649d4e2ddb05e7f396e43e2d9e0402cc Mon Sep 17 00:00:00 2001
From: Ethan Lee <[EMAIL REDACTED]>
Date: Sun, 13 Nov 2022 12:45:13 -0500
Subject: [PATCH] x11: Add support for the Steam Deck on-screen keyboard

---
 src/video/x11/SDL_x11keyboard.c | 44 +++++++++++++++++++++++++++++++++
 src/video/x11/SDL_x11keyboard.h |  4 +++
 src/video/x11/SDL_x11video.c    |  9 +++++++
 src/video/x11/SDL_x11video.h    |  4 +++
 4 files changed, 61 insertions(+)

diff --git a/src/video/x11/SDL_x11keyboard.c b/src/video/x11/SDL_x11keyboard.c
index 664c816613f7..f5be37ec5d61 100644
--- a/src/video/x11/SDL_x11keyboard.c
+++ b/src/video/x11/SDL_x11keyboard.c
@@ -22,6 +22,8 @@
 
 #if SDL_VIDEO_DRIVER_X11
 
+#include "SDL_hints.h"
+#include "SDL_misc.h"
 #include "SDL_x11video.h"
 
 #include "../../events/SDL_keyboard_c.h"
@@ -841,6 +843,48 @@ X11_SetTextInputRect(_THIS, const SDL_Rect *rect)
 #endif
 }
 
+SDL_bool
+X11_HasScreenKeyboardSupport(_THIS)
+{
+    SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
+    return videodata->is_steam_deck;
+}
+
+void
+X11_ShowScreenKeyboard(_THIS, SDL_Window *window)
+{
+    SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
+
+    if (videodata->is_steam_deck) {
+        /* For more documentation of the URL parameters, see:
+         * https://partner.steamgames.com/doc/api/ISteamUtils#ShowFloatingGamepadTextInput
+         */
+        char deeplink[128];
+        SDL_snprintf(deeplink, sizeof(deeplink),
+                     "steam://open/keyboard?XPosition=0&YPosition=0&Width=0&Height=0&Mode=%d",
+                     SDL_GetHintBoolean(SDL_HINT_RETURN_KEY_HIDES_IME, SDL_FALSE) ? 0 : 1);
+        SDL_OpenURL(deeplink);
+        videodata->steam_keyboard_open = SDL_TRUE;
+    }
+}
+
+void X11_HideScreenKeyboard(_THIS, SDL_Window *window)
+{
+    SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
+
+    if (videodata->is_steam_deck) {
+        SDL_OpenURL("steam://close/keyboard");
+        videodata->steam_keyboard_open = SDL_FALSE;
+    }
+}
+
+SDL_bool X11_IsScreenKeyboardShown(_THIS, SDL_Window *window)
+{
+    SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
+
+    return videodata->steam_keyboard_open;
+}
+
 #endif /* SDL_VIDEO_DRIVER_X11 */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/x11/SDL_x11keyboard.h b/src/video/x11/SDL_x11keyboard.h
index 4ce41069b7b7..645e7ba415a6 100644
--- a/src/video/x11/SDL_x11keyboard.h
+++ b/src/video/x11/SDL_x11keyboard.h
@@ -29,6 +29,10 @@ extern void X11_QuitKeyboard(_THIS);
 extern void X11_StartTextInput(_THIS);
 extern void X11_StopTextInput(_THIS);
 extern void X11_SetTextInputRect(_THIS, const SDL_Rect *rect);
+extern SDL_bool X11_HasScreenKeyboardSupport(_THIS);
+extern void X11_ShowScreenKeyboard(_THIS, SDL_Window *window);
+extern void X11_HideScreenKeyboard(_THIS, SDL_Window *window);
+extern SDL_bool X11_IsScreenKeyboardShown(_THIS, SDL_Window *window);
 extern KeySym X11_KeyCodeToSym(_THIS, KeyCode, unsigned char group);
 
 #endif /* SDL_x11keyboard_h_ */
diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c
index 2e5e190bd0cb..49f5c5b9bc49 100644
--- a/src/video/x11/SDL_x11video.c
+++ b/src/video/x11/SDL_x11video.c
@@ -212,6 +212,11 @@ X11_CreateDevice(void)
     safety_net_triggered = SDL_FALSE;
     orig_x11_errhandler = X11_XSetErrorHandler(X11_SafetyNetErrHandler);
 
+    /* Steam Deck will have an on-screen keyboard, so check their environment
+     * variable so we can make use of SDL_StartTextInput.
+     */
+    data->is_steam_deck = SDL_GetHintBoolean("SteamDeck", SDL_FALSE);
+
     /* Set the function pointers */
     device->VideoInit = X11_VideoInit;
     device->VideoQuit = X11_VideoQuit;
@@ -307,6 +312,10 @@ X11_CreateDevice(void)
     device->StartTextInput = X11_StartTextInput;
     device->StopTextInput = X11_StopTextInput;
     device->SetTextInputRect = X11_SetTextInputRect;
+    device->HasScreenKeyboardSupport = X11_HasScreenKeyboardSupport;
+    device->ShowScreenKeyboard = X11_ShowScreenKeyboard;
+    device->HideScreenKeyboard = X11_HideScreenKeyboard;
+    device->IsScreenKeyboardShown = X11_IsScreenKeyboardShown;
 
     device->free = X11_DeleteDevice;
 
diff --git a/src/video/x11/SDL_x11video.h b/src/video/x11/SDL_x11video.h
index e2edf248ad53..511ed365c9c3 100644
--- a/src/video/x11/SDL_x11video.h
+++ b/src/video/x11/SDL_x11video.h
@@ -152,6 +152,10 @@ typedef struct SDL_VideoData
     PFN_XGetXCBConnection vulkan_XGetXCBConnection;
 #endif
 
+    /* Used to interact with the on-screen keyboard */
+    SDL_bool is_steam_deck;
+    SDL_bool steam_keyboard_open;
+
 } SDL_VideoData;
 
 extern SDL_bool X11_UseDirectColorVisuals(void);