SDL: Added the option for GameInput support to the Win32 SDL build

From fee140bdfeb9971d625392310430347c8f4691df Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 17 Feb 2024 14:07:42 -0800
Subject: [PATCH] Added the option for GameInput support to the Win32 SDL build

GameInput is designed to be used by Win32 C applications, so no need to restrict it to the GDK build.
---
 VisualC-GDK/SDL/SDL.vcxproj                   |   7 +-
 VisualC-GDK/SDL/SDL.vcxproj.filters           |   2 +
 VisualC/SDL/SDL.vcxproj                       |   2 +
 VisualC/SDL/SDL.vcxproj.filters               |   9 ++
 .../build_config/SDL_build_config_windows.h   |   1 +
 src/core/windows/SDL_xinput.c                 |   4 -
 ...utjoystick.cpp => SDL_gameinputjoystick.c} | 130 ++++++++++--------
 src/joystick/gdk/SDL_gameinputjoystick_c.h    |  13 +-
 8 files changed, 89 insertions(+), 79 deletions(-)
 rename src/joystick/gdk/{SDL_gameinputjoystick.cpp => SDL_gameinputjoystick.c} (82%)

diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index cec3972f0f9c..9d4ecf0da5f6 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -425,6 +425,7 @@
     <ClInclude Include="..\..\src\hidapi\hidapi\hidapi.h" />
     <ClInclude Include="..\..\src\hidapi\SDL_hidapi_c.h" />
     <ClInclude Include="..\..\src\joystick\controller_type.h" />
+    <ClInclude Include="..\..\src\joystick\gdk\SDL_gameinputjoystick_c.h" />
     <ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapijoystick_c.h" />
     <ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapi_rumble.h" />
     <ClInclude Include="..\..\src\joystick\SDL_gamepad_c.h" />
@@ -438,7 +439,6 @@
     <ClInclude Include="..\..\src\joystick\windows\SDL_rawinputjoystick_c.h" />
     <ClInclude Include="..\..\src\joystick\windows\SDL_windowsjoystick_c.h" />
     <ClInclude Include="..\..\src\joystick\windows\SDL_xinputjoystick_c.h" />
-    <ClInclude Include="..\..\src\joystick\gdk\SDL_gameinputjoystick_c.h" />
     <ClInclude Include="..\..\src\libm\math_libm.h" />
     <ClInclude Include="..\..\src\libm\math_private.h" />
     <ClInclude Include="..\..\src\locale\SDL_syslocale.h" />
@@ -646,6 +646,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\hidapi\SDL_hidapijoystick.c" />
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_combined.c" />
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_gamecube.c" />
@@ -690,10 +691,6 @@
       <CompileAs Condition="'$(Configuration)|$(Platform)'=='Release|Gaming.Xbox.Scarlett.x64'">CompileAsCpp</CompileAs>
       <CompileAs Condition="'$(Configuration)|$(Platform)'=='Release|Gaming.Xbox.XboxOne.x64'">CompileAsCpp</CompileAs>
     </ClCompile>
-    <ClCompile Include="..\..\src\joystick\gdk\SDL_gameinputjoystick.cpp">
-      <PrecompiledHeaderOutputFile Condition="'$(Configuration)|$(Platform)'=='Debug|Gaming.Desktop.x64'">$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
-      <PrecompiledHeaderOutputFile Condition="'$(Configuration)|$(Platform)'=='Release|Gaming.Desktop.x64'">$(IntDir)$(TargetName)_cpp.pch</PrecompiledHeaderOutputFile>
-    </ClCompile>
     <ClCompile Include="..\..\src\libm\e_atan2.c" />
     <ClCompile Include="..\..\src\libm\e_exp.c" />
     <ClCompile Include="..\..\src\libm\e_fmod.c" />
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index f5d648fe70cb..c59af5d0a57b 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/VisualC-GDK/SDL/SDL.vcxproj.filters
@@ -52,6 +52,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\hidapi\SDL_hidapijoystick.c" />
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_combined.c" />
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_gamecube.c" />
@@ -319,6 +320,7 @@
     <ClInclude Include="..\..\src\hidapi\hidapi\hidapi.h" />
     <ClInclude Include="..\..\src\hidapi\SDL_hidapi_c.h" />
     <ClInclude Include="..\..\src\joystick\controller_type.h" />
