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.
This commit is contained in:
Sam Lantinga
2026-06-10 12:57:43 -07:00
committed by GitHub
parent 10620ec33e
commit 36f621842b
11 changed files with 124 additions and 62 deletions

View File

@@ -12,7 +12,6 @@ from typing import Optional
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 @@ class JobDetails:
"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:

View File

@@ -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

View File

@@ -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)

View File

@@ -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>

View File

@@ -175,7 +175,7 @@ class VisualStudio:
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 @@ class VisualStudio:
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 @@ class Releaser:
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 @@ class Releaser:
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:

View File

@@ -7,24 +7,27 @@ import urllib.request
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())

View File

@@ -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"
}
},

View File

@@ -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()

View File

@@ -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

View File

@@ -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;

View File

@@ -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