Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamically load XInput library at runtime maybe a better choice #3645

Closed
Demonese opened this issue Dec 9, 2020 · 4 comments
Closed

Dynamically load XInput library at runtime maybe a better choice #3645

Demonese opened this issue Dec 9, 2020 · 4 comments

Comments

@Demonese
Copy link
Contributor

Demonese commented Dec 9, 2020

Version/Branch of Dear ImGui:

Version: any
Branch: master

Back-end/Renderer/Compiler/OS

Back-ends: imgui_impl_win32.cpp
Compiler: VS2019
Operating System: any

My Issue/Question:

My application designed to run on multiple Windows versions and load XInput library dynamically. So disable links to XInput library via macros will cause link error.
I rewrite the implement of Win32 XInput on my project. #3646

Screenshots/Video

none

Standalone, minimal, complete and verifiable example: (see #2261)
global datas:

static HMODULE           g_hXInputDLL                                                  = NULL;
static DWORD (__stdcall *g_fXInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*) = NULL;
static DWORD (__stdcall *g_fXInputGetState)(DWORD, XINPUT_STATE*)                      = NULL;
static XINPUT_STATE      g_tXInputState;

init:

ZeroMemory(&g_tXInputState, sizeof(XINPUT_STATE)); // clean it first
const wchar_t* xinput_dll_name[] = {
    L"xinput1_4.dll",   // Windows 8+
    L"xinput1_3.dll",   // DirectX SDK, Windows XP...
    L"xinput9_1_0.dll", // Windows 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;
    }
}

shutdown:

g_fXInputGetCapabilities = NULL;
g_fXInputGetState = NULL;
if (g_hXInputDLL)
    FreeLibrary(g_hXInputDLL);
g_hXInputDLL = NULL;
ZeroMemory(&g_tXInputState, sizeof(XINPUT_STATE));

update:

ImGuiIO& io = ImGui::GetIO();
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_tXInputState, sizeof(XINPUT_STATE)); // clear if no gamepad
}

io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
if (g_HasGamepad && g_fXInputGetState)
{
    if (g_fXInputGetState(0, &g_tXInputState) == ERROR_SUCCESS)
    {
        const XINPUT_GAMEPAD& gamepad = g_tXInputState.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
    }
}
@ocornut
Copy link
Owner

ocornut commented Dec 9, 2020

Linking to #3248, #3607
You are right maybe your approach would be simply better.

@ocornut
Copy link
Owner

ocornut commented Dec 9, 2020

PS: Instead of pasting code as images, you can paste code using a block starting with ```cpp

and ending with ```

@Xiliusha
Copy link

Xiliusha commented Dec 9, 2020

good, now I didn't need to make choise

@ocornut
Copy link
Owner

ocornut commented Jan 25, 2021

Closing this now (will merge #3646 asap). Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants