SDL: Updated to GameInput v1.1

From 8a57c83ff9b44d5529146bb4408c2d4d8ab062b0 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 11 Apr 2025 11:58:12 -0700
Subject: [PATCH] Updated to GameInput v1.1

The biggest change is that the C API is no longer available.

Here are more details:
https://www.nuget.org/packages/Microsoft.GameInput

Fixes https://github.com/libsdl-org/SDL/issues/12802
---
 .github/workflows/create-test-plan.py         |   2 +
 CMakeLists.txt                                |   5 +-
 VisualC-GDK/SDL/SDL.vcxproj                   |   6 +-
 VisualC-GDK/SDL/SDL.vcxproj.filters           |   6 +-
 VisualC/SDL/SDL.vcxproj                       |  31 ++++-
 VisualC/SDL/SDL.vcxproj.filters               |  20 ++--
 .../{SDL_gameinput.c => SDL_gameinput.cpp}    |  21 ++--
 src/core/windows/SDL_gameinput.h              |   9 +-
 ...utjoystick.c => SDL_gameinputjoystick.cpp} | 111 +++++++++++-------
 ...wsgameinput.c => SDL_windowsgameinput.cpp} | 111 +++++++++---------
 src/video/windows/SDL_windowsgameinput.h      |   9 ++
 src/video/windows/SDL_windowsvideo.h          |   2 +
 12 files changed, 201 insertions(+), 132 deletions(-)
 rename src/core/windows/{SDL_gameinput.c => SDL_gameinput.cpp} (85%)
 rename src/joystick/gdk/{SDL_gameinputjoystick.c => SDL_gameinputjoystick.cpp} (89%)
 rename src/video/windows/{SDL_windowsgameinput.c => SDL_windowsgameinput.cpp} (81%)

diff --git a/.github/workflows/create-test-plan.py b/.github/workflows/create-test-plan.py
index a137889c81a7d..8048e2bc3229f 100755
--- a/.github/workflows/create-test-plan.py
+++ b/.github/workflows/create-test-plan.py
@@ -381,9 +381,11 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool) -> JobDeta
                 match spec.msvc_arch:
                     case MsvcArch.X86:
                         job.cflags.append("/clang:-m32")
+                        job.cxxflags.append("/clang:-m32")
                         job.ldflags.append("/MACHINE:X86")
                     case MsvcArch.X64:
                         job.cflags.append("/clang:-m64")
+                        job.cxxflags.append("/clang:-m64")
                         job.ldflags.append("/MACHINE:X64")
                     case _:
                         raise ValueError(f"Unsupported clang-cl architecture (arch={spec.msvc_arch})")
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 364cb5c0fd359..0db59b3eec21c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1893,11 +1893,13 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
     set (USE_POSIX_SPAWN 1)
   endif()
 elseif(WINDOWS)
