SDL: windows: vendor GameInput headers

From b048e8e6f83aaa6f32f801e51eed2c3a1bb131fb Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Sat, 25 Jul 2026 22:20:58 +0200
Subject: [PATCH] windows: vendor GameInput headers

---
 .github/workflows/create-test-plan.py         |   15 -
 .github/workflows/generic.yml                 |    7 -
 CMakeLists.txt                                |   67 +-
 VisualC/SDL/SDL.vcxproj                       |   23 +-
 VisualC/SDL/SDL.vcxproj.filters               |    6 +
 build-scripts/build-release.py                |    2 +-
 build-scripts/download-gameinput-sdk.py       |   47 -
 build-scripts/release-info.json               |   12 -
 cmake/sdlcommands.cmake                       |    9 +
 .../build_config/SDL_build_config_windows.h   |    4 -
 src/core/windows/SDL_gameinput.cpp            |   44 +-
 src/core/windows/SDL_gameinput.h              |    2 +-
 src/core/windows/gameinput/gameinput.cpp      |  637 ++++++++
 src/core/windows/gameinput/gameinput.h        | 1332 +++++++++++++++++
 14 files changed, 2021 insertions(+), 186 deletions(-)
 delete mode 100755 build-scripts/download-gameinput-sdk.py
 create mode 100644 src/core/windows/gameinput/gameinput.cpp
 create mode 100644 src/core/windows/gameinput/gameinput.h

diff --git a/.github/workflows/create-test-plan.py b/.github/workflows/create-test-plan.py
index a2f95f8cbca32..a8f133fed31f2 100755
--- a/.github/workflows/create-test-plan.py
+++ b/.github/workflows/create-test-plan.py
@@ -244,8 +244,6 @@ class JobDetails:
     msys2_packages: list[str] = dataclasses.field(default_factory=list)
     cygwin_packages: list[str] = dataclasses.field(default_factory=list)
     werror: bool = True
-    microsoft_gameinput: bool = False
-    microsoft_gameinput_arch: str = ""
     msvc_vcvars_arch: str = ""
     msvc_vcvars_sdk: str = ""
     msvc_project: str = ""
@@ -316,8 +314,6 @@ def to_workflow(self, enable_artifacts: bool) -> dict[str, str|bool]:
             "android-mk": self.android_mk,
             "werror": self.werror,
             "sudo": self.sudo,
-            "microsoft-gameinput": self.microsoft_gameinput,
-            "microsoft-gameinput-arch": self.microsoft_gameinput_arch,
             "msvc-vcvars-arch": self.msvc_vcvars_arch,
             "msvc-vcvars-sdk": self.msvc_vcvars_sdk,
             "msvc-project": self.msvc_project,
@@ -483,14 +479,6 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool, ctest_args
                         job.setup_libusb_arch = "x86"
                     case MsvcArch.X64:
                         job.setup_libusb_arch = "x64"
-                job.microsoft_gameinput = True
-                match spec.msvc_arch:
-                    case MsvcArch.X64:
-                        job.microsoft_gameinput_arch = "x64"
-                    case MsvcArch.Arm64:
-                        job.microsoft_gameinput_arch = "arm64"
-                job.cflags.append("-I$GAMEINPUT_INCLUDE")
-                job.cxxflags.append("-I$GAMEINPUT_INCLUDE")
         case SdlPlatform.Linux:
             if spec.name.startswith("Ubuntu"):
                 assert spec.os.value.startswith("ubuntu-")
@@ -827,9 +815,6 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool, ctest_args
                 job.msys2_packages.append(f"{msys2_env}-clang-tools-extra")
             if job.ccache:
                 job.msys2_packages.append(f"{msys2_env}-ccache")
-            job.microsoft_gameinput = True
-            job.cflags.append("-I$GAMEINPUT_INCLUDE")
-            job.cxxflags.append("-I$GAMEINPUT_INCLUDE")
         case SdlPlatform.Cygwin:
             job.ccache = False # Missing evict-older-than option
             job.clang_tidy = False # error finding files [clang-diagnostic-error] cause might be space in command path
diff --git a/.github/workflows/generic.yml b/.github/workflows/generic.yml
index b8df2d6f44a9e..301e47c06190f 100644
--- a/.github/workflows/generic.yml
+++ b/.github/workflows/generic.yml
@@ -176,13 +176,6 @@ jobs:
               echo '#error "System SDL headers must not be used by build system"' >"$dest"
             done
           done
