From f13984675099cf66485b6b5099f8a2028a542496 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 25 Jan 2021 16:19:43 +0100 Subject: [PATCH] Backends: Win32: dynamically load XInput library (amends) (#3646, #3645, #3248, #2716) --- backends/imgui_impl_win32.cpp | 81 ++++++++++++++++------------------- docs/CHANGELOG.txt | 2 + 2 files changed, 38 insertions(+), 45 deletions(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index f0bb7295..848dfab9 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -19,7 +19,7 @@ #include #include -// Using XInput library for gamepad +// Using XInput for gamepad (will load DLL dynamically) #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #include typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*); @@ -28,6 +28,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (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-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. @@ -61,13 +62,12 @@ static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT; static bool g_HasGamepad = false; static bool g_WantUpdateHasGamepad = true; -// XInput Data +// XInput DLL and functions #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD -static XINPUT_STATE g_XInputState; -static HMODULE g_hXInputDLL = NULL; -static PFN_XInputGetCapabilities g_fXInputGetCapabilities = NULL; -static PFN_XInputGetState g_fXInputGetState = NULL; -#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD +static HMODULE g_XInputDLL = NULL; +static PFN_XInputGetCapabilities g_XInputGetCapabilities = NULL; +static PFN_XInputGetState g_XInputGetState = NULL; +#endif // Functions bool ImGui_ImplWin32_Init(void* hwnd) @@ -108,28 +108,25 @@ bool ImGui_ImplWin32_Init(void* hwnd) io.KeyMap[ImGuiKey_X] = 'X'; io.KeyMap[ImGuiKey_Y] = 'Y'; io.KeyMap[ImGuiKey_Z] = 'Z'; - - // Gamepad, dynamically load XInput library + + // Dynamically load XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - const wchar_t* _XinputLibraryName[] = { - L"xinput1_4.dll", // Windows 8+ - L"xinput1_3.dll", // DirectX SDK - L"xinput9_1_0.dll", // Windows Vista, Windows 7 - L"xinput1_2.dll", // DirectX SDK - L"xinput1_1.dll", // DirectX SDK - NULL, - }; - for (size_t idx = 0; _XinputLibraryName[idx]; idx += 1) + const char* xinput_dll_names[] = { - if (HMODULE dll = ::LoadLibraryW(_XinputLibraryName[idx])) + "xinput1_4.dll", // Windows 8+ + "xinput1_3.dll", // DirectX SDK + "xinput9_1_0.dll", // Windows Vista, Windows 7 + "xinput1_2.dll", // DirectX SDK + "xinput1_1.dll" // DirectX SDK + }; + for (int n = 0; n < IM_ARRAYSIZE(xinput_dll_names); n++) + if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n])) { - g_hXInputDLL = dll; - g_fXInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities"); - g_fXInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState"); + g_XInputDLL = dll; + g_XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities"); + g_XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState"); break; } - } - ZeroMemory(&g_XInputState, sizeof(XINPUT_STATE)); #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD return true; @@ -137,14 +134,13 @@ bool ImGui_ImplWin32_Init(void* hwnd) void ImGui_ImplWin32_Shutdown() { - // Gamepad, unload XInput library + // Unload XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - if (g_hXInputDLL) - ::FreeLibrary(g_hXInputDLL); - ZeroMemory(&g_XInputState, sizeof(XINPUT_STATE)); - g_hXInputDLL = NULL; - g_fXInputGetCapabilities = NULL; - g_fXInputGetState = NULL; + if (g_XInputDLL) + ::FreeLibrary(g_XInputDLL); + g_XInputDLL = NULL; + g_XInputGetCapabilities = NULL; + g_XInputGetState = NULL; #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD g_hWnd = NULL; @@ -217,30 +213,25 @@ static void ImGui_ImplWin32_UpdateGamepads() memset(io.NavInputs, 0, sizeof(io.NavInputs)); if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) return; - + // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow. // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. if (g_WantUpdateHasGamepad) { - g_WantUpdateHasGamepad = false; XINPUT_CAPABILITIES caps; - if (g_fXInputGetCapabilities) - g_HasGamepad = (g_fXInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS); - else - g_HasGamepad = false; - if (!g_HasGamepad) - ZeroMemory(&g_XInputState, sizeof(XINPUT_STATE)); // clear if no gamepad + g_HasGamepad = g_XInputGetCapabilities ? (g_XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false; + g_WantUpdateHasGamepad = false; } - + 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; + #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);\ - if (vn > 1.0f) vn = 1.0f;\ - if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; } + #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; } MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0bc4f694..f2d3463e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -39,6 +39,8 @@ Other Changes: - ImDrawList: Fixed AddCircle()/AddCircleFilled() with (rad > 0.0f && rad < 1.0f && num_segments == 0). (#3738) 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]