+    <ClInclude Include="..\..\src\joystick\gdk\SDL_gameinputjoystick_c.h" />
     <ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapijoystick_c.h" />
     <ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapi_rumble.h" />
     <ClInclude Include="..\..\src\joystick\SDL_gamepad_c.h" />
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index 45415212dcfd..c5a508b82746 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -347,6 +347,7 @@
     <ClInclude Include="..\..\src\hidapi\hidapi\hidapi.h" />
     <ClInclude Include="..\..\src\hidapi\SDL_hidapi_c.h" />
     <ClInclude Include="..\..\src\joystick\controller_type.h" />
+    <ClInclude Include="..\..\src\joystick\gdk\SDL_gameinputjoystick_c.h" />
     <ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapijoystick_c.h" />
     <ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapi_rumble.h" />
     <ClInclude Include="..\..\src\joystick\SDL_gamepad_c.h" />
@@ -520,6 +521,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\hidapi\SDL_hidapijoystick.c" />
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_combined.c" />
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_gamecube.c" />
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index 8073004813fd..679a2bc55203 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -88,6 +88,9 @@
     <Filter Include="joystick\dummy">
       <UniqueIdentifier>{d008487d-6ed0-4251-848b-79a68e3c1459}</UniqueIdentifier>
     </Filter>
+    <Filter Include="joystick\gdk">
+      <UniqueIdentifier>{c9e8273e-13ae-47dc-bef8-8ad8e64c9a3e}</UniqueIdentifier>
+    </Filter>
     <Filter Include="joystick\hidapi">
       <UniqueIdentifier>{c9e8273e-13ae-47dc-bef8-8ad8e64c9a3d}</UniqueIdentifier>
     </Filter>
@@ -546,6 +549,9 @@
     <ClInclude Include="..\..\src\haptic\windows\SDL_windowshaptic_c.h">
       <Filter>haptic\windows</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\joystick\gdk\SDL_gameinputjoystick_c.h">
+      <Filter>joystick\gdk</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapijoystick_c.h">
       <Filter>joystick\hidapi</Filter>
     </ClInclude>
@@ -1063,6 +1069,9 @@
     <ClCompile Include="..\..\src\joystick\dummy\SDL_sysjoystick.c">
       <Filter>joystick\dummy</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\joystick\gdk\SDL_gameinputjoystick.c">
+      <Filter>joystick\gdk</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_combined.c">
       <Filter>joystick\hidapi</Filter>
     </ClCompile>
diff --git a/include/build_config/SDL_build_config_windows.h b/include/build_config/SDL_build_config_windows.h
index 52c2df72593b..68664c5ba9a3 100644
--- a/include/build_config/SDL_build_config_windows.h
+++ b/include/build_config/SDL_build_config_windows.h
@@ -238,6 +238,7 @@ typedef unsigned int uintptr_t;
 
 /* Enable various input drivers */
 #define SDL_JOYSTICK_DINPUT 1
+/*#define SDL_JOYSTICK_GAMEINPUT 1*/
 #define SDL_JOYSTICK_HIDAPI 1
 #ifndef SDL_PLATFORM_WINRT
 #define SDL_JOYSTICK_RAWINPUT   1
diff --git a/src/core/windows/SDL_xinput.c b/src/core/windows/SDL_xinput.c
index fdeed7085449..13c06bde4dbc 100644
--- a/src/core/windows/SDL_xinput.c
+++ b/src/core/windows/SDL_xinput.c
@@ -20,8 +20,6 @@
 */
 #include "SDL_internal.h"
 
-#ifndef SDL_JOYSTICK_GAMEINPUT
-
 #include "SDL_xinput.h"
 
 /* Set up for C function definitions, even when using C++ */
@@ -144,5 +142,3 @@ void WIN_UnloadXInputDLL(void)
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* !SDL_JOYSTICK_GAMEINPUT */
diff --git a/src/joystick/gdk/SDL_gameinputjoystick.cpp b/src/joystick/gdk/SDL_gameinputjoystick.c
similarity index 82%
rename from src/joystick/gdk/SDL_gameinputjoystick.cpp
rename to src/joystick/gdk/SDL_gameinputjoystick.c
index 1b4223d6ea72..230f5c96ff8d 100644
--- a/src/joystick/gdk/SDL_gameinputjoystick.cpp
+++ b/src/joystick/gdk/SDL_gameinputjoystick.c
@@ -18,6 +18,8 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+#include "SDL_internal.h"
+
 #include "SDL_gameinputjoystick_c.h"
 
 #if defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT
@@ -39,9 +41,9 @@ typedef struct GAMEINPUT_InternalDevice
     SDL_JoystickGUID joystickGuid; /* generated by SDL. */
     SDL_JoystickID instanceId;     /* generated by SDL. */
     int playerIndex;
-    Uint32 caps;
+    GameInputRumbleMotors supportedRumbleMotors;
     char devicePath[(APP_LOCAL_DEVICE_ID_SIZE * 2) + 1];
