From 36f621842b9aa5b07fdf5820b59223f1d2a5f584 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Wed, 10 Jun 2026 12:57:43 -0700
Subject: [PATCH] Use the Microsoft provided GameInputCreate() function
(#15797)
It does better version checking and has better compatibility.
This also fixes a crash in GameInputRedist.dll when attempting to load v3 when v2 is installed on the system. In this case, a thread is created in GameInputCreate() which is not cleaned up when the object is released, and can crash later with a NULL pointer dereference.
---
.github/workflows/create-test-plan.py | 20 ++++---
.github/workflows/generic.yml | 13 +++--
CMakeLists.txt | 36 +++++++++++--
VisualC-GDK/SDL/SDL.vcxproj | 12 ++---
build-scripts/build-release.py | 12 +++--
...t-headers.py => download-gameinput-sdk.py} | 21 ++++----
build-scripts/release-info.json | 2 +-
cmake/sdlcommands.cmake | 4 +-
include/build_config/SDL_build_config.h.cmake | 1 +
src/core/windows/SDL_gameinput.cpp | 54 ++++++++++++-------
src/core/windows/SDL_gameinput.h | 11 ++--
11 files changed, 124 insertions(+), 62 deletions(-)
rename build-scripts/{download-gameinput-headers.py => download-gameinput-sdk.py} (62%)
diff --git a/.github/workflows/create-test-plan.py b/.github/workflows/create-test-plan.py
index 963c06bde1558..941a84ff24588 100755
--- a/.github/workflows/create-test-plan.py
+++ b/.github/workflows/create-test-plan.py
@@ -12,7 +12,6 @@
logger = logging.getLogger(__name__)
-WINDOWS_GAMEINPUT_VERSION = "v3.3.195.0 "
class AppleArch(Enum):
Aarch64 = "aarch64"
@@ -222,7 +221,8 @@ class JobDetails:
msys2_packages: list[str] = dataclasses.field(default_factory=list)
cygwin_packages: list[str] = dataclasses.field(default_factory=list)
werror: bool = True
- microsoft_gameinput_version: str = ""
+ microsoft_gameinput: bool = False
+ microsoft_gameinput_arch: str = ""
msvc_vcvars_arch: str = ""
msvc_vcvars_sdk: str = ""
msvc_project: str = ""
@@ -293,7 +293,8 @@ def to_workflow(self, enable_artifacts: bool) -> dict[str, str|bool]:
"android-mk": self.android_mk,
"werror": self.werror,
"sudo": self.sudo,
- "microsoft-gameinput-version": self.microsoft_gameinput_version,
+ "microsoft-gameinput": self.microsoft_gameinput,
+ "microsoft-gameinput-arch": self.microsoft_gameinput_arch,
"msvc-vcvars-arch": self.msvc_vcvars_arch,
"msvc-vcvars-sdk": self.msvc_vcvars_sdk,
"msvc-project": self.msvc_project,
@@ -445,9 +446,14 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool, ctest_args
job.setup_libusb_arch = "x86"
case MsvcArch.X64:
job.setup_libusb_arch = "x64"
- job.microsoft_gameinput_version = WINDOWS_GAMEINPUT_VERSION
- job.cflags.append("-I$GAMEINPUT_INCLUDE")
- job.cxxflags.append("-I$GAMEINPUT_INCLUDE")
+ job.microsoft_gameinput = True
+ match spec.msvc_arch:
+ case MsvcArch.X64:
+ job.microsoft_gameinput_arch = "x64"
+ case MsvcArch.Arm64:
+ job.microsoft_gameinput_arch = "arm64"
+ job.cflags.append("-I$GAMEINPUT_INCLUDE")
+ job.cxxflags.append("-I$GAMEINPUT_INCLUDE")
case SdlPlatform.Linux:
if spec.name.startswith("Ubuntu"):
assert spec.os.value.startswith("ubuntu-")
@@ -775,7 +781,7 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool, ctest_args
job.msys2_packages.append(f"{msys2_env}-clang-tools-extra")
if job.ccache:
job.msys2_packages.append(f"{msys2_env}-ccache")
- job.microsoft_gameinput_version = WINDOWS_GAMEINPUT_VERSION
+ job.microsoft_gameinput = True
job.cflags.append("-I$GAMEINPUT_INCLUDE")
job.cxxflags.append("-I$GAMEINPUT_INCLUDE")
case SdlPlatform.Cygwin:
diff --git a/.github/workflows/generic.yml b/.github/workflows/generic.yml
index 928ee1f9e0dd2..ccfdb1da3b8ef 100644
--- a/.github/workflows/generic.yml
+++ b/.github/workflows/generic.yml
@@ -173,14 +173,12 @@ jobs:
done
done
- name: 'Set up Microsoft.GameInput headers'
- if: ${{ matrix.platform.microsoft-gameinput-version != '' }}
+ if: ${{ !!matrix.platform.microsoft-gameinput }}
run: |
- python build-scripts/download-gameinput-headers.py \
- --version ${{ matrix.platform.microsoft-gameinput-version }} \
- -o $HOME/gameinput
- echo "GAMEINPUT_INCLUDE=$(cygpath -w "$HOME/gameinput")" >>$GITHUB_ENV
- echo "INCLUDE=$INCLUDE;$(cygpath -w "$HOME/gameinput")" >>$GITHUB_ENV
-
+ python build-scripts/download-gameinput-sdk.py -o $HOME/gameinput
+ echo "GAMEINPUT_INCLUDE=$(cygpath -w "$HOME/gameinput/include")" >>$GITHUB_ENV
+ echo "INCLUDE=$(cygpath -w "$HOME/gameinput/include");$INCLUDE" >>$GITHUB_ENV
+ ${{ (!!matrix.platform.microsoft-gameinput-arch && format('echo "LIB=$(cygpath -w "$HOME/gameinput/lib/{0}");$LIB" >>$GITHUB_ENV', matrix.platform.microsoft-gameinput-arch)) || '' }}
- name: 'Calculate ccache key'
if: ${{ matrix.platform.ccache }}
id: prepare-restore-ccache
@@ -439,6 +437,7 @@ jobs:
path: |
build/dist/SDL3*
build/include*
+ build/CMakeFiles/CMakeConfigureLog.yaml
- name: 'Upload minidumps'
uses: actions/upload-artifact@v7
continue-on-error: true
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5c61b5383259a..99962e327d235 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -938,7 +938,7 @@ if(SDL_ASSEMBLY)
}]==] COMPILER_SUPPORTS_ARMSVE2)
if(COMPILER_SUPPORTS_ARMSVE2)
# IMPORTANT: As not all AArch64 processors support SVE2, we only
- # attach the following compilation option to SVE
+ # attach the following compilation option to SVE
# dedicated source files.
set(SVE2_MARCH_FLAG "-march=armv8-a+sve2")
set(HAVE_ARMSVE2 TRUE)
@@ -2313,12 +2313,42 @@ elseif(WINDOWS OR CYGWIN)
static __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2 *s2;
int main(int argc, char **argv) { return 0; }" HAVE_WINDOWS_GAMING_INPUT_H
)
+
check_cxx_source_compiles("
- #include <stdbool.h>
- #define COBJMACROS
#include <gameinput.h>
int main(int argc, char **argv) { return 0; }" HAVE_GAMEINPUT_H
)
+ if(HAVE_GAMEINPUT_H)
+ set(SDL_GAMEINPUT_DYNAMIC 1)
+ find_library(GAMEINPUT_LIB NAMES "gameinput.lib")
+ if(GAMEINPUT_LIB)
+ cmake_push_check_state()
+ list(APPEND CMAKE_REQUIRED_LIBRARIES "${GAMEINPUT_LIB}")
+ check_cxx_source_compiles("
+ #include <windows.h>
+ #include <gameinput.h>
+ #if defined(GAMEINPUT_API_VERSION) && GAMEINPUT_API_VERSION > 0
+ #define STR_JOIN2(A, B) A##B
+ #define STR_JOIN(A, B) STR_JOIN2(A, B)
+ using namespace GameInput::STR_JOIN(v, GAMEINPUT_API_VERSION);
+ #endif
+ int main(int argc, char **argv) {
+ IGameInput *gameInput;
+ HRESULT hr = GameInputCreate(&gameInput);
+ if (SUCCEEDED(hr)) {
+ gameInput->Release();
+ }
+ (void) argc; (void) argv;
+ return 0;
+ }" HAVE_GAMEINPUT_LIB
+ )
+ if(HAVE_GAMEINPUT_LIB)
+ sdl_link_dependency(gameinput LIBS gameinput.lib PKG_CONFIG_LINK_OPTIONS -lgameinput)
+ set(SDL_GAMEINPUT_DYNAMIC 0)
+ endif()
+ endif()
+ endif()
+
check_include_file(dxgi1_5.h HAVE_DXGI1_5_H)
check_include_file(dxgi1_6.h HAVE_DXGI1_6_H)
check_include_file(tpcshrd.h HAVE_TPCSHRD_H)
diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index d09e24c5d07a6..b116c521c1bd0 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -132,7 +132,7 @@
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<Link>
- <AdditionalDependencies>setupapi.lib;winmm.lib;imm32.lib;version.lib;xgameruntime.lib;vcruntimed.lib;msvcrtd.lib;ucrtd.lib;msvcprtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies>gameinput.lib;setupapi.lib;winmm.lib;imm32.lib;version.lib;xgameruntime.lib;vcruntimed.lib;msvcrtd.lib;ucrtd.lib;msvcprtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
@@ -161,7 +161,7 @@
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<Link>
- <AdditionalDependencies>setupapi.lib;winmm.lib;imm32.lib;version.lib;xgameruntime.lib;d3d12_xs.lib;uuid.lib;vcruntimed.lib;msvcrtd.lib;ucrtd.lib;msvcprtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies>gameinput.lib;setupapi.lib;winmm.lib;imm32.lib;version.lib;xgameruntime.lib;d3d12_xs.lib;uuid.lib;vcruntimed.lib;msvcrtd.lib;ucrtd.lib;msvcprtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
@@ -199,7 +199,7 @@
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<Link>
- <AdditionalDependencies>setupapi.lib;winmm.lib;imm32.lib;version.lib;xgameruntime.lib;d3d12_x.lib;uuid.lib;vcruntimed.lib;msvcrtd.lib;ucrtd.lib;msvcprtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies>gameinput.lib;setupapi.lib;winmm.lib;imm32.lib;version.lib;xgameruntime.lib;d3d12_x.lib;uuid.lib;vcruntimed.lib;msvcrtd.lib;ucrtd.lib;msvcprtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
@@ -238,7 +238,7 @@
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<Link>
- <AdditionalDependencies>setupapi.lib;winmm.lib;imm32.lib;version.lib;xgameruntime.lib;vcruntime.lib;msvcrt.lib;ucrt.lib;msvcprt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies>gameinput.lib;setupapi.lib;winmm.lib;imm32.lib;version.lib;xgameruntime.lib;vcruntime.lib;msvcrt.lib;ucrt.lib;msvcprt.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
@@ -268,7 +268,7 @@
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<Link>
- <AdditionalDependencies>setupapi.lib;winmm.lib;imm32.lib;version.lib;xgameruntime.lib;d3d12_xs.lib;uuid.lib;vcruntime.lib;msvcrt.lib;ucrt.lib;msvcprt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies>gameinput.lib;setupapi.lib;winmm.lib;imm32.lib;version.lib;xgameruntime.lib;d3d12_xs.lib;uuid.lib;vcruntime.lib;msvcrt.lib;ucrt.lib;msvcprt.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
@@ -307,7 +307,7 @@
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<Link>
- <AdditionalDependencies>setupapi.lib;winmm.lib;imm32.lib;version.lib;xgameruntime.lib;d3d12_x.lib;uuid.lib;vcruntime.lib;msvcrt.lib;ucrt.lib;msvcprt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies>gameinput.lib;setupapi.lib;winmm.lib;imm32.lib;version.lib;xgameruntime.lib;d3d12_x.lib;uuid.lib;vcruntime.lib;msvcrt.lib;ucrt.lib;msvcprt.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
diff --git a/build-scripts/build-release.py b/build-scripts/build-release.py
index 4e153a1bf996e..2201edb7a241d 100755
--- a/build-scripts/build-release.py
+++ b/build-scripts/build-release.py
@@ -175,7 +175,7 @@ def find_msbuild(self) -> typing.Optional[Path]:
assert msbuild_path.is_file(), "MSBuild.exe does not exist"
return msbuild_path
- def build(self, arch_platform: VsArchPlatformConfig, projects: list[Path], include_paths: list[str]):
+ def build(self, arch_platform: VsArchPlatformConfig, projects: list[Path], include_paths: list[str], lib_paths: list[str]):
assert projects, "Need at least one project to build"
vsdev_cmd_str = f"\"{self.vsdevcmd}\" -arch={arch_platform.arch}"
@@ -183,7 +183,10 @@ def build(self, arch_platform: VsArchPlatformConfig, projects: list[Path], inclu
include_contents = "%INCLUDE%"
if include_paths:
include_contents = f"{';'.join(str(p) for p in include_paths)};" + include_contents
- bat_contents = textwrap.dedent(f"{vsdev_cmd_str} && set INCLUDE={include_contents} && {msbuild_cmd_str}")
+ lib_contents = "%LIB%"
+ if lib_paths:
+ lib_contents = f"{';'.join(str(p) for p in lib_paths)};" + lib_contents
+ bat_contents = textwrap.dedent(f"{vsdev_cmd_str} && set INCLUDE={include_contents} && set LIB={lib_paths} && {msbuild_cmd_str}")
bat_path = Path(tempfile.gettempdir()) / "cmd.bat"
with bat_path.open("w") as f:
f.write(bat_contents)
@@ -1218,9 +1221,12 @@ def build_msvc(self):
def _build_msvc_msbuild(self, arch_platform: VsArchPlatformConfig, vs: VisualStudio):
platform_context = self.get_context(arch_platform.extra_context())
include_paths = []
+ lib_paths = []
for dep in self.release_info.get("dependencies", {}):#release_info["msvc"].get("dependencies", {}).items():
if "command" in self.release_info["dependencies"][dep]:
include_paths.append(self.deps_path / dep / "include")
+ lib_paths.append(self.deps_path / dep / "lib")
+ lib_paths.append(self.deps_path / dep / "lib" / arch_platform.arch)
continue
depinfo = self.release_info["msvc"]["dependencies"][dep]
msvc_zip = self.deps_path / glob.glob(depinfo["artifact"], root_dir=self.deps_path)[0]
@@ -1270,7 +1276,7 @@ def _build_msvc_msbuild(self, arch_platform: VsArchPlatformConfig, vs: VisualStu
shutil.copy(src=src, dst=dir_b_props)
with self.section_printer.group(f"Build {arch_platform.arch} VS binary"):
- vs.build(arch_platform=arch_platform, projects=projects, include_paths=include_paths)
+ vs.build(arch_platform=arch_platform, projects=projects, include_paths=include_paths, lib_paths=lib_paths)
if self.dry:
for b in built_paths:
diff --git a/build-scripts/download-gameinput-headers.py b/build-scripts/download-gameinput-sdk.py
similarity index 62%
rename from build-scripts/download-gameinput-headers.py
rename to build-scripts/download-gameinput-sdk.py
index 13d50028c0859..8a85d551997c3 100755
--- a/build-scripts/download-gameinput-headers.py
+++ b/build-scripts/download-gameinput-sdk.py
@@ -7,24 +7,27 @@
logger = logging.getLogger(__name__)
-def download_headers(tag: str, lowercase: bool, output: Path):
+DEFAULT_GAMEINPUT_VERSION = "v3.3.195.0"
+
+def download_sdk(tag: str, lowercase: bool, output: Path):
base_url = f"https://raw.githubusercontent.com/microsoftconnect/GameInput/refs/tags/{tag}/"
url_relpaths = (
"include/GameInput.h",
"include/v0/GameInput.h",
"include/v1/GameInput.h",
"include/v2/GameInput.h",
+ "lib/arm64/GameInput.lib",
+ "lib/x64/GameInput.lib",
)
- remove_prefix = "include/"
for url_relpath in url_relpaths:
url = base_url + url_relpath
- local_relpath = url_relpath.removeprefix(remove_prefix)
+ local_relpath = url_relpath
if lowercase:
local_relpath = local_relpath.lower()
local_path = output / local_relpath
local_dirpath = local_path.parent
- local_dirpath.mkdir(parents=False, exist_ok=True)
+ local_dirpath.mkdir(parents=True, exist_ok=True)
logger.info("Downloading %s to %s...", url, local_path)
urllib.request.urlretrieve(url, local_path)
@@ -33,12 +36,12 @@ def download_headers(tag: str, lowercase: bool, output: Path):
def main():
logging.basicConfig(level=logging.INFO)
- parser = argparse.ArgumentParser(description="Download Microsoft.GameInput headers", allow_abbrev=False)
- parser.add_argument("--version", required=True, help="GameInput release tag (see https://github.com/microsoftconnect/GameInput/tags)")
- parser.add_argument("--no-lowercase", action="store_false", dest="lowercase", help="Don't lowercase downloaded headers")
- parser.add_argument("-o", "--output", type=Path, default=Path.cwd(), help="Headers will be stored here (subdirectories created as ")
+ parser = argparse.ArgumentParser(description="Download Microsoft.GameInput SDK", allow_abbrev=False)
+ parser.add_argument("--version", help="GameInput release tag (see https://github.com/microsoftconnect/GameInput/tags)", default=DEFAULT_GAMEINPUT_VERSION)
+ parser.add_argument("--no-lowercase", action="store_false", dest="lowercase", help="Don't lowercase downloaded files")
+ parser.add_argument("-o", "--output", type=Path, default=Path.cwd(), help="SDK will be stored here (in include and lib subdirectories)")
args = parser.parse_args()
- download_headers(tag=args.version, lowercase=args.lowercase, output=args.output)
+ download_sdk(tag=args.version, lowercase=args.lowercase, output=args.output)
if __name__ == "__main__":
raise SystemExit(main())
diff --git a/build-scripts/release-info.json b/build-scripts/release-info.json
index e70683bc3a35b..e9e30e1d164bb 100644
--- a/build-scripts/release-info.json
+++ b/build-scripts/release-info.json
@@ -3,7 +3,7 @@
"remote": "libsdl-org/SDL",
"dependencies": {
"gameinput": {
- "command": "build-scripts/download-gameinput-headers.py -o @<@DEPS_PATH@>@/include --version v3.3.195.0",
+ "command": "build-scripts/download-gameinput-sdk.py -o @<@DEPS_PATH@>@",
"artifact": "gameinput/include/gameinput.h"
}
},
diff --git a/cmake/sdlcommands.cmake b/cmake/sdlcommands.cmake
index 58889d19f3cfe..1f781bc9dcb2c 100644
--- a/cmake/sdlcommands.cmake
+++ b/cmake/sdlcommands.cmake
@@ -65,11 +65,12 @@ endfunction()
# - LIBS: list of libraries to link to (cmake and pkg-config)
# - LINK_OPTIONS: list of link options (also used in pc file, unless PKG_CONFIG_LINK_OPTION is used)
function(sdl_generic_link_dependency ID)
- cmake_parse_arguments(ARGS "" "COLLECTOR" "SHARED_TARGETS;STATIC_TARGETS;INCLUDES;PKG_CONFIG_LINK_OPTIONS;PKG_CONFIG_LIBS;PKG_CONFIG_PREFIX;PKG_CONFIG_SPECS;CMAKE_MODULE;LIBS;LINK_OPTIONS" ${ARGN})
+ cmake_parse_arguments(ARGS "" "COLLECTOR" "SHARED_TARGETS;STATIC_TARGETS;INCLUDES;PKG_CONFIG_LINK_OPTIONS;PKG_CONFIG_LIBS;PKG_CONFIG_PREFIX;PKG_CONFIG_SPECS;CMAKE_MODULE;PUBLIC_LIBS;LIBS;LINK_OPTIONS" ${ARGN})
foreach(target IN LISTS ARGS_SHARED_TARGETS)
if(TARGET ${target})
target_include_directories(${target} SYSTEM PRIVATE ${ARGS_INCLUDES})
target_link_libraries(${target} PRIVATE ${ARGS_LIBS})
+ target_link_libraries(${target} PUBLIC ${ARGS_PUBLIC_LIBS})
target_link_options(${target} PRIVATE ${ARGS_LINK_OPTIONS})
endif()
endforeach()
@@ -77,6 +78,7 @@ function(sdl_generic_link_dependency ID)
if(TARGET ${target})
target_include_directories(${target} SYSTEM PRIVATE ${ARGS_INCLUDES})
target_link_libraries(${target} PRIVATE ${ARGS_LIBS})
+ target_link_libraries(${target} PUBLIC ${ARGS_PUBLIC_LIBS})
target_link_options(${target} INTERFACE ${ARGS_LINK_OPTIONS})
endif()
endforeach()
diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake
index 341b9e689efce..a93090f8908ee 100644
--- a/include/build_config/SDL_build_config.h.cmake
+++ b/include/build_config/SDL_build_config.h.cmake
@@ -231,6 +231,7 @@
#cmakedefine HAVE_XINPUT_H 1
#cmakedefine HAVE_WINDOWS_GAMING_INPUT_H 1
#cmakedefine HAVE_GAMEINPUT_H 1
+#cmakedefine SDL_GAMEINPUT_DYNAMIC 1
#cmakedefine HAVE_DXGI_H 1
#cmakedefine HAVE_DXGI1_5_H 1
#cmakedefine HAVE_DXGI1_6_H 1
diff --git a/src/core/windows/SDL_gameinput.cpp b/src/core/windows/SDL_gameinput.cpp
index f9996916a573a..225407552a59f 100644
--- a/src/core/windows/SDL_gameinput.cpp
+++ b/src/core/windows/SDL_gameinput.cpp
@@ -25,6 +25,13 @@
#ifdef HAVE_GAMEINPUT_H
+#ifndef SDL_GAMEINPUT_DYNAMIC
+#define USE_GAMEINPUT_LIB
+#ifdef _MSC_VER
+#pragma comment(lib, "gameinput.lib")
+#endif
+#endif // !SDL_GAMEINPUT_DYNAMIC
+
static SDL_SharedObject *g_hGameInputDLL;
static IGameInput *g_pGameInput;
static int g_nGameInputRefCount;
@@ -33,6 +40,32 @@ static int g_nGameInputRefCount;
bool SDL_InitGameInput(IGameInput **ppGameInput)
{
if (g_nGameInputRefCount == 0) {
+#ifdef USE_GAMEINPUT_LIB
+ // This is recommended, as Microsoft's GameInputCreate() is robust
+ // and better handles various GameInput installations
+ HRESULT hr = GameInputCreate(&g_pGameInput);
+ if (FAILED(hr)) {
+ return WIN_SetErrorFromHRESULT("GameInputCreate failed", hr);
+ }
+#elif GAMEINPUT_API_VERSION > 0
+ g_hGameInputDLL = SDL_LoadObject("gameinputredist.dll");
+ if (!g_hGameInputDLL) {
+ return false;
+ }
+
+ typedef HRESULT (WINAPI *pfnGameInputInitialize)(REFIID riid, void **ppvObject);
+ pfnGameInputInitialize pGameInputInitialize = (pfnGameInputInitialize)SDL_LoadFunction(g_hGameInputDLL, "GameInputInitialize");
+ if (!pGameInputInitialize) {
+ SDL_UnloadObject(g_hGameInputDLL);
+ return false;
+ }
+
+ HRESULT hr = pGameInputInitialize(IID_IGameInput, (void **)&g_pGameInput);
+ if (FAILED(hr)) {
+ SDL_UnloadObject(g_hGameInputDLL);
+ return WIN_SetErrorFromHRESULT("GameInputInitialize failed", hr);
+ }
+#else
g_hGameInputDLL = SDL_LoadObject("gameinput.dll");
if (!g_hGameInputDLL) {
return false;
@@ -45,29 +78,12 @@ bool SDL_InitGameInput(IGameInput **ppGameInput)
return false;
}
- IGameInput *pGameInput = NULL;
- HRESULT hr = pGameInputCreate(&pGameInput);
+ HRESULT hr = pGameInputCreate(&g_pGameInput);
if (FAILED(hr)) {
SDL_UnloadObject(g_hGameInputDLL);
return WIN_SetErrorFromHRESULT("GameInputCreate failed", hr);
}
-
-#ifdef SDL_PLATFORM_WIN32
-#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
- g_pGameInput = pGameInput;
-#endif
+#endif // USE_GAMEINPUT_LIB
}
++g_nGameInputRefCount;
diff --git a/src/core/windows/SDL_gameinput.h b/src/core/windows/SDL_gameinput.h
index ea16b85dce6c8..2b01e61e73cb2 100644
--- a/src/core/windows/SDL_gameinput.h
+++ b/src/core/windows/SDL_gameinput.h
@@ -40,12 +40,11 @@
#define GAMEINPUT_API_VERSION 0
#endif
-#if GAMEINPUT_API_VERSION == 3
-using namespace GameInput::v3;
-#elif GAMEINPUT_API_VERSION == 2
-using namespace GameInput::v2;
-#elif GAMEINPUT_API_VERSION == 1
-using namespace GameInput::v1;
+#if GAMEINPUT_API_VERSION > 0
+// Use the namespace of the current GameInput version
+#define STR_JOIN2(A, B) A##B
+#define STR_JOIN(A, B) STR_JOIN2(A, B)
+using namespace GameInput::STR_JOIN(v, GAMEINPUT_API_VERSION);
#endif
// Default value for SDL_HINT_JOYSTICK_GAMEINPUT