Repository: libsdl-org/SDL (C)

Issue:
GameInput v1 header changes
On Windows, the `gameinput.h` file (taken from [NuGet](https://www.nuget.org/packages/Microsoft.GameInput)) is C++ instead of C. Since the source files that include it are C, the compiler tries to interpret the header file as C and errors. Also, there are other differences that make it incompatible with the v0 API on console


Discussion:

Patch:
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[i]) {
@@ -658,6 +667,7 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
         }
     }
 
+#if 0
     if (info->supportedInput & GameInputKindTouch) {
         GameInputTouchState *touch_state = SDL_stack_alloc(GameInputTouchState, info->touchPointCount);
         if (touch_state) {
@@ -679,8 +689,9 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
             // FIXME: How do we interpret the motion data?
         }
     }
+#endif
 
-    IGameInputReading_Release(reading);
+    reading->Release();
 
     // FIXME: We can poll this at a much lower rate
     GAMEINPUT_UpdatePowerInfo(joystick, device);
@@ -691,7 +702,11 @@ static void GAMEINPUT_JoystickClose(SDL_Joystick* joystick)
     GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata;
 
     if (hwdata->system_button_callback_token) {
-        IGameInput_UnregisterCallback(g_pGameInput, hwdata->system_button_callback_token, 5000);
+#if GAMEINPUT_API_VERSION >= 1
+        g_pGameInput->UnregisterCallback(hwdata->system_button_callback_token);
+#else
+        g_pGameInput->UnregisterCallback(hwdata->system_button_callback_token, 10000);
+#endif
     }
     SDL_free(hwdata);
 
@@ -702,8 +717,14 @@ static void GAMEINPUT_JoystickQuit(void)
 {
     if (g_pGameInput) {
         // free the callback
-        IGameInput_UnregisterCallback(g_pGameInput, g_GameInputCallbackToken, /*timeoutInUs:*/ 10000);
-        g_GameInputCallbackToken = GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE;
+        if (g_GameInputCallbackToken) {
+#if GAMEINPUT_API_VERSION >= 1
+            g_pGameInput->UnregisterCallback(g_GameInputCallbackToken);
+#else
+            g_pGameInput->UnregisterCallback(g_GameInputCallbackToken, 10000);
+#endif
+            g_GameInputCallbackToken = 0;
+        }
 
         // free the list
         while (g_GameInputList.count > 0) {
@@ -738,7 +759,7 @@ static bool GAMEINPUT_JoystickGetGamepadMapping(int device_index, SDL_GamepadMap
     out->back.kind = EMappingKind_Button;
     out->back.target = SDL_GAMEPAD_BUTTON_BACK;
 
-#ifdef IGameInput_RegisterSystemButtonCallback
+#if GAMEINPUT_API_VERSION >= 1
     if (elem->info->supportedSystemButtons & GameInputSystemButtonGuide) {
         out->guide.kind = EMappingKind_Button;
         out->guide.target = SDL_GAMEPAD_BUTTON_GUIDE;
@@ -748,7 +769,7 @@ static bool GAMEINPUT_JoystickGetGamepadMapping(int device_index, SDL_GamepadMap
         out->misc1.kind = EMappingKind_Button;
         out->misc1.target = SDL_GAMEPAD_BUTTON_GAMEINPUT_SHARE;
     }
-#endif
+#endif // GAMEINPUT_API_VERSION >= 1
 
     out->start.kind = EMappingKind_Button;
     out->start.target = SDL_GAMEPAD_BUTTON_START;
diff --git a/src/video/windows/SDL_windowsgameinput.c b/src/video/windows/SDL_windowsgameinput.cpp
similarity index 81%
rename from src/video/windows/SDL_windowsgameinput.c
rename to src/video/windows/SDL_windowsgameinput.cpp
index 183733a380470..7ea77ecc558b4 100644
--- a/src/video/windows/SDL_windowsgameinput.c
+++ b/src/video/windows/SDL_windowsgameinput.cpp
@@ -22,16 +22,14 @@
 
 #include "SDL_windowsvideo.h"
 
-// GameInput currently has a bug with keys stuck on focus change, and crashes on initialization on some systems, so we'll disable it until these issues are fixed.
-#undef HAVE_GAMEINPUT_H
-
 #ifdef HAVE_GAMEINPUT_H
 
 #include "../../core/windows/SDL_gameinput.h"
+extern "C" {
 #include "../../events/SDL_mouse_c.h"
 #include "../../events/SDL_keyboard_c.h"
 #include "../../events/scancodes_windows.h"
-
+}
 
 #define MAX_GAMEINPUT_BUTTONS   7   // GameInputMouseWheelTiltRight is the highest button
 
@@ -75,7 +73,14 @@ static bool GAMEINPUT_InternalAddOrFind(WIN_GameInputData *data, IGameInputDevic
     const GameInputDeviceInfo *info;
     bool result = false;
 
-    info = IGameInputDevice_GetDeviceInfo(pDevice);
+#if GAMEINPUT_API_VERSION >= 1
+    HRESULT hr = pDevice->GetDeviceInfo(&info);
+    if (FAILED(hr)) {
+        return WIN_SetErrorFromHRESULT("IGameInputDevice_GetDeviceInfo", hr);
+    }
+#else
+    info = pDevice->GetDeviceInfo();
+#endif
 
     SDL_LockMutex(data->lock);
     {
@@ -100,15 +105,11 @@ static bool GAMEINPUT_InternalAddOrFind(WIN_GameInputData *data, IGameInputDevic
             goto done;
         }
 
-        if (info->deviceStrings) {
-            // In theory we could get the manufacturer and product strings here, but they're NULL for all the devices I've tested
-        }
-
         if (info->displayName) {
             // This could give us a product string, but it's NULL for all the devices I've tested
         }
 
-        IGameInputDevice_AddRef(pDevice);
+        pDevice->AddRef();
         device->pDevice = pDevice;
         device->instance_id = SDL_GetNextObjectID();
         device->info = info;
@@ -147,15 +148,15 @@ static bool GAMEINPUT_InternalRemoveByIndex(WIN_GameInputData *data, int idx)
                     SDL_RemoveKeyboard(device->instance_id, true);
                 }
                 if (device->last_mouse_reading) {
-                    IGameInputReading_Release(device->last_mouse_reading);
+                    device->last_mouse_reading->Release();
                     device->last_mouse_reading = NULL;
                 }
                 if (device->last_keyboard_reading) {
-                    IGameInputReading_Release(device->last_keyboard_reading);
+                    device->last_keyboard_reading->Release();
                     device->last_keyboard_reading = NULL;
                 }
             }
-            IGameInputDevice_Release(device->pDevice);
+            device->pDevice->Release();
             SDL_free(device->name);
             SDL_free(device);
         }
@@ -217,6 +218,8 @@ bool WIN_InitGameInput(SDL_VideoDevice *_this)
 {
     WIN_GameInputData *data;
     HRESULT hr;
+    Uint64 now;
+    uint64_t timestampUS;
     bool result = false;
 
     if (_this->internal->gameinput_context) {
@@ -238,22 +241,21 @@ bool WIN_InitGameInput(SDL_VideoDevice *_this)
         goto done;
     }
 
-    hr = IGameInput_RegisterDeviceCallback(data->pGameInput,
-                                           NULL,
-                                           (GameInputKindMouse | GameInputKindKeyboard),
-                                           GameInputDeviceConnected,
-                                           GameInputBlockingEnumeration,
-                                           data,
-                                           GAMEINPUT_InternalDeviceCallback,
-                                           &data->gameinput_callback_token);
+    hr = data->pGameInput->RegisterDeviceCallback(NULL,
+                                                  (GameInputKindMouse | GameInputKindKeyboard),
+                                                  GameInputDeviceConnected,
+                                                  GameInputBlockingEnumeration,
+                                                  data,
+                                                  GAMEINPUT_InternalDeviceCallback,
+                                                  &data->gameinput_callback_token);
     if (FAILED(hr)) {
-        SDL_SetError("IGameInput::RegisterDeviceCallback failure with HRESULT of %08X", hr);
+        WIN_SetErrorFromHRESULT("IGameInput::RegisterDeviceCallback", hr);
         goto done;
     }
 
     // Calculate the relative offset between SDL timestamps and GameInput timestamps
-    Uint64 now = SDL_GetTicksNS();
-    uint64_t timestampUS = IGameInput_GetCurrentTimestamp(data->pGameInput);
+    now = SDL_GetTicksNS();
+    timestampUS = data->pGameInput->GetCurrentTimestamp();
     data->timestamp_offset = (SDL_NS_TO_US(now) - timestampUS);
 
     result = true;
@@ -268,12 +270,12 @@ bool WIN_InitGameInput(SDL_VideoDevice *_this)
 static void GAMEINPUT_InitialMouseReading(WIN_GameInputData *data, SDL_Window *window, GAMEINPUT_Device *device, IGameInputReading *reading)
 {
     GameInputMouseState state;
-    if (SUCCEEDED(IGameInputReading_GetMouseState(reading, &state))) {
-        Uint64 timestamp = SDL_US_TO_NS(IGameInputReading_GetTimestamp(reading) + data->timestamp_offset);
+    if (reading->GetMouseState(&state)) {
+        Uint64 timestamp = SDL_US_TO_NS(reading->GetTimestamp() + data->timestamp_offset);
         SDL_MouseID mouseID = device->instance_id;
 
         for (int i = 0; i < MAX_GAMEINPUT_BUTTONS; ++i) {
-            const GameInputMouseButtons mask = (1 << i);
+            const GameInputMouseButtons mask = GameInputMouseButtons(1 << i);
             bool down = ((state.buttons & mask) != 0);
             SDL_SendMouseButton(timestamp, window, mouseID, GAMEINPUT_button_map[i], down);
         }
@@ -284,9 +286,8 @@ static void GAMEINPUT_HandleMouseDelta(WIN_GameInputData *data, SDL_Window *wind
 {
     GameInputMouseState last;
     GameInputMouseState state;
-    if (SUCCEEDED(IGameInputReading_GetMouseState(last_reading, &last)) &&
-        SUCCEEDED(IGameInputReading_GetMouseState(reading, &state))) {
-        Uint64 timestamp = SDL_US_TO_NS(IGameInputReading_GetTimestamp(reading) + data->timestamp_offset);
+    if (last_reading->GetMouseState(&last) && reading->GetMouseState(&state)) {
+        Uint64 timestamp = SDL_US_TO_NS(reading->GetTimestamp() + data->timestamp_offset);
         SDL_MouseID mouseID = device->instance_id;
 
         GameInputMouseState delta;
@@ -301,7 +302,7 @@ static void GAMEINPUT_HandleMouseDelta(WIN_GameInputData *data, SDL_Window *wind
         }
         if (delta.buttons) {
             for (int i = 0; i < MAX_GAMEINPUT_BUTTONS; ++i) {
-                const GameInputMouseButtons mask = (1 << i);
+                const GameInputMouseButtons mask = GameInputMouseButtons(1 << i);
                 if (delta.buttons & mask) {
                     bool down = ((state.buttons & mask) != 0);
                     SDL_SendMouseButton(timestamp, window, mouseID, GAMEINPUT_button_map[i], down);
@@ -337,7 +338,7 @@ static bool KeysHaveScancode(const GameInputKeyState *keys, uint32_t count, SDL_
 
 static void GAMEINPUT_InitialKeyboardReading(WIN_GameInputData *data, SDL_Window *window, GAMEINPUT_Device *device, IGameInputReading *reading)
 {
-    Uint64 timestamp = SDL_US_TO_NS(IGameInputReading_GetTimestamp(reading) + data->timestamp_offset);
+    Uint64 timestamp = SDL_US_TO_NS(reading->GetTimestamp() + data->timestamp_offset);
     SDL_KeyboardID keyboardID = device->instance_id;
 
     uint32_t max_keys = device->info->keyboardInfo->maxSimultaneousKeys;
@@ -346,7 +347,7 @@ static void GAMEINPUT_InitialKeyboardReading(WIN_GameInputData *data, SDL_Window
         return;
     }
 
-    uint32_t num_keys = IGameInputReading_GetKeyState(reading, max_keys, keys);
+    uint32_t num_keys = reading->GetKeyState(max_keys, keys);
     if (!num_keys) {
         // FIXME: We probably need to track key state by keyboardID
         SDL_ResetKeyboard();
@@ -382,7 +383,7 @@ static void DumpKeys(const char *prefix, GameInputKeyState *keys, uint32_t count
 
 static void GAMEINPUT_HandleKeyboardDelta(WIN_GameInputData *data, SDL_Window *window, GAMEINPUT_Device *device, IGameInputReading *last_reading, IGameInputReading *reading)
 {
-    Uint64 timestamp = SDL_US_TO_NS(IGameInputReading_GetTimestamp(reading) + data->timestamp_offset);
+    Uint64 timestamp = SDL_US_TO_NS(reading->GetTimestamp() + data->timestamp_offset);
     SDL_KeyboardID keyboardID = device->instance_id;
 
     uint32_t max_keys = device->info->keyboardInfo->maxSimultaneousKeys;
@@ -394,8 +395,8 @@ static void GAMEINPUT_HandleKeyboardDelta(WIN_GameInputData *data, SDL_Window *w
 
     uint32_t index_last = 0;
     uint32_t index_keys = 0;
-    uint32_t num_last = IGameInputReading_GetKeyState(last_reading, max_keys, last);
-    uint32_t num_keys = IGameInputReading_GetKeyState(reading, max_keys, keys);
+    uint32_t num_last = last_reading->GetKeyState(max_keys, last);
+    uint32_t num_keys = reading->GetKeyState(max_keys, keys);
 #ifdef DEBUG_KEYS
     SDL_Log("Timestamp: %llu", timestamp);
     DumpKeys("Last keys:", last, num_last);
@@ -463,20 +464,20 @@ void WIN_UpdateGameInput(SDL_VideoDevice *_this)
             if (data->enabled_input & GameInputKindMouse) {
                 if (device->last_mouse_reading) {
                     HRESULT hr;
-                    while (SUCCEEDED(hr = IGameInput_GetNextReading(data->pGameInput, device->last_mouse_reading, GameInputKindMouse, device->pDevice, &reading))) {
+                    while (SUCCEEDED(hr = data->pGameInput->GetNextReading(device->last_mouse_reading, GameInputKindMouse, device->pDevice, &reading))) {
                         GAMEINPUT_HandleMouseDelta(data, window, device, device->last_mouse_reading, reading);
-                        IGameInputReading_Release(device->last_mouse_reading);
+                        device->last_mouse_reading->Release();
                         device->last_mouse_reading = reading;
                     }
                     if (hr != GAMEINPUT_E_READING_NOT_FOUND) {
-                        if (SUCCEEDED(IGameInput_GetCurrentReading(data->pGameInput, GameInputKindMouse, device->pDevice, &reading))) {
+                        if (SUCCEEDED(data->pGameInput->GetCurrentReading(GameInputKindMouse, device->pDevice, &reading))) {
                             GAMEINPUT_HandleMouseDelta(data, window, device, device->last_mouse_reading, reading);
-                            IGameInputReading_Release(device->last_mouse_reading);
+                            device->last_mouse_reading->Release();
                             device->last_mouse_reading = reading;
                         }
                     }
                 } else {
-                    if (SUCCEEDED(IGameInput_GetCurrentReading(data->pGameInput, GameInputKindMouse, device->pDevice, &reading))) {
+                    if (SUCCEEDED(data->pGameInput->GetCurrentReading(GameInputKindMouse, device->pDevice, &reading))) {
                         GAMEINPUT_InitialMouseReading(data, window, device, reading);
                         device->last_mouse_reading = reading;
                     }
@@ -487,26 +488,26 @@ void WIN_UpdateGameInput(SDL_VideoDevice *_this)
                 if (window->text_input_active) {
                     // Reset raw input while text input is active
                     if (device->last_keyboard_reading) {
-                        IGameInputReading_Release(device->last_keyboard_reading);
+                        device->last_keyboard_reading->Release();
                         device->last_keyboard_reading = NULL;
                     }
                 } else {
                     if (device->last_keyboard_reading) {
                         HRESULT hr;
-                        while (SUCCEEDED(hr = IGameInput_GetNextReading(data->pGameInput, device->last_keyboard_reading, GameInputKindKeyboard, device->pDevice, &reading))) {
+                        while (SUCCEEDED(hr = data->pGameInput->GetNextReading(device->last_keyboard_reading, GameInputKindKeyboard, device->pDevice, &reading))) {
                             GAMEINPUT_HandleKeyboardDelta(data, window, device, device->last_keyboard_reading, reading);
-                            IGameInputReading_Release(device->last_keyboard_reading);
+                            device->last_keyboard_reading->Release();
                             device->last_keyboard_reading = reading;
                         }
                         if (hr != GAMEINPUT_E_READING_NOT_FOUND) {
-                            if (SUCCEEDED(IGameInput_GetCurrentReading(data->pGameInput, GameInputKindKeyboard, device->pDevice, &reading))) {
+                            if (SUCCEEDED(data->pGameInput->GetCurrentReading(GameInputKindKeyboard, device->pDevice, &reading))) {
                                 GAMEINPUT_HandleKeyboardDelta(data, window, device, device->last_keyboard_reading, reading);
-                                IGameInputReading_Release(device->last_keyboard_reading);
+                                device->last_keyboard_reading->Release();
                                 device->last_keyboard_reading = reading;
                             }
                         }
                     } else {
-                        if (SUCCEEDED(IGameInput_GetCurrentReading(data->pGameInput, GameInputKindKeyboard, device->pDevice, &reading))) {
+                        if (SUCCEEDED(data->pGameInput->GetCurrentReading(GameInputKindKeyboard, device->pDevice, &reading))) {
                             GAMEINPUT_InitialKeyboardReading(data, window, device, reading);
                             device->last_keyboard_reading = reading;
                         }
@@ -534,12 +535,12 @@ bool WIN_UpdateGameInputEnabled(SDL_VideoDevice *_this)
             GAMEINPUT_Device *device = data->devices[i];
 
             if (device->last_mouse_reading && !raw_mouse_enabled) {
-                IGameInputReading_Release(device->last_mouse_reading);
+                device->last_mouse_reading->Release();
                 device->last_mouse_reading = NULL;
             }
 
             if (device->last_keyboard_reading && !raw_keyboard_enabled) {
-                IGameInputReading_Release(device->last_keyboard_reading);
+                device->last_keyboard_reading->Release();
                 device->last_keyboard_reading = NULL;
             }
         }
@@ -559,9 +560,13 @@ void WIN_QuitGameInput(SDL_VideoDevice *_this)
 
     if (data->pGameInput) {
         // free the callback
-        if (data->gameinput_callback_token != GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE) {
-            IGameInput_UnregisterCallback(data->pGameInput, data->gameinput_callback_token, /*timeoutInUs:*/ 10000);
-            data->gameinput_callback_token = GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE;
+        if (data->gameinput_callback_token) {
+#if GAMEINPUT_API_VERSION >= 1
+            data->pGameInput->UnregisterCallback(data->gameinput_callback_token);
+#else
+            data->pGameInput->UnregisterCallback(data->gameinput_callback_token, 10000);
+#endif
+            data->gameinput_callback_token = 0;
         }
 
         // free the list
@@ -569,7 +574,7 @@ void WIN_QuitGameInput(SDL_VideoDevice *_this)
             GAMEINPUT_InternalRemoveByIndex(data, 0);
         }
 
-        IGameInput_Release(data->pGameInput);
+        data->pGameInput->Release();
         data->pGameInput = NULL;
     }
 
diff --git a/src/video/windows/SDL_windowsgameinput.h b/src/video/windows/SDL_windowsgameinput.h
index 561de9c4e0c89..e6f1afa2d62ff 100644
--- a/src/video/windows/SDL_windowsgameinput.h
+++ b/src/video/windows/SDL_windowsgameinput.h
@@ -22,8 +22,17 @@
 
 typedef struct WIN_GameInputData WIN_GameInputData;
 
+// Set up for C function definitions, even when using C++
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 extern bool WIN_InitGameInput(SDL_VideoDevice *_this);
 extern bool WIN_UpdateGameInputEnabled(SDL_VideoDevice *_this);
 extern void WIN_UpdateGameInput(SDL_VideoDevice *_this);
 extern void WIN_QuitGameInput(SDL_VideoDevice *_this);
 
+// Ends C function definitions when using C++
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h
index 8a0edf5ae0243..0e9c50eb86932 100644
--- a/src/video/windows/SDL_windowsvideo.h
+++ b/src/video/windows/SDL_windowsvideo.h
@@ -28,8 +28,10 @@
 #include "../SDL_sysvideo.h"
 
 #ifdef HAVE_DXGI_H
+#ifndef __cplusplus
 #define CINTERFACE
 #define COBJMACROS
+#endif
 #include <dxgi.h>
 #endif
 


Test patch:
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})")