diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index f73d11e8bcd0..b7228c545056 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -19,15 +19,9 @@ #include #include -// Using XInput library for gamepad (with recent Windows SDK this may leads to executables which won't run on Windows 7) +// Using XInput library for gamepad #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD #include -#else -#define IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT -#endif -#if defined(_MSC_VER) && !defined(IMGUI_IMPL_WIN32_DISABLE_LINKING_XINPUT) -#pragma comment(lib, "xinput") -//#pragma comment(lib, "Xinput9_1_0") #endif // CHANGELOG @@ -65,6 +59,11 @@ static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT; static bool g_HasGamepad = false; static bool g_WantUpdateHasGamepad = true; +// XInput Data +static HMODULE g_hXInputDLL = NULL; +DWORD (__stdcall *g_fXInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*) = NULL; +DWORD (__stdcall *g_fXInputGetState)(DWORD, XINPUT_STATE*) = NULL; + // Functions bool ImGui_ImplWin32_Init(void* hwnd) { @@ -104,12 +103,43 @@ 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 +#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + const wchar_t* xinput_dll_name[] = { + L"xinput1_4.dll", // Windows 8+ + L"xinput1_3.dll", // DirectX SDK + L"xinput9_1_0.dll", // Windows xp, vista, 7... + }; + typedef DWORD (__stdcall *f_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*); + typedef DWORD (__stdcall *f_XInputGetState)(DWORD, XINPUT_STATE*); + for (size_t idx = 0; idx < 3; idx += 1) + { + HMODULE dll = LoadLibraryW(xinput_dll_name[idx]); + if (dll != NULL) + { + g_hXInputDLL = dll; + g_fXInputGetCapabilities = (f_XInputGetCapabilities)GetProcAddress(dll, "XInputGetCapabilities"); + g_fXInputGetState = (f_XInputGetState)GetProcAddress(dll, "XInputGetState"); + break; + } + } +#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + return true; } void ImGui_ImplWin32_Shutdown() { + // Gamepad, unload XInput library +#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + g_fXInputGetCapabilities = NULL; + g_fXInputGetState = NULL; + if (g_hXInputDLL) + FreeLibrary(g_hXInputDLL); + g_hXInputDLL = NULL; +#endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD + g_hWnd = (HWND)0; } @@ -175,43 +205,50 @@ 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) { - XINPUT_CAPABILITIES caps; - g_HasGamepad = (XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS); g_WantUpdateHasGamepad = false; + XINPUT_CAPABILITIES caps; + if (g_fXInputGetCapabilities) + g_HasGamepad = (g_fXInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS); + else + g_HasGamepad = false; } - - XINPUT_STATE xinput_state; + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; - if (g_HasGamepad && XInputGetState(0, &xinput_state) == ERROR_SUCCESS) + if (g_HasGamepad && g_fXInputGetState) { - 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; } - 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 - MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y - MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left - MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right - MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up - MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down - MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB - MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB - MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB - MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB - MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); - MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); - MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); - MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767); - #undef MAP_BUTTON - #undef MAP_ANALOG + XINPUT_STATE xinput_state; + if (g_fXInputGetState(0, &xinput_state) == ERROR_SUCCESS) + { + 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; } + 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 + MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y + MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left + MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right + MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up + MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down + MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB + MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB + MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB + MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB + MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); + MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); + MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); + MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767); + #undef MAP_BUTTON + #undef MAP_ANALOG + } } #endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD } diff --git a/examples/example_win32_directx11/build_win32.bat b/examples/example_win32_directx11/build_win32.bat index cbe96b0479ae..6b55f6bbfc6e 100644 --- a/examples/example_win32_directx11/build_win32.bat +++ b/examples/example_win32_directx11/build_win32.bat @@ -1,4 +1,4 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I .. /I ..\.. /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I "%DXSDK_DIR%Include" /D UNICODE /D _UNICODE *.cpp ..\..\backends\imgui_impl_dx11.cpp ..\..\backends\imgui_impl_win32.cpp ..\..\imgui*.cpp /FeDebug/example_win32_directx11.exe /FoDebug/ /link /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d11.lib d3dcompiler.lib +cl /nologo /Zi /MD /I ..\.. /I ..\..\backends /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I "%DXSDK_DIR%Include" /D UNICODE /D _UNICODE *.cpp ..\..\backends\imgui_impl_dx11.cpp ..\..\backends\imgui_impl_win32.cpp ..\..\imgui*.cpp /FeDebug/example_win32_directx11.exe /FoDebug/ /link /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d11.lib d3dcompiler.lib