-      - name: 'Set up Microsoft.GameInput headers'
-        if: ${{ !!matrix.platform.microsoft-gameinput }}
-        run: |
-          python build-scripts/download-gameinput-sdk.py -o $HOME/gameinput
-          echo "GAMEINPUT_INCLUDE=$(cygpath -w "$HOME/gameinput/include")" >>$GITHUB_ENV
-          echo "INCLUDE=$(cygpath -w "$HOME/gameinput/include");$INCLUDE" >>$GITHUB_ENV
-          ${{ (!!matrix.platform.microsoft-gameinput-arch && format('echo "LIB=$(cygpath -w "$HOME/gameinput/lib/{0}");$LIB" >>$GITHUB_ENV', matrix.platform.microsoft-gameinput-arch)) || '' }}
       - name: 'Calculate ccache key'
         if: ${{ matrix.platform.ccache }}
         id: prepare-restore-ccache
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 412afd96edb9e..6ecbae2c4244d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -512,14 +512,6 @@ if(SDL_SHARED)
   SDL_AddCommonCompilerFlags(SDL3-shared)
   target_compile_definitions(SDL3-shared PRIVATE "$<$<CONFIG:Debug>:DEBUG>")
   set_property(TARGET SDL3-shared PROPERTY UNITY_BUILD OFF)
-  if ("c_std_99" IN_LIST CMAKE_C_COMPILE_FEATURES)
-    target_compile_features(SDL3-shared PRIVATE c_std_99)
-  else()
-    # tcc does support the subset of C99 used by SDL
-    if (NOT USE_TCC)
-      message(WARNING "target_compile_features does not know c_std_99 for C compiler")
-    endif()
-  endif()
 endif()
 
 if(SDL_STATIC)
@@ -528,12 +520,13 @@ if(SDL_STATIC)
   SDL_AddCommonCompilerFlags(SDL3-static)
   target_compile_definitions(SDL3-static PRIVATE "$<$<CONFIG:Debug>:DEBUG>")
   set_property(TARGET SDL3-static PROPERTY UNITY_BUILD OFF)
-  if ("c_std_99" IN_LIST CMAKE_C_COMPILE_FEATURES)
-    target_compile_features(SDL3-static PRIVATE c_std_99)
-  else()
-    if (NOT USE_TCC)
-      message(WARNING "target_compile_features does not know c_std_99 for C compiler")
-    endif()
+endif()
+
+if("c_std_99" IN_LIST CMAKE_C_COMPILE_FEATURES)
+  sdl_compile_features(c_std_99)
+else()
+  if(NOT USE_TCC)
+    message(WARNING "target_compile_features does not know c_std_99 for C compiler")
   endif()
 endif()
 
@@ -2353,40 +2346,22 @@ elseif(WINDOWS OR CYGWIN)
     int main(int argc, char **argv) { return 0; }" HAVE_WINDOWS_GAMING_INPUT_H
   )
 
-  check_cxx_source_compiles("
-    #include <gameinput.h>
-    int main(int argc, char **argv) { return 0; }" HAVE_GAMEINPUT_H
-  )
-  if(HAVE_GAMEINPUT_H)
-    set(SDL_GAMEINPUT_DYNAMIC 1)
-    find_library(GAMEINPUT_LIB NAMES "gameinput.lib")
-    if(GAMEINPUT_LIB)
-      cmake_push_check_state()
-      list(APPEND CMAKE_REQUIRED_LIBRARIES "${GAMEINPUT_LIB}")
-      check_cxx_source_compiles("
-        #include <windows.h>
-        #include <gameinput.h>
-        #if defined(GAMEINPUT_API_VERSION) && GAMEINPUT_API_VERSION > 0
-        #define STR_JOIN2(A, B) A##B
-        #define STR_JOIN(A, B) STR_JOIN2(A, B)
-        using namespace GameInput::STR_JOIN(v, GAMEINPUT_API_VERSION);
-        #endif
-        int main(int argc, char **argv) {
-         IGameInput *gameInput;
-         HRESULT hr = GameInputCreate(&gameInput);
-         if (SUCCEEDED(hr)) {
-          gameInput->Release();
-         }
-         (void) argc; (void) argv;
-         return 0;
-        }" HAVE_GAMEINPUT_LIB
-      )
-      cmake_pop_check_state()
-      if(HAVE_GAMEINPUT_LIB)
-        sdl_link_dependency(gameinput LIBS gameinput.lib PKG_CONFIG_LINK_OPTIONS -lgameinput)
-        set(SDL_GAMEINPUT_DYNAMIC 0)
+  set(HAVE_GAMEINPUT_H 1)
+  sdl_include_directories(PRIVATE SYSTEM "${SDL3_SOURCE_DIR}/src/core/windows/gameinput")
+  if("cxx_std_11" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
+    sdl_sources("${SDL3_SOURCE_DIR}/src/core/windows/gameinput/gameinput.cpp")
+    sdl_compile_features(cxx_std_11)
+    if(USE_GCC OR USE_CLANG)
+      # Avoid a dependency on libstdc++-X.dll for `__gxx_personality_seh0`
+      check_c_compiler_flag(-fno-exceptions COMPILER_SUPPORTS_FNO_EXCEPTIONS)
+      if(COMPILER_SUPPORTS_FNO_EXCEPTIONS)
+        set_property(SOURCE "${SDL3_SOURCE_DIR}/src/core/windows/gameinput/gameinput.cpp" APPEND PROPERTY COMPILE_OPTIONS "-fno-exceptions")
+        set_property(SOURCE "${SDL3_SOURCE_DIR}/src/core/windows/gameinput/gameinput.cpp" PROPERTY SKIP_PRECOMPILE_HEADERS 1)
       endif()
     endif()
+    set(SDL_GAMEINPUT_DYNAMIC 0)
+  else()
+    set(SDL_GAMEINPUT_DYNAMIC 1)
   endif()
 
   check_include_file(dxgi1_5.h HAVE_DXGI1_5_H)
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index 10b17d27cbf2c..b90a327497407 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -114,22 +114,22 @@
     <LibraryPath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Lib\x86;$(LibraryPath)</LibraryPath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(IncludePath)</IncludePath>
+    <IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(ProjectDir)/../../src/core/windows/gameinput;$(IncludePath)</IncludePath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(IncludePath)</IncludePath>
+    <IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(ProjectDir)/../../src/core/windows/gameinput;$(IncludePath)</IncludePath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(IncludePath)</IncludePath>
+    <IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(ProjectDir)/../../src/core/windows/gameinput;$(IncludePath)</IncludePath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
-    <IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(IncludePath)</IncludePath>
+    <IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(ProjectDir)/../../src/core/windows/gameinput;$(IncludePath)</IncludePath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-    <IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(IncludePath)</IncludePath>
+    <IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(ProjectDir)/../../src/core/windows/gameinput;$(IncludePath)</IncludePath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
-    <IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(IncludePath)</IncludePath>
+    <IncludePath>$(ProjectDir)/../../src;$(ProjectDir)/../../src/core/windows;$(ProjectDir)/../../src/core/windows/gameinput;$(IncludePath)</IncludePath>
   </PropertyGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <PreBuildEvent>
@@ -435,6 +435,7 @@
     <ClInclude Include="..\..\src\camera\SDL_camera_c.h" />
     <ClInclude Include="..\..\src\camera\SDL_syscamera.h" />
     <ClInclude Include="..\..\src\core\SDL_core_unsupported.h" />
+    <ClInclude Include="..\..\src\core\windows\gameinput\gameinput.h" />
     <ClInclude Include="..\..\src\core\windows\SDL_directx.h" />
     <ClInclude Include="..\..\src\core\windows\SDL_gameinput.h" />
     <ClInclude Include="..\..\src\core\windows\SDL_hid.h" />
@@ -773,13 +774,11 @@
     <ClCompile Include="..\..\src\audio\SDL_wave.c" />
     <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
     <ClCompile Include="..\..\src\core\SDL_core_unsupported.c" />
+    <ClCompile Include="..\..\src\core\windows\gameinput\gameinput.cpp">
+      <PrecompiledHeaderOutputFile>$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
+    </ClCompile>
     <ClCompile Include="..\..\src\core\windows\SDL_gameinput.cpp">
-      <PrecompiledHeaderOutputFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
-      <PrecompiledHeaderOutputFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
-      <PrecompiledHeaderOutputFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
-      <PrecompiledHeaderOutputFile Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
-      <PrecompiledHeaderOutputFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
-      <PrecompiledHeaderOutputFile Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
+      <PrecompiledHeaderOutputFile>$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
     </ClCompile>
     <ClCompile Include="..\..\src\core\windows\SDL_hid.c" />
     <ClCompile Include="..\..\src\core\windows\SDL_immdevice.c" />
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index 2985fa9ab584a..6159f7aa61299 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -543,6 +543,9 @@
     <ClInclude Include="..\..\src\core\windows\SDL_directx.h">
       <Filter>core\windows</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\core\windows\gameinput\gameinput.h">
+      <Filter>core\windows\gameinput</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\src\core\windows\SDL_gameinput.h">
       <Filter>core\windows</Filter>
     </ClInclude>
@@ -1447,6 +1450,9 @@
     <ClCompile Include="..\..\src\core\SDL_core_unsupported.c">
       <Filter>core</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\core\windows\gameinput\gameinput.cpp">
+      <Filter>core\windows\gameinput</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\src\core\windows\SDL_gameinput.cpp">
       <Filter>core\windows</Filter>
     </ClCompile>
diff --git a/build-scripts/build-release.py b/build-scripts/build-release.py
index 2201edb7a241d..faf0dd496cf16 100755
--- a/build-scripts/build-release.py
+++ b/build-scripts/build-release.py
@@ -1200,7 +1200,7 @@ def build_msvc(self):
             shutil.rmtree(deps_path, ignore_errors=True)
             dep_roots = []
             dep_includes = []
-            for dep in self.release_info.get("dependencies"):
+            for dep in self.release_info.get("dependencies", []):
                 if "command" in self.release_info["dependencies"][dep]:
                     dep_includes.append(self.deps_path / dep / "include")
                     continue
diff --git a/build-scripts/download-gameinput-sdk.py b/build-scripts/download-gameinput-sdk.py
deleted file mode 100755
index 8a85d551997c3..0000000000000
--- a/build-scripts/download-gameinput-sdk.py
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python3
-
-import argparse
-import logging
-from pathlib import Path
-import urllib.request
-
-logger = logging.getLogger(__name__)
-
-DEFAULT_GAMEINPUT_VERSION = "v3.3.195.0"
-
-def download_sdk(tag: str, lowercase: bool, output: Path):
-    base_url = f"https://raw.githubusercontent.com/microsoftconnect/GameInput/refs/tags/{tag}/"
-    url_relpaths = (
-        "include/GameInput.h",
-        "include/v0/GameInput.h",
-        "include/v1/GameInput.h",
-        "include/v2/GameInput.h",
-        "lib/arm64/GameInput.lib",
-        "lib/x64/GameInput.lib",
-    )
-    for url_relpath in url_relpaths:
-        url = base_url + url_relpath
-        local_relpath = url_relpath
-        if lowercase:
-            local_relpath = local_relpath.lower()
-        local_path = output / local_relpath
-
-        local_dirpath = local_path.parent
-        local_dirpath.mkdir(parents=True, exist_ok=True)
-
-        logger.info("Downloading %s to %s...", url, local_path)
-        urllib.request.urlretrieve(url, local_path)
-        logger.info("... done")
-
-def main():
-    logging.basicConfig(level=logging.INFO)
-
-    parser = argparse.ArgumentParser(description="Download Microsoft.GameInput SDK", allow_abbrev=False)
-    parser.add_argument("--version", help="GameInput release tag (see https://github.com/microsoftconnect/GameInput/tags)", default=DEFAULT_GAMEINPUT_VERSION)
-    parser.add_argument("--no-lowercase", action="store_false", dest="lowercase", help="Don't lowercase downloaded files")
-    parser.add_argument("-o", "--output", type=Path, default=Path.cwd(), help="SDK will be stored here (in include and lib subdirectories)")
-    args = parser.parse_args()
-    download_sdk(tag=args.version, lowercase=args.lowercase, output=args.output)
-
-if __name__ == "__main__":
-    raise SystemExit(main())
diff --git a/build-scripts/release-info.json b/build-scripts/release-info.json
index e9e30e1d164bb..48477514142aa 100644
--- a/build-scripts/release-info.json
+++ b/build-scripts/release-info.json
@@ -1,12 +1,6 @@
 {
   "name": "SDL3",
   "remote": "libsdl-org/SDL",
-  "dependencies": {
-    "gameinput": {
-      "command": "build-scripts/download-gameinput-sdk.py -o @<@DEPS_PATH@>@",
-      "artifact": "gameinput/include/gameinput.h"
-    }
-  },
   "version": {
     "file": "include/SDL3/SDL_version.h",
     "re_major": "^#define SDL_MAJOR_VERSION\\s+([0-9]+)$",
@@ -60,9 +54,6 @@
         "build-scripts/pkg-support/mingw/cmake/SDL3Config.cmake",
         "build-scripts/pkg-support/mingw/cmake/SDL3ConfigVersion.cmake"
       ]
-    },
-    "dependencies": {
-      "gameinput": {}
     }
   },
   "msvc": {
@@ -138,9 +129,6 @@
       "include/SDL3": [
         "include/SDL3/*.h"
       ]
-    },
-    "dependencies": {
-      "gameinput": {}
     }
   },
   "android": {
diff --git a/cmake/sdlcommands.cmake b/cmake/sdlcommands.cmake
index a90203f47ae14..776ace9e2d2f4 100644
--- a/cmake/sdlcommands.cmake
+++ b/cmake/sdlcommands.cmake
@@ -104,6 +104,15 @@ function(sdl_test_link_dependency )
   sdl_generic_link_dependency(${ARGN} COLLECTOR SDL3_test-collector STATIC_TARGETS SDL3_test)
 endfunction()
 
+function(sdl_compile_features)
+  if(TARGET SDL3-shared)
+    target_compile_features(SDL3-shared PRIVATE ${ARGN})
+  endif()
+  if(TARGET SDL3-static)
+    target_compile_features(SDL3-static PRIVATE ${ARGN})
+  endif()
+endfunction()
+
 macro(_get_ARGS_visibility)
   set(_conflict FALSE)
   set(visibility)
diff --git a/include/build_config/SDL_build_config_windows.h b/include/build_config/SDL_build_config_windows.h
index eb7ff81312e4e..36050400e831c 100644
--- a/include/build_config/SDL_build_config_windows.h
+++ b/include/build_config/SDL_build_config_windows.h
@@ -100,9 +100,7 @@ typedef unsigned int uintptr_t;
 #define HAVE_AUDIOCLIENT_H 1
 #define HAVE_TPCSHRD_H 1
 #define HAVE_SENSORSAPI_H 1
-#if defined(__has_include) && __has_include(<gameinput.h>)
 #define HAVE_GAMEINPUT_H 1
-#endif
 #if (defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64)) && (defined(_MSC_VER) && _MSC_VER >= 1600)
 #elif defined(__has_include) && (defined(__i386__) || defined(__x86_64))
 # if !__has_include(<immintrin.h>)
@@ -225,9 +223,7 @@ typedef unsigned int uintptr_t;
 
 /* Enable various input drivers */
 #define SDL_JOYSTICK_DINPUT 1
-#ifdef HAVE_GAMEINPUT_H
 #define SDL_JOYSTICK_GAMEINPUT 1
-#endif
 #define SDL_JOYSTICK_HIDAPI 1
 #define SDL_JOYSTICK_RAWINPUT 1
 #define SDL_JOYSTICK_VIRTUAL 1
diff --git a/src/core/windows/SDL_gameinput.cpp b/src/core/windows/SDL_gameinput.cpp
index 225407552a59f..1d5113671e751 100644
--- a/src/core/windows/SDL_gameinput.cpp
+++ b/src/core/windows/SDL_gameinput.cpp
@@ -25,12 +25,12 @@
 
 #ifdef HAVE_GAMEINPUT_H
 
-#ifndef SDL_GAMEINPUT_DYNAMIC
-#define USE_GAMEINPUT_LIB
+#define USE_GAMEINPUTCREATE
+#if defined(SDL_PLATFORM_GDK)
 #ifdef _MSC_VER
 #pragma comment(lib, "gameinput.lib")
 #endif
-#endif // !SDL_GAMEINPUT_DYNAMIC
+#endif
 
 static SDL_SharedObject *g_hGameInputDLL;
 static IGameInput *g_pGameInput;
@@ -40,50 +40,12 @@ static int g_nGameInputRefCount;
 bool SDL_InitGameInput(IGameInput **ppGameInput)
 {
     if (g_nGameInputRefCount == 0) {
-#ifdef USE_GAMEINPUT_LIB
         // This is recommended, as Microsoft's GameInputCreate() is robust
         // and better handles various GameInput installations
         HRESULT hr = GameInputCreate(&g_pGameInput);
         if (FAILED(hr)) {
             return WIN_SetErrorFromHRESULT("GameInputCreate failed", hr);
         }
-#elif GAMEINPUT_API_VERSION > 0
-        g_hGameInputDLL = SDL_LoadObject("gameinputredist.dll");
-        if (!g_hGameInputDLL) {
-            return false;
-        }
-
-        typedef HRESULT (WINAPI *pfnGameInputInitialize)(REFIID riid, void **ppvObject);
-        pfnGameInputInitialize pGameInputInitialize = (pfnGameInputInitialize)SDL_LoadFunction(g_hGameInputDLL, "GameInputInitialize");
-        if (!pGameInputInitialize) {
-            SDL_UnloadObject(g_hGameInputDLL);
-            return false;
-        }
-
-        HRESULT hr = pGameInputInitialize(IID_IGameInput, (void **)&g_pGameInput);
-        if (FAILED(hr)) {
-            SDL_UnloadObject(g_hGameInputDLL);
-            return WIN_SetErrorFromHRESULT("GameInputInitialize failed", hr);
-        }
-#else
-        g_hGameInputDLL = SDL_LoadObject("gameinput.dll");
-        if (!g_hGameInputDLL) {
-            return false;
-        }
-
-        typedef HRESULT (WINAPI *pfnGameInputCreate)(IGameInput **gameInput);
-        pfnGameInputCreate pGameInputCreate = (pfnGameInputCreate)SDL_LoadFunction(g_hGameInputDLL, "GameInputCreate");
-        if (!pGameInputCreate) {
-            SDL_UnloadObject(g_hGameInputDLL);
-            return false;
-        }
-
-        HRESULT hr = pGameInputCreate(&g_pGameInput);
-        if (FAILED(hr)) {
-            SDL_UnloadObject(g_hGameInputDLL);
-            return WIN_SetErrorFromHRESULT("GameInputCreate failed", hr);
-        }
-#endif // USE_GAMEINPUT_LIB
     }
     ++g_nGameInputRefCount;
 
diff --git a/src/core/windows/SDL_gameinput.h b/src/core/windows/SDL_gameinput.h
index 2b01e61e73cb2..85cd97b470d70 100644
--- a/src/core/windows/SDL_gameinput.h
+++ b/src/core/windows/SDL_gameinput.h
@@ -30,7 +30,7 @@
 #pragma GCC diagnostic ignored "-Wundef"
 #endif
 
-#include <gameinput.h>
+#include "gameinput.h"
 
 #ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
 #pragma GCC diagnostic pop
diff --git a/src/core/windows/gameinput/gameinput.cpp b/src/core/windows/gameinput/gameinput.cpp
new file mode 100644
index 0000000000000..ba62658870a1d
--- /dev/null
+++ b/src/core/windows/gameinput/gameinput.cpp
@@ -0,0 +1,637 @@
+// Copyright (c) Microsoft Corporation.
+//
+// This file is licensed under the MIT License.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+// Start SDL modifications
+#include "SDL_internal.h"
+
+#ifdef HAVE_GAMEINPUT_H
+
+#ifdef _MBCS
+#undef _MBCS
+#endif
+#define _UNICODE
+#define UNICODE
+// End SDL modifications
+
+#include <windows.h>
+#include <unknwn.h>
+#include <stdint.h>
+
+// Start SDL modifications
+#ifndef RRF_SUBKEY_WOW6432KEY
+#define RRF_SUBKEY_WOW6432KEY 0x00020000
+#endif
+
+#define memcpy_s(DST, DST_SIZE, SRC, SRC_SIZE) SDL_memcpy(DST, SRC, SDL_min(DST_SIZE, SRC_SIZE))
+#define wcscpy_s(DST, DST_SIZE, SRC) SDL_wcslcpy(DST, SRC, DST_SIZE)
+#define wcscat_s(DST, DST_SIZE, SRC) SDL_wcslcat(DST, SRC, DST_SIZE)
+
+// Additional changes:
+// - Removed unused `static uint32_t g_productType = PRODUCT_UNDEFINED;`
+// - Removed unused `static HMODULE g_ntdllDll = nullptr;`
+// End SDL modifications
+
+namespace GameInput {
+
+
+//
+//  Constants
+//
+
+static const IID IID_IGameInput_v0 = {0x11be2a7e, 0x4254, 0x445a, {0x9c, 0x09, 0xff, 0xc4, 0x0f, 0x00, 0x69, 0x18}};
+
+
+//
+//  Globals
+//
+
+static HMODULE g_gameInputDll = nullptr;
+static HMODULE g_advapi32Dll = nullptr;
+static HMODULE g_versionDll = nullptr;
+
+
+//
+//  Macros
+//
+
+#define RETURN_HR_IF(hr, condition) { bool _condition = (condition); if(_condition) { return (hr); } }
+#define RETURN_HR_IF_NULL(hr, ptr) RETURN_HR_IF(hr, ((ptr) == nullptr))
+#define RETURN_IF_FAILED(x) { HRESULT _hr = (x); if (FAILED(_hr)) { return _hr; } }
+#define RETURN_IF_NULL_ALLOC(ptr) RETURN_HR_IF_NULL(E_OUTOFMEMORY, ptr)
+
+#define RETURN_WIN32(error) { return HRESULT_FROM_WIN32(error); }
+#define RETURN_IF_WIN32_ERROR(error) { if (error != ERROR_SUCCESS) { RETURN_WIN32(error); } }
+#define RETURN_IF_WIN32_BOOL_FALSE(condition) { if (condition == FALSE) { RETURN_WIN32(GetLastError()); } }
+#define RETURN_LAST_ERROR_IF(condition) { bool _condition = (condition); if(_condition) { RETURN_WIN32(GetLastError()); } }
+#define RETURN_LAST_ERROR_IF_NULL(ptr) RETURN_LAST_ERROR_IF((ptr) == NULL)
+
+#define RETURN_NTSTATUS(status) { NTSTATUS _value = (status); return NT_SUCCESS(_value) ? S_OK : (HRESULT)((_value) | FACILITY_NT_BIT); }
+#define RETURN_IF_NTSTATUS_FAILED(status) { NTSTATUS _status = (status); if (!NT_SUCCESS(_status)) { RETURN_NTSTATUS(_status); } }
+
+#define FAIL_FAST_HR(hr) { __fastfail(hr); }
+#define FAIL_FAST() FAIL_FAST_HR(E_UNEXPECTED)
+#define FAIL_FAST_IF(condition) { if (condition) { FAIL_FAST_HR(E_UNEXPECTED); } }
+#define FAIL_FAST_IF_FAILED(x) { HRESULT _hr = (x); if (FAILED(_hr)) { FAIL_FAST_HR(_hr); } }
+#define FAIL_FAST_IF_WIN32_BOOL_FALSE(condition) { if (condition == FALSE) { FAIL_FAST_HR(HRESULT_FROM_WIN32(GetLastError())); } }
+
+
+//
+//  Containers
+//
+
+template <typename T>
+class Vector
+{
+public:
+    ~Vector()
+    {
+        if (m_data != nullptr)
+        {
+            LocalFree(m_data);
+        }
+    }
+
+    HRESULT Resize(
+        size_t count) noexcept
+    {
+        if (count > m_capacity)
+        {
+            const size_t capacity = (m_capacity + count) * 2;
+
+            T* const data = static_cast<T*>(LocalAlloc(LPTR, capacity * sizeof(T)));
+            RETURN_IF_NULL_ALLOC(data);
+
+            if (m_data != nullptr)
+            {
+                memcpy_s(data, count * sizeof(T), m_data, m_count * sizeof(T));
+                LocalFree(m_data);
+            }
+
+            m_data = data;
+            m_capacity = capacity;
+        }
+
+        m_count = count;
+
+        return S_OK;
+    }
+
+    size_t GetCount() const noexcept
+    {
+        return m_count;
+    }
+
+    T& operator[](
+        size_t index) noexcept
+    {
+        return m_data[index];
+    }
+
+    const T& operator[](
+        size_t index) const noexcept
+    {
+        return m_data[index];
+    }
+
+    operator const T*() const noexcept
+    {
+        return m_data;
+    }
+
+    operator T*() noexcept
+    {
+        return m_data;
+    }
+
+private:
+    T*     m_data = nullptr;
+    size_t m_count = 0;
+    size_t m_capacity = 0;
+};
+
+template <typename T>
+class String
+{
+public:
+    HRESULT Assign(
+        _In_z_ const wchar_t* string)
+    {
+        RETURN_IF_FAILED(Resize(wcslen(string)));
+        wcscpy_s(m_string, m_string.GetCount(), string);
+
+        return S_OK;
+    }
+
+    HRESULT Append(
+        _In_z_ const T* part) noexcept
+    {
+        const size_t stringLength = GetLength();
+        const size_t partLength = wcslen(part);
+        const size_t totalLength = stringLength + partLength;
+
+        if (totalLength >= m_string.GetCount())
+        {
+            RETURN_IF_FAILED(Resize(totalLength));
+        }
+
+        wcscat_s(m_string, m_string.GetCount(), part);
+
+        return S_OK;
+    }
+
+    HRESULT Resize(
+        size_t length) noexcept
+    {
+        if (length >= m_string.GetCount())
+        {
+            RETURN_IF_FAILED(m_string.Resize(length + 1));
+        }
+
+        if (m_string.GetCount() > 0)
+        {
+            m_string[m_string.GetCount() - 1] = 0;
+        }
+
+        return S_OK;
+    }
+
+    size_t GetLength() const noexcept
+    {
+        const size_t count = m_string.GetCount();
+        return count == 0 ? count : count - 1;
+    }
+
+    operator const T*() const noexcept
+    {
+        return m_string;
+    }
+
+    operator T*() noexcept
+    {
+        return m_string;
+    }
+
+    const T& GetFront() const noexcept
+    {
+        return m_string[0];
+    }
+
+    const T& GetBack() const noexcept
+    {
+        return m_string[m_string.GetCount() - 1];
+    }
+
+private:
+    Vector<T> m_string;
+};
+
+using WString = String<wchar_t>;
+using AString = String<char>;
+
+
+//
+//  Helpers
+//
+
+template <typename T>
+static HRESULT GetModuleProc(
+    _In_ HMODULE module,
+    _In_z_ const char* name,
+    _Out_ T* proc) noexcept
+{
+    *proc = reinterpret_cast<T>(reinterpret_cast<uintptr_t>(GetProcAddress(module, name)));
+    RETURN_LAST_ERROR_IF_NULL(*proc);
+
+    return S_OK;
+}
+
+template <typename T>
+static HRESULT LoadSystemModuleProc(
+    _In_ HMODULE* module,
+    _In_z_ const wchar_t* path,
+    _In_z_ const char* name,
+    _Out_ T* proc) noexcept
+{
+    *proc = nullptr;
+
+    if (*module == nullptr)
+    {
+        *module = LoadLibraryEx(path, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
+        RETURN_LAST_ERROR_IF_NULL(*module);
+    }
+
+    RETURN_IF_FAILED(GetModuleProc(*module, name, proc));
+
+    return S_OK;
+}
+
+static HRESULT GetSystemDirectory(
+    _Inout_ WString* systemDir) noexcept
+{
+    uint32_t length = ::GetSystemDirectory(nullptr, 0);
+    RETURN_LAST_ERROR_IF(length == 0);
+    RETURN_IF_FAILED(systemDir->Resize(length));
+
+    length = ::GetSystemDirectory(*systemDir, length) + 1;
+    RETURN_LAST_ERROR_IF(length == 0);
+    RETURN_IF_FAILED(systemDir->Resize(length));
+
+    return S_OK;
+}
+
+static HRESULT GetApplicationDirectory(
+    _Inout_ WString* appDir) noexcept
+{
+    // GetModuleFileName does not report the required buffer size; grow until the
+    // full module path fits, capping retries to avoid unbounded allocation.
+
+    DWORD capacity = MAX_PATH;
+    DWORD length = 0;
+
+    for (;;)
+    {
+        RETURN_IF_FAILED(appDir->Resize(capacity));
+
+        SetLastError(ERROR_SUCCESS);
+        length = ::GetModuleFileName(nullptr, *appDir, capacity + 1);
+        RETURN_LAST_ERROR_IF(length == 0);
+
+        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+        {
+            break;
+        }
+
+        if (capacity >= 0x8000)
+        {
+            return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+        }
+
+        capacity *= 2;
+    }
+
+    // Trim the executable filename, leaving only the directory path.
+
+    wchar_t* const path = *appDir;
+    while (length > 0 && p

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