-    bool isAdded, isDeleteRequested;
+    SDL_bool isAdded, isDeleteRequested;
 } GAMEINPUT_InternalDevice;
 
 typedef struct GAMEINPUT_InternalList
@@ -59,6 +61,7 @@ typedef struct joystick_hwdata
 
 
 static GAMEINPUT_InternalList g_GameInputList = { NULL };
+static void *g_hGameInputDLL = NULL;
 static IGameInput *g_pGameInput = NULL;
 static GameInputCallbackToken g_GameInputCallbackToken = GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE;
 
@@ -75,7 +78,7 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice)
         return SDL_SetError("GAMEINPUT_InternalAddOrFind argument pDevice cannot be NULL");
     }
 
-    devinfo = pDevice->GetDeviceInfo();
+    devinfo = IGameInputDevice_GetDeviceInfo(pDevice);
     if (!devinfo) {
         return SDL_SetError("GAMEINPUT_InternalAddOrFind GetDeviceInfo returned NULL");
     }
@@ -95,8 +98,8 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice)
 
     /* generate a device name */
     for (idx = 0; idx < APP_LOCAL_DEVICE_ID_SIZE; ++idx) {
-        (void)SDL_snprintf(tmpbuff, SDL_arraysize(tmpbuff), "%02hhX", devinfo->deviceId.value[idx]);
-        (void)strncat_s(elem->devicePath, tmpbuff, SDL_arraysize(tmpbuff));
+        SDL_snprintf(tmpbuff, SDL_arraysize(tmpbuff), "%02hhX", devinfo->deviceId.value[idx]);
+        SDL_strlcat(elem->devicePath, tmpbuff, SDL_arraysize(tmpbuff));
     }
 
     devicelist = (GAMEINPUT_InternalDevice **)SDL_realloc(g_GameInputList.devices, sizeof(elem) * (g_GameInputList.count + 1LL));
@@ -106,16 +109,10 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice)
     }
 
     g_GameInputList.devices = devicelist;
-    pDevice->AddRef();
+    IGameInputDevice_AddRef(pDevice);
     elem->device = pDevice;
     elem->deviceName = "GameInput Gamepad";
-    elem->caps = 0;
-    if (devinfo->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) {
-        elem->caps |= SDL_JOYSTICK_CAP_RUMBLE;
-    }
-    if (devinfo->supportedRumbleMotors & (GameInputRumbleLeftTrigger | GameInputRumbleRightTrigger)) {
-        elem->caps |= SDL_JOYSTICK_CAP_TRIGGER_RUMBLE;
-    }
+    elem->supportedRumbleMotors = devinfo->supportedRumbleMotors;
     elem->joystickGuid = SDL_CreateJoystickGUID(
         SDL_HARDWARE_BUS_BLUETOOTH,
         USB_VENDOR_MICROSOFT,
@@ -142,7 +139,7 @@ static int GAMEINPUT_InternalRemoveByIndex(int idx)
         return SDL_SetError("GAMEINPUT_InternalRemoveByIndex argument idx %d is out of range", idx);
     }
 
-    g_GameInputList.devices[idx]->device->Release();
+    IGameInputDevice_Release(g_GameInputList.devices[idx]->device);
 
     if (g_GameInputList.devices[idx]) {
         SDL_free(g_GameInputList.devices[idx]);
@@ -199,7 +196,7 @@ static void CALLBACK GAMEINPUT_InternalJoystickDeviceCallback(
             elem = g_GameInputList.devices[idx];
             if (elem && elem->device == device) {
                 /* will be deleted on the next Detect call */
-                elem->isDeleteRequested = true;
+                elem->isDeleteRequested = SDL_TRUE;
                 break;
             }
         }
@@ -212,29 +209,40 @@ static int GAMEINPUT_JoystickInit(void)
 {
     HRESULT hR;
 
+    if (!g_hGameInputDLL) {
+        g_hGameInputDLL = SDL_LoadObject("gameinput.dll");
+        if (!g_hGameInputDLL) {
+            return -1;
+        }
+    }
+
     if (!g_pGameInput) {
-        hR = GameInputCreate(&g_pGameInput);
+        typedef HRESULT (WINAPI *GameInputCreate_t)(IGameInput * *gameInput);
+        GameInputCreate_t GameInputCreateFunc = (GameInputCreate_t)SDL_LoadFunction(g_hGameInputDLL, "GameInputCreate");
+        if (!GameInputCreateFunc) {
+            return -1;
+        }
+
+        hR = GameInputCreateFunc(&g_pGameInput);
         if (FAILED(hR)) {
             return SDL_SetError("GameInputCreate failure with HRESULT of %08X", hR);
         }
     }
 
-    hR = g_pGameInput->RegisterDeviceCallback(
-        nullptr,
-        GameInputKindGamepad,
-        GameInputDeviceConnected,
-        GameInputBlockingEnumeration,
-        nullptr,
-        GAMEINPUT_InternalJoystickDeviceCallback,
-        &g_GameInputCallbackToken
-    );
+    hR = IGameInput_RegisterDeviceCallback(g_pGameInput,
+                                           NULL,
+                                           GameInputKindGamepad,
+                                           GameInputDeviceConnected,
+                                           GameInputBlockingEnumeration,
+                                           NULL,
+                                           GAMEINPUT_InternalJoystickDeviceCallback,
+                                           &g_GameInputCallbackToken);
     if (FAILED(hR)) {
         return SDL_SetError("IGameInput::RegisterDeviceCallback failure with HRESULT of %08X", hR);
     }
 
     GAMEINPUT_JoystickDetect();
 
-    /* no need to free IGameInput on failure. */
     return 0;
 }
 
@@ -256,10 +264,10 @@ static void GAMEINPUT_JoystickDetect(void)
 
         if (!elem->isAdded) {
             SDL_PrivateJoystickAdded(elem->instanceId);
-            elem->isAdded = true;
+            elem->isAdded = SDL_TRUE;
         }
 
-        if (elem->isDeleteRequested || !(elem->device->GetDeviceStatus() & GameInputDeviceConnected)) {
+        if (elem->isDeleteRequested || !(IGameInputDevice_GetDeviceStatus(elem->device) & GameInputDeviceConnected)) {
             SDL_PrivateJoystickRemoved(elem->instanceId);
             GAMEINPUT_InternalRemoveByIndex(idx--);
         }
@@ -332,8 +340,8 @@ static SDL_JoystickGUID GAMEINPUT_JoystickGetDeviceGUID(int device_index)
     GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index);
 
     if (!elem) {
-        /* empty guid */
-        return { { 0 } };
+        static SDL_JoystickGUID emptyGUID;
+        return emptyGUID;
     }
 
     return elem->joystickGuid;
@@ -371,6 +379,13 @@ static int GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index)
     joystick->nbuttons = 11;
     joystick->nhats = 1;
 
+    if (elem->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) {
+        SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, SDL_TRUE);
+    }
+    if (elem->supportedRumbleMotors & (GameInputRumbleLeftTrigger | GameInputRumbleRightTrigger)) {
+        SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, SDL_TRUE);
+    }
+
     return 0;
 }
 
@@ -381,7 +396,7 @@ static int GAMEINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency
     GameInputRumbleParams *params = &hwdata->rumbleParams;
     params->lowFrequency = (float)low_frequency_rumble / (float)SDL_MAX_UINT16;
     params->highFrequency = (float)high_frequency_rumble / (float)SDL_MAX_UINT16;
-    hwdata->devref->device->SetRumbleState(params);
+    IGameInputDevice_SetRumbleState(hwdata->devref->device, params);
     return 0;
 }
 
@@ -392,15 +407,10 @@ static int 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;
-    hwdata->devref->device->SetRumbleState(params);
+    IGameInputDevice_SetRumbleState(hwdata->devref->device, params);
     return 0;
 }
 