+  enable_language(CXX)
   check_c_source_compiles("
     #include <windows.h>
     int main(int argc, char **argv) { return 0; }" HAVE_WIN32_CC)
 
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/core/windows/*.c")
+  sdl_glob_sources("${SDL3_SOURCE_DIR}/src/core/windows/*.cpp")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/main/windows/*.c")
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/io/windows/*.c")
 
@@ -2008,6 +2010,7 @@ elseif(WINDOWS)
   if(SDL_VIDEO)
     set(SDL_VIDEO_DRIVER_WINDOWS 1)
     sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/windows/*.c")
+    sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/windows/*.cpp")
 
     CheckOpenVR()
 
@@ -2135,7 +2138,7 @@ elseif(WINDOWS)
       set(SDL_JOYSTICK_WGI 1)
     endif()
     if(HAVE_GAMEINPUT_H)
-      sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/gdk/*.c")
+      sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/gdk/*.cpp")
       set(SDL_JOYSTICK_GAMEINPUT 1)
     endif()
     set(HAVE_SDL_JOYSTICK TRUE)
diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index d15619c802ee2..baa2fffe2c2af 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -645,7 +645,7 @@
     <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\SDL_gameinput.c"/>
+    <ClCompile Include="..\..\src\core\windows\SDL_gameinput.cpp"/>
     <ClCompile Include="..\..\src\core\windows\SDL_hid.c" />
     <ClCompile Include="..\..\src\core\windows\SDL_immdevice.c" />
     <ClCompile Include="..\..\src\core\windows\SDL_windows.c" />
@@ -710,7 +710,7 @@
     <ClCompile Include="..\..\src\hidapi\SDL_hidapi.c" />
     <ClCompile Include="..\..\src\joystick\controller_type.c" />
     <ClCompile Include="..\..\src\joystick\dummy\SDL_sysjoystick.c" />
-    <ClCompile Include="..\..\src\joystick\gdk\SDL_gameinputjoystick.c" />
+    <ClCompile Include="..\..\src\joystick\gdk\SDL_gameinputjoystick.cpp" />
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapijoystick.c" />
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_8bitdo.c" />
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_combined.c" />
@@ -891,7 +891,7 @@
     <ClCompile Include="..\..\src\video\windows\SDL_windowsevents.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsframebuffer.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowskeyboard.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowsgameinput.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsgameinput.cpp" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsmessagebox.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsmodes.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsmouse.c" />
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index 03b5a6a8f817d..26f228826c966 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/VisualC-GDK/SDL/SDL.vcxproj.filters
@@ -27,7 +27,7 @@
     <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\SDL_gameinput.c" />
+    <ClCompile Include="..\..\src\core\windows\SDL_gameinput.cpp" />
     <ClCompile Include="..\..\src\core\windows\SDL_hid.c" />
     <ClCompile Include="..\..\src\core\windows\SDL_immdevice.c" />
     <ClCompile Include="..\..\src\core\windows\SDL_windows.c" />
@@ -61,7 +61,7 @@
     <ClCompile Include="..\..\src\hidapi\SDL_hidapi.c" />
     <ClCompile Include="..\..\src\joystick\controller_type.c" />
     <ClCompile Include="..\..\src\joystick\dummy\SDL_sysjoystick.c" />
-    <ClCompile Include="..\..\src\joystick\gdk\SDL_gameinputjoystick.c" />
+    <ClCompile Include="..\..\src\joystick\gdk\SDL_gameinputjoystick.cpp" />
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapijoystick.c" />
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_8bitdo.c" />
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_combined.c" />
@@ -193,7 +193,7 @@
     <ClCompile Include="..\..\src\video\windows\SDL_windowsevents.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsframebuffer.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowskeyboard.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowsgameinput.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsgameinput.cpp" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsmessagebox.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsmodes.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsmouse.c" />
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index 738a4110fbc8f..c0a4d8643665d 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -424,6 +424,16 @@
     <ClCompile Include="..\..\src\camera\dummy\SDL_camera_dummy.c" />
     <ClCompile Include="..\..\src\camera\mediafoundation\SDL_camera_mediafoundation.c" />
     <ClCompile Include="..\..\src\camera\SDL_camera.c" />
+    <ClCompile Include="..\..\src\core\windows\pch_cpp.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+      <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)'=='Release|x64'">$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
+    </ClCompile>
     <ClCompile Include="..\..\src\dialog\SDL_dialog.c" />
     <ClCompile Include="..\..\src\dialog\SDL_dialog_utils.c" />
     <ClCompile Include="..\..\src\filesystem\SDL_filesystem.c" />
@@ -543,7 +553,12 @@
     <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\SDL_gameinput.c" />
+    <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)'=='Release|x64'">$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
+    </ClCompile>
     <ClCompile Include="..\..\src\core\windows\SDL_hid.c" />
     <ClCompile Include="..\..\src\core\windows\SDL_immdevice.c" />
     <ClCompile Include="..\..\src\core\windows\SDL_windows.c" />
@@ -580,7 +595,12 @@
     <ClCompile Include="..\..\src\hidapi\SDL_hidapi.c" />
     <ClCompile Include="..\..\src\joystick\controller_type.c" />
     <ClCompile Include="..\..\src\joystick\dummy\SDL_sysjoystick.c" />
-    <ClCompile Include="..\..\src\joystick\gdk\SDL_gameinputjoystick.c" />
+    <ClCompile Include="..\..\src\joystick\gdk\SDL_gameinputjoystick.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)'=='Release|x64'">$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
+    </ClCompile>
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapijoystick.c" />
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_8bitdo.c" />
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_combined.c" />
@@ -726,7 +746,12 @@
     <ClCompile Include="..\..\src\video\windows\SDL_windowsevents.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsframebuffer.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowskeyboard.c" />
-    <ClCompile Include="..\..\src\video\windows\SDL_windowsgameinput.c" />
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsgameinput.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)'=='Release|x64'">$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
+    </ClCompile>
     <ClCompile Include="..\..\src\video\windows\SDL_windowsmessagebox.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsmodes.c" />
     <ClCompile Include="..\..\src\video\windows\SDL_windowsmouse.c" />
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index a5b201ed6d5e2..2583c9f3791ea 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -537,9 +537,6 @@
     <ClInclude Include="..\..\src\events\SDL_events_c.h">
       <Filter>events</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\src\events\SDL_eventfilter_c.h">
-      <Filter>events</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\src\events\SDL_keyboard_c.h">
       <Filter>events</Filter>
     </ClInclude>
@@ -962,6 +959,7 @@
     <ClInclude Include="..\..\include\SDL3\SDL_storage.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_time.h" />
     <ClInclude Include="..\..\src\events\SDL_categories_c.h" />
+    <ClInclude Include="..\..\src\events\SDL_eventwatch_c.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
@@ -1049,7 +1047,7 @@
     <ClCompile Include="..\..\src\core\SDL_core_unsupported.c">
       <Filter>core</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\src\core\windows\SDL_gameinput.c">
+    <ClCompile Include="..\..\src\core\windows\SDL_gameinput.cpp">
       <Filter>core\windows</Filter>
     </ClCompile>
     <ClCompile Include="..\..\src\core\windows\SDL_hid.c">
@@ -1088,9 +1086,6 @@
     <ClCompile Include="..\..\src\events\SDL_events.c">
       <Filter>events</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\src\events\SDL_eventfilter.c">
-      <Filter>events</Filter>
-    </ClCompile>
     <ClCompile Include="..\..\src\events\SDL_keyboard.c">
       <Filter>events</Filter>
     </ClCompile>
@@ -1184,7 +1179,7 @@
     <ClCompile Include="..\..\src\joystick\dummy\SDL_sysjoystick.c">
       <Filter>joystick\dummy</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\src\joystick\gdk\SDL_gameinputjoystick.c">
+    <ClCompile Include="..\..\src\joystick\gdk\SDL_gameinputjoystick.cpp">
       <Filter>joystick\gdk</Filter>
     </ClCompile>
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_8bitdo.c">
@@ -1367,7 +1362,7 @@
     <ClCompile Include="..\..\src\video\windows\SDL_windowskeyboard.c">
       <Filter>video\windows</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\src\video\windows\SDL_windowsgameinput.c">
+    <ClCompile Include="..\..\src\video\windows\SDL_windowsgameinput.cpp">
       <Filter>video\windows</Filter>
     </ClCompile>
     <ClCompile Include="..\..\src\video\windows\SDL_windowsmessagebox.c">
@@ -1606,11 +1601,12 @@
     <ClCompile Include="..\..\src\storage\generic\SDL_genericstorage.c" />
     <ClCompile Include="..\..\src\storage\steam\SDL_steamstorage.c" />
     <ClCompile Include="..\..\src\storage\SDL_storage.c" />
+    <ClCompile Include="..\..\src\events\SDL_eventwatch.c" />
+    <ClCompile Include="..\..\src\core\windows\pch_cpp.cpp">
+      <Filter>core\windows</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\..\src\core\windows\version.rc" />
   </ItemGroup>
-  <ItemGroup>
-    <MASM Include="..\..\src\stdlib\SDL_mslibc_x64.masm" />
-  </ItemGroup>
 </Project>
diff --git a/src/core/windows/SDL_gameinput.c b/src/core/windows/SDL_gameinput.cpp
similarity index 85%
rename from src/core/windows/SDL_gameinput.c
rename to src/core/windows/SDL_gameinput.cpp
index 9ac5912db9d06..e2ea3fb4ab285 100644
--- a/src/core/windows/SDL_gameinput.c
+++ b/src/core/windows/SDL_gameinput.cpp
@@ -25,16 +25,11 @@
 #include "SDL_windows.h"
 #include "SDL_gameinput.h"
 
-#ifdef SDL_PLATFORM_WIN32
-#include <initguid.h>
-// {11BE2A7E-4254-445A-9C09-FFC40F006918}
-DEFINE_GUID(SDL_IID_GameInput, 0x11BE2A7E, 0x4254, 0x445A, 0x9C, 0x09, 0xFF, 0xC4, 0x0F, 0x00, 0x69, 0x18);
-#endif
-
 static SDL_SharedObject *g_hGameInputDLL;
 static IGameInput *g_pGameInput;
 static int g_nGameInputRefCount;
 
+
 bool SDL_InitGameInput(IGameInput **ppGameInput)
 {
     if (g_nGameInputRefCount == 0) {
@@ -43,7 +38,7 @@ bool SDL_InitGameInput(IGameInput **ppGameInput)
             return false;
         }
 
-        typedef HRESULT (WINAPI *GameInputCreate_t)(IGameInput * *gameInput);
+        typedef HRESULT (WINAPI *GameInputCreate_t)(IGameInput **gameInput);
         GameInputCreate_t GameInputCreateFunc = (GameInputCreate_t)SDL_LoadFunction(g_hGameInputDLL, "GameInputCreate");
         if (!GameInputCreateFunc) {
             SDL_UnloadObject(g_hGameInputDLL);
@@ -58,15 +53,19 @@ bool SDL_InitGameInput(IGameInput **ppGameInput)
         }
 
 #ifdef SDL_PLATFORM_WIN32
-        hr = IGameInput_QueryInterface(pGameInput, &SDL_IID_GameInput, (void **)&g_pGameInput);
-        IGameInput_Release(pGameInput);
+#if GAMEINPUT_API_VERSION >= 1
+        hr = pGameInput->QueryInterface(IID_IGameInput, (void **)&g_pGameInput);
+#else
+        // We require GameInput v1.1 or newer
+        hr = E_NOINTERFACE;
+#endif
+        pGameInput->Release();
         if (FAILED(hr)) {
             SDL_UnloadObject(g_hGameInputDLL);
             return WIN_SetErrorFromHRESULT("GameInput QueryInterface failed", hr);
         }
 #else
         // Assume that the version we get is compatible with the current SDK
-        // If that isn't the case, define the correct GUID for SDL_IID_GameInput above
         g_pGameInput = pGameInput;
 #endif
     }
@@ -85,7 +84,7 @@ void SDL_QuitGameInput(void)
     --g_nGameInputRefCount;
     if (g_nGameInputRefCount == 0) {
         if (g_pGameInput) {
-            IGameInput_Release(g_pGameInput);
+            g_pGameInput->Release();
             g_pGameInput = NULL;
         }
         if (g_hGameInputDLL) {
diff --git a/src/core/windows/SDL_gameinput.h b/src/core/windows/SDL_gameinput.h
index 0022c0bdde163..4d2beb5647005 100644
--- a/src/core/windows/SDL_gameinput.h
+++ b/src/core/windows/SDL_gameinput.h
@@ -25,9 +25,16 @@
 
 #ifdef HAVE_GAMEINPUT_H
 
-#define COBJMACROS
 #include <gameinput.h>
 
+#ifndef GAMEINPUT_API_VERSION
+#define GAMEINPUT_API_VERSION 0
+#endif
+
+#if GAMEINPUT_API_VERSION == 1
+using namespace GameInput::v1;
+#endif
+
 extern bool SDL_InitGameInput(IGameInput **ppGameInput);
 extern void SDL_QuitGameInput(void);
 
diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.cpp
similarity index 89%
rename from src/joystick/gdk/SDL_gameinputjoystick.c
rename to src/joystick/gdk/SDL_gameinputjoystick.cpp
index 6cf0a902b7f17..46d4ecc5f117a 100644
--- a/src/joystick/gdk/SDL_gameinputjoystick.c
+++ b/src/joystick/gdk/SDL_gameinputjoystick.cpp
@@ -24,6 +24,7 @@
 
 #include "../SDL_sysjoystick.h"
 #include "../usb_ids.h"
+#include "../../core/windows/SDL_windows.h"
 #include "../../core/windows/SDL_gameinput.h"
 
 // Default value for SDL_HINT_JOYSTICK_GAMEINPUT
@@ -66,7 +67,7 @@ typedef struct joystick_hwdata
 
 static GAMEINPUT_InternalList g_GameInputList = { NULL };
 static IGameInput *g_pGameInput = NULL;
-static GameInputCallbackToken g_GameInputCallbackToken = GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE;
+static GameInputCallbackToken g_GameInputCallbackToken = 0;
 static Uint64 g_GameInputTimestampOffset;
 
 static bool GAMEINPUT_InternalIsGamepad(const GameInputDeviceInfo *info)
@@ -93,15 +94,22 @@ static bool GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice)
 
     SDL_AssertJoysticksLocked();
 
-    info = IGameInputDevice_GetDeviceInfo(pDevice);
-    if (info->capabilities & GameInputDeviceCapabilityWireless) {
+#if GAMEINPUT_API_VERSION >= 1
+    HRESULT hr = pDevice->GetDeviceInfo(&info);
+    if (FAILED(hr)) {
+        return WIN_SetErrorFromHRESULT("IGameInputDevice::GetDeviceInfo", hr);
+    }
+#else
+    info = pDevice->GetDeviceInfo();
+#endif
+    if (false /*info->capabilities & GameInputDeviceCapabilityWireless*/) {
         bus = SDL_HARDWARE_BUS_BLUETOOTH;
     } else {
         bus = SDL_HARDWARE_BUS_USB;
     }
     vendor = info->vendorId;
     product = info->productId;
-    version = (info->firmwareVersion.major << 8) | info->firmwareVersion.minor;
+    //version = (info->firmwareVersion.major << 8) | info->firmwareVersion.minor;
 
     if (SDL_JoystickHandledByAnotherDriver(&SDL_GAMEINPUT_JoystickDriver, vendor, product, version, "")) {
         return true;
@@ -130,18 +138,20 @@ static bool GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice)
     // Generate a device path
     for (idx = 0; idx < APP_LOCAL_DEVICE_ID_SIZE; ++idx) {
         SDL_snprintf(tmp, SDL_arraysize(tmp), "%02hhX", info->deviceId.value[idx]);
-        SDL_strlcat(elem->path, tmp, SDL_arraysize(tmp));
+        SDL_strlcat(elem->path, tmp, SDL_arraysize(elem->path));
     }
 
-    if (info->deviceStrings) {
-        // In theory we could get the manufacturer and product strings here, but they're NULL for all the controllers I've tested
+#if GAMEINPUT_API_VERSION >= 1
+    if (info->displayName) {
+        product_string = info->displayName;
     }
-
+#else
     if (info->displayName) {
-        // This could give us a product string, but it's NULL for all the controllers I've tested
+        product_string = info->displayName->data;
     }
+#endif
 
-    IGameInputDevice_AddRef(pDevice);
+    pDevice->AddRef();
     elem->device = pDevice;
     elem->name = SDL_CreateJoystickName(vendor, product, manufacturer_string, product_string);
     elem->guid = SDL_CreateJoystickGUID(bus, vendor, product, version, manufacturer_string, product_string, 'g', 0);
@@ -168,7 +178,7 @@ static bool GAMEINPUT_InternalRemoveByIndex(int idx)
 
     elem = g_GameInputList.devices[idx];
     if (elem) {
-        IGameInputDevice_Release(elem->device);
+        elem->device->Release();
         SDL_free(elem->name);
         SDL_free(elem);
     }
@@ -232,10 +242,11 @@ static void CALLBACK GAMEINPUT_InternalJoystickDeviceCallback(
 }
 
 static void GAMEINPUT_JoystickDetect(void);
+static void GAMEINPUT_JoystickQuit(void);
 
 static bool GAMEINPUT_JoystickInit(void)
 {
-    HRESULT hR;
+    HRESULT hr;
 
     if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_GAMEINPUT, SDL_GAMEINPUT_DEFAULT)) {
         return true;
@@ -245,21 +256,21 @@ static bool GAMEINPUT_JoystickInit(void)
         return false;
     }
 
-    hR = IGameInput_RegisterDeviceCallback(g_pGameInput,
-                                           NULL,
+    hr = g_pGameInput->RegisterDeviceCallback(NULL,
                                            GameInputKindController,
                                            GameInputDeviceConnected,
                                            GameInputBlockingEnumeration,
                                            NULL,
                                            GAMEINPUT_InternalJoystickDeviceCallback,
                                            &g_GameInputCallbackToken);
-    if (FAILED(hR)) {
-        return SDL_SetError("IGameInput::RegisterDeviceCallback failure with HRESULT of %08lX", hR);
+    if (FAILED(hr)) {
+        GAMEINPUT_JoystickQuit();
+        return WIN_SetErrorFromHRESULT("IGameInput::RegisterDeviceCallback", hr);
     }
 
     // Calculate the relative offset between SDL timestamps and GameInput timestamps
     Uint64 now = SDL_GetTicksNS();
-    uint64_t timestampUS = IGameInput_GetCurrentTimestamp(g_pGameInput);
+    uint64_t timestampUS = g_pGameInput->GetCurrentTimestamp();
     g_GameInputTimestampOffset = (SDL_NS_TO_US(now) - timestampUS);
 
     GAMEINPUT_JoystickDetect();
@@ -292,7 +303,7 @@ static void GAMEINPUT_JoystickDetect(void)
             elem->isAdded = true;
         }
 
-        if (elem->isDeleteRequested || !(IGameInputDevice_GetDeviceStatus(elem->device) & GameInputDeviceConnected)) {
+        if (elem->isDeleteRequested || !(elem->device->GetDeviceStatus() & GameInputDeviceConnected)) {
             SDL_PrivateJoystickRemoved(elem->device_instance);
             GAMEINPUT_InternalRemoveByIndex(idx--);
         }
@@ -357,6 +368,7 @@ static SDL_JoystickID GAMEINPUT_JoystickGetDeviceInstanceID(int device_index)
 
 static void GAMEINPUT_UpdatePowerInfo(SDL_Joystick *joystick, IGameInputDevice *device)
 {
+#if 0
     GameInputBatteryState battery_state;
     SDL_PowerState state;
     int percent = 0;
@@ -385,10 +397,10 @@ static void GAMEINPUT_UpdatePowerInfo(SDL_Joystick *joystick, IGameInputDevice *
         percent = (int)SDL_roundf((battery_state.remainingCapacity / battery_state.fullChargeCapacity) * 100.0f);
     }
     SDL_SendJoystickPowerInfo(joystick, state, percent);
+#endif
 }
 
-#ifdef IGameInput_RegisterSystemButtonCallback
-
+#if GAMEINPUT_API_VERSION >= 1
 static void CALLBACK GAMEINPUT_InternalSystemButtonCallback(
     _In_ GameInputCallbackToken callbackToken,
     _In_ void * context,
@@ -415,8 +427,7 @@ static void CALLBACK GAMEINPUT_InternalSystemButtonCallback(
         SDL_UnlockJoysticks();
     }
 }
-
-#endif // IGameInput_RegisterSystemButtonCallback
+#endif // GAMEINPUT_API_VERSION >= 1
 
 static bool GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index)
 {
@@ -441,19 +452,15 @@ static bool GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index)
         joystick->nbuttons = 11;
         joystick->nhats = 1;
 
-#ifdef IGameInput_RegisterSystemButtonCallback
+#if GAMEINPUT_API_VERSION >= 1
         if (info->supportedSystemButtons != GameInputSystemButtonNone) {
             if (info->supportedSystemButtons & GameInputSystemButtonShare) {
                 ++joystick->nbuttons;
             }
 
-#if 1 // The C macro in GameInput.h version 10.0.26100 refers to a focus policy which I guess has been removed from the final API?
-#undef IGameInput_RegisterSystemButtonCallback
-#define IGameInput_RegisterSystemButtonCallback(This, device, buttonFilter, context, callbackFunc, callbackToken) ((This)->lpVtbl->RegisterSystemButtonCallback(This, device, buttonFilter, context, callbackFunc, callbackToken))
-#endif
-            IGameInput_RegisterSystemButtonCallback(g_pGameInput, elem->device, (GameInputSystemButtonGuide | GameInputSystemButtonShare), joystick, GAMEINPUT_InternalSystemButtonCallback, &hwdata->system_button_callback_token);
+            g_pGameInput->RegisterSystemButtonCallback(elem->device, (GameInputSystemButtonGuide | GameInputSystemButtonShare), joystick, GAMEINPUT_InternalSystemButtonCallback, &hwdata->system_button_callback_token);
         }
-#endif // IGameInput_RegisterSystemButtonCallback
+#endif // GAMEINPUT_API_VERSION >= 1
     } else {
         joystick->naxes = info->controllerAxisCount;
         joystick->nbuttons = info->controllerButtonCount;
@@ -467,6 +474,7 @@ static bool GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index)
         SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, true);
     }
 
+#if 0
     if (info->supportedInput & GameInputKindTouch) {
         SDL_PrivateJoystickAddTouchpad(joystick, info->touchPointCount);
     }
@@ -482,6 +490,7 @@ static bool GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index)
     } else {
         joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRED;
     }
+#endif
     return true;
 }
 
@@ -492,7 +501,7 @@ static bool GAMEINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequenc
     GameInputRumbleParams *params = &hwdata->rumbleParams;
     params->lowFrequency = (float)low_frequency_rumble / (float)SDL_MAX_UINT16;
     params->highFrequency = (float)high_frequency_rumble / (float)SDL_MAX_UINT16;
-    IGameInputDevice_SetRumbleState(hwdata->devref->device, params);
+    hwdata->devref->device->SetRumbleState(params);
     return true;
 }
 
@@ -503,7 +512,7 @@ static bool GAMEINPUT_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left
     GameInputRumbleParams *params = &hwdata->rumbleParams;
     params->leftTrigger = (float)left_rumble / (float)SDL_MAX_UINT16;
     params->rightTrigger = (float)right_rumble / (float)SDL_MAX_UINT16;
-    IGameInputDevice_SetRumbleState(hwdata->devref->device, params);
+    hwdata->devref->device->SetRumbleState(params);
     return true;
 }
 
@@ -531,15 +540,15 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
     IGameInputReading *reading = NULL;
     Uint64 timestamp;
     GameInputGamepadState state;
-    HRESULT hR;
+    HRESULT hr;
 
-    hR = IGameInput_GetCurrentReading(g_pGameInput, info->supportedInput, device, &reading);
-    if (FAILED(hR)) {
+    hr = g_pGameInput->GetCurrentReading(info->supportedInput, device, &reading);
+    if (FAILED(hr)) {
         // don't SetError here since there can be a legitimate case when there's no reading avail
         return;
     }
 
-    timestamp = SDL_US_TO_NS(IGameInputReading_GetTimestamp(reading) + g_GameInputTimestampOffset);
+    timestamp = SDL_US_TO_NS(reading->GetTimestamp() + g_GameInputTimestampOffset);
 
     if (GAMEINPUT_InternalIsGamepad(info)) {
         static WORD s_XInputButtons[] = {
@@ -557,7 +566,7 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
         };
         Uint8 btnidx = 0, hat = 0;
 
-        if (IGameInputReading_GetGamepadState(reading, &state)) {
+        if (reading->GetGamepadState(&state)) {
             for (btnidx = 0; btnidx < SDL_arraysize(s_XInputButtons); ++btnidx) {
                 WORD button_mask = s_XInputButtons[btnidx];
                 if (!button_mask) {
@@ -599,7 +608,7 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
 
         if (button_state) {
             uint32_t i;
-            uint32_t button_count = IGameInputReading_GetControllerButtonState(reading, info->controllerButtonCount, button_state);
+            uint32_t button_count = reading->GetControllerButtonState(info->controllerButtonCount, button_state);
             for (i = 0; i < button_count; ++i) {
                 SDL_SendJoystickButton(timestamp, joystick, (Uint8)i, button_state[i]);
             }
@@ -609,7 +618,7 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
 #define CONVERT_AXIS(v) (Sint16)((v)*65535.0f - 32768.0f)
         if (axis_state) {
             uint32_t i;
-            uint32_t axis_count = IGameInputReading_GetControllerAxisState(reading, info->controllerAxisCount, axis_state);
+            uint32_t axis_count = reading->GetControllerAxisState(info->controllerAxisCount, axis_state);
             for (i = 0; i < axis_count; ++i) {
                 SDL_SendJoystickAxis(timestamp, joystick, (Uint8)i, CONVERT_AXIS(axis_state[i]));
             }
@@ -619,7 +628,7 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
 
         if (switch_state) {
             uint32_t i;
-            uint32_t switch_count = IGameInputReading_GetControllerSwitchState(reading, info->controllerSwitchCount, switch_state);
+            uint32_t switch_count = reading->GetControllerSwitchState(info->controllerSwitchCount, switch_state);
             for (i = 0; i < switch_count; ++i) {
                 Uint8 hat;
                 switch (switch_state[

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