Backends: Win32: dynamically load XInput library (amends) (#3646, #3645, #3248, #2716)

This commit is contained in:
ocornut 2021-01-25 16:19:43 +01:00
parent 633d1033af
commit f139846750
2 changed files with 38 additions and 45 deletions

View File

@ -19,7 +19,7 @@
#include <windows.h> #include <windows.h>
#include <tchar.h> #include <tchar.h>
// Using XInput library for gamepad // Using XInput for gamepad (will load DLL dynamically)
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
#include <XInput.h> #include <XInput.h>
typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*); typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*);
@ -28,6 +28,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2021-01-25: Inputs: Dynamically loading XInput DLL.
// 2020-12-04: Misc: Fixed setting of io.DisplaySize to invalid/uninitialized data when after hwnd has been closed. // 2020-12-04: Misc: Fixed setting of io.DisplaySize to invalid/uninitialized data when after hwnd has been closed.
// 2020-03-03: Inputs: Calling AddInputCharacterUTF16() to support surrogate pairs leading to codepoint >= 0x10000 (for more complete CJK inputs) // 2020-03-03: Inputs: Calling AddInputCharacterUTF16() to support surrogate pairs leading to codepoint >= 0x10000 (for more complete CJK inputs)
// 2020-02-17: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), ImGui_ImplWin32_GetDpiScaleForMonitor() helper functions. // 2020-02-17: Added ImGui_ImplWin32_EnableDpiAwareness(), ImGui_ImplWin32_GetDpiScaleForHwnd(), ImGui_ImplWin32_GetDpiScaleForMonitor() helper functions.
@ -61,13 +62,12 @@ static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
static bool g_HasGamepad = false; static bool g_HasGamepad = false;
static bool g_WantUpdateHasGamepad = true; static bool g_WantUpdateHasGamepad = true;
// XInput Data // XInput DLL and functions
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
static XINPUT_STATE g_XInputState; static HMODULE g_XInputDLL = NULL;
static HMODULE g_hXInputDLL = NULL; static PFN_XInputGetCapabilities g_XInputGetCapabilities = NULL;
static PFN_XInputGetCapabilities g_fXInputGetCapabilities = NULL; static PFN_XInputGetState g_XInputGetState = NULL;
static PFN_XInputGetState g_fXInputGetState = NULL; #endif
#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
// Functions // Functions
bool ImGui_ImplWin32_Init(void* hwnd) bool ImGui_ImplWin32_Init(void* hwnd)
@ -109,27 +109,24 @@ bool ImGui_ImplWin32_Init(void* hwnd)
io.KeyMap[ImGuiKey_Y] = 'Y'; io.KeyMap[ImGuiKey_Y] = 'Y';
io.KeyMap[ImGuiKey_Z] = 'Z'; io.KeyMap[ImGuiKey_Z] = 'Z';
// Gamepad, dynamically load XInput library // Dynamically load XInput library
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
const wchar_t* _XinputLibraryName[] = { const char* xinput_dll_names[] =
L"xinput1_4.dll", // Windows 8+ {
L"xinput1_3.dll", // DirectX SDK "xinput1_4.dll", // Windows 8+
L"xinput9_1_0.dll", // Windows Vista, Windows 7 "xinput1_3.dll", // DirectX SDK
L"xinput1_2.dll", // DirectX SDK "xinput9_1_0.dll", // Windows Vista, Windows 7
L"xinput1_1.dll", // DirectX SDK "xinput1_2.dll", // DirectX SDK
NULL, "xinput1_1.dll" // DirectX SDK
}; };
for (size_t idx = 0; _XinputLibraryName[idx]; idx += 1) for (int n = 0; n < IM_ARRAYSIZE(xinput_dll_names); n++)
if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n]))
{ {
if (HMODULE dll = ::LoadLibraryW(_XinputLibraryName[idx])) g_XInputDLL = dll;
{ g_XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities");
g_hXInputDLL = dll; g_XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState");
g_fXInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities");
g_fXInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState");
break; break;
} }
}
ZeroMemory(&g_XInputState, sizeof(XINPUT_STATE));
#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
return true; return true;
@ -137,14 +134,13 @@ bool ImGui_ImplWin32_Init(void* hwnd)
void ImGui_ImplWin32_Shutdown() void ImGui_ImplWin32_Shutdown()
{ {
// Gamepad, unload XInput library // Unload XInput library
#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
if (g_hXInputDLL) if (g_XInputDLL)
::FreeLibrary(g_hXInputDLL); ::FreeLibrary(g_XInputDLL);
ZeroMemory(&g_XInputState, sizeof(XINPUT_STATE)); g_XInputDLL = NULL;
g_hXInputDLL = NULL; g_XInputGetCapabilities = NULL;
g_fXInputGetCapabilities = NULL; g_XInputGetState = NULL;
g_fXInputGetState = NULL;
#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
g_hWnd = NULL; g_hWnd = NULL;
@ -222,25 +218,20 @@ static void ImGui_ImplWin32_UpdateGamepads()
// Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE.
if (g_WantUpdateHasGamepad) if (g_WantUpdateHasGamepad)
{ {
g_WantUpdateHasGamepad = false;
XINPUT_CAPABILITIES caps; XINPUT_CAPABILITIES caps;
if (g_fXInputGetCapabilities) g_HasGamepad = g_XInputGetCapabilities ? (g_XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false;
g_HasGamepad = (g_fXInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS); g_WantUpdateHasGamepad = false;
else
g_HasGamepad = false;
if (!g_HasGamepad)
ZeroMemory(&g_XInputState, sizeof(XINPUT_STATE)); // clear if no gamepad
} }
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
if (g_HasGamepad && g_fXInputGetState && (g_fXInputGetState(0, &g_XInputState) == ERROR_SUCCESS)) XINPUT_STATE xinput_state;
if (g_HasGamepad && g_XInputGetState && g_XInputGetState(0, &xinput_state) == ERROR_SUCCESS)
{ {
const XINPUT_GAMEPAD& gamepad = g_XInputState.Gamepad; const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad;
io.BackendFlags |= ImGuiBackendFlags_HasGamepad; io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
#define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; } #define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; }
#define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0);\ #define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
if (vn > 1.0f) vn = 1.0f;\
if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A
MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B
MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X

View File

@ -39,6 +39,8 @@ Other Changes:
- ImDrawList: Fixed AddCircle()/AddCircleFilled() with (rad > 0.0f && rad < 1.0f && num_segments == 0). (#3738) - ImDrawList: Fixed AddCircle()/AddCircleFilled() with (rad > 0.0f && rad < 1.0f && num_segments == 0). (#3738)
Would lead to a buffer read overflow. Would lead to a buffer read overflow.
- Backends: Win32: Dynamically loading XInput DLL instead of linking with it, facilite compiling with
old WindowSDK versions or running on Windows 7. (#3646, #3645, #3248, #2716) [@Demonese]
- Backends: Metal: Fixed texture storage mode when building on Mac Catalyst. (#3748) [@Belinsky-L-V] - Backends: Metal: Fixed texture storage mode when building on Mac Catalyst. (#3748) [@Belinsky-L-V]