-static Uint32 GAMEINPUT_JoystickGetCapabilities(SDL_Joystick *joystick)
-{
-    return joystick->hwdata->devref->caps;
-}
-
 static int GAMEINPUT_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
 {
     return SDL_Unsupported();
@@ -418,7 +428,7 @@ static int GAMEINPUT_JoystickSendEffect(SDL_Joystick *joystick, const void *data
 
     effect = (const GAMEINPUT_JoystickEffectData *)data;
     if (effect->type == GAMEINPUT_JoystickEffectDataType_HapticFeedback) {
-        hR = hwdata->devref->device->SetHapticMotorState(
+        hR = IGameInputDevice_SetHapticMotorState(hwdata->devref->device,
             effect->hapticFeedbackMotorIndex,
             &effect->hapticFeedbackParams
         );
@@ -450,7 +460,7 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
     IGameInputReading *reading = NULL;
     uint64_t ts = 0;
     GameInputGamepadState state;
-    HRESULT hR = g_pGameInput->GetCurrentReading(
+    HRESULT hR = IGameInput_GetCurrentReading(g_pGameInput,
         GameInputKindGamepad,
         device,
         &reading
@@ -463,9 +473,9 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
 
     /* GDKX private docs for GetTimestamp: "The microsecond timestamp describing when the input was made." */
     /* SDL expects a nanosecond timestamp, so I guess US_TO_NS should be used here? */
-    ts = SDL_US_TO_NS(reading->GetTimestamp());
+    ts = SDL_US_TO_NS(IGameInputReading_GetTimestamp(reading));
 
-    if (((!hwdata->lastTimestamp) || (ts != hwdata->lastTimestamp)) && reading->GetGamepadState(&state)) {
+    if (((!hwdata->lastTimestamp) || (ts != hwdata->lastTimestamp)) && IGameInputReading_GetGamepadState(reading, &state)) {
         /* `state` is now valid */
 
 #define tosint16(_TheValue) ((Sint16)(((_TheValue) < 0.0f) ? ((_TheValue) * 32768.0f) : ((_TheValue) * 32767.0f)))
@@ -507,7 +517,7 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
         hwdata->lastTimestamp = ts;
     }
 
-    reading->Release();
+    IGameInputReading_Release(reading);
 }
 
 static void GAMEINPUT_JoystickClose(SDL_Joystick* joystick)
@@ -520,26 +530,29 @@ static void GAMEINPUT_JoystickQuit(void)
 {
     int idx;
 
-    if (!g_pGameInput) {
-        return;
-    }
+    if (g_pGameInput) {
+        /* free the callback */
+        IGameInput_UnregisterCallback(g_pGameInput, g_GameInputCallbackToken, /*timeoutInUs:*/ 10000);
+        g_GameInputCallbackToken = GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE;
 
-    /* free the callback */
-    g_pGameInput->UnregisterCallback(g_GameInputCallbackToken, /*timeoutInUs:*/ 10000);
-    g_GameInputCallbackToken = GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE;
+        /* free the list */
+        for (idx = 0; idx < g_GameInputList.count; ++idx) {
+            IGameInputDevice_Release(g_GameInputList.devices[idx]->device);
+            SDL_free(g_GameInputList.devices[idx]);
+            g_GameInputList.devices[idx] = NULL;
+        }
+        SDL_free(g_GameInputList.devices);
+        g_GameInputList.devices = NULL;
+        g_GameInputList.count = 0;
 
-    /* free the list */
-    for (idx = 0; idx < g_GameInputList.count; ++idx) {
-        g_GameInputList.devices[idx]->device->Release();
-        SDL_free(g_GameInputList.devices[idx]);
-        g_GameInputList.devices[idx] = NULL;
+        IGameInput_Release(g_pGameInput);
+        g_pGameInput = NULL;
     }
-    SDL_free(g_GameInputList.devices);
-    g_GameInputList.devices = NULL;
-    g_GameInputList.count = 0;
 
-    g_pGameInput->Release();
-    g_pGameInput = NULL;
+    if (g_hGameInputDLL) {
+        SDL_UnloadObject(g_hGameInputDLL);
+        g_hGameInputDLL = NULL;
+    }
 }
 
 static SDL_bool GAMEINPUT_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
@@ -563,7 +576,6 @@ SDL_JoystickDriver SDL_GAMEINPUT_JoystickDriver =
     GAMEINPUT_JoystickOpen,
     GAMEINPUT_JoystickRumble,
     GAMEINPUT_JoystickRumbleTriggers,
-    GAMEINPUT_JoystickGetCapabilities,
     GAMEINPUT_JoystickSetLED,
     GAMEINPUT_JoystickSendEffect,
     GAMEINPUT_JoystickSetSensorsEnabled,
diff --git a/src/joystick/gdk/SDL_gameinputjoystick_c.h b/src/joystick/gdk/SDL_gameinputjoystick_c.h
index 03bc1c059950..0b3bc51a63ab 100644
--- a/src/joystick/gdk/SDL_gameinputjoystick_c.h
+++ b/src/joystick/gdk/SDL_gameinputjoystick_c.h
@@ -23,14 +23,10 @@
 
 #if defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT
 
-/* include this file in C++ */
+#include <stdbool.h>
+#define COBJMACROS
 #include <GameInput.h>
 
-/* Set up for C function definitions, even when using C++ */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 typedef enum GAMEINPUT_JoystickEffectDataType
 {
     GAMEINPUT_JoystickEffectDataType_HapticFeedback
@@ -50,9 +46,4 @@ typedef struct GAMEINPUT_JoystickEffectData
     };
 } GAMEINPUT_JoystickEffectData;
 
-/* Ends C function definitions when using C++ */
-#ifdef __cplusplus
-}
-#endif
-
 #endif /* defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT */