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

GLFW: Added support for multiple gamepads and preventing devices (such as … #4408

Closed
wants to merge 1 commit into from

Conversation

PeterSilie24
Copy link

I have encountered several problems using gamepads with imgui:

  • It is not possible to use multiple gamepads.
  • The GLFW backend incorrectly reports my mouse and keyboard as joysticks on Linux and therefore prevents me from using my gamepad.
  • The current implementation of the gamepad dpad buttons for GLFW does not work on Linux.
    This is probably a result of a bug in GLFW.
    If we want to retrieve the gamepad state on Linux with the code below, we will only be able to navigate downwards with dpad right and we can only get up by pressing the right stick.
    Apparently buttons is indexed differently on (Manjaro) Linux and Windows.
#define MAP_BUTTON(NAV_NO, BUTTON_NO)       { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; }
#define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; }
int axes_count = 0, buttons_count = 0;
const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count);
const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count);
...
MAP_BUTTON(ImGuiNavInput_DpadLeft,   13);    // D-Pad Left
MAP_BUTTON(ImGuiNavInput_DpadRight,  11);    // D-Pad Right
MAP_BUTTON(ImGuiNavInput_DpadUp,     10);    // D-Pad Up
MAP_BUTTON(ImGuiNavInput_DpadDown,   12);    // D-Pad Down
...
#undef MAP_BUTTON
  • If we change this as follows, dpad navigation will work fine (on Linux).
...
MAP_BUTTON(ImGuiNavInput_DpadLeft, GLFW_GAMEPAD_BUTTON_DPAD_LEFT);
MAP_BUTTON(ImGuiNavInput_DpadRight, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT);
MAP_BUTTON(ImGuiNavInput_DpadUp, GLFW_GAMEPAD_BUTTON_DPAD_UP);
MAP_BUTTON(ImGuiNavInput_DpadDown, GLFW_GAMEPAD_BUTTON_DPAD_DOWN);
...
  • Since we are only interested in gamepads, we can use glfwGetGamepadState instead, which behaves consistently and is especially made for gamepads.

All of that should be resolved with this commit.

@ocornut
Copy link
Owner

ocornut commented Aug 7, 2021

Thanks for the PR!

It seems like the use of glfwGetGamepadState() + those defines fix the last two bullet point, and the first point might be an XY Problem. Do you actually need multiple gamepads to interact with dear imgui if the misdetection of mouse and keyboard is fixed?

  • I would suggest to solve the last two issues in a separate commit (same branch) then the multiple gamepads in a second commit, so the first come can be cherry-picked with more ease.
  • However glfwGetGamepadState() was added in GLFW 3.3 and we would need to support lower version via a compile-time define (say, 3.0). Also note GLFW has a glfwJoystickIsGamepad() function (might be of use, I don't know).
  • I believe there's a point when gamepad use (single or multiple) may interfere with applications wanting to use one or more gamepads for their own use and we may need a better mechanism to wire this access and potentially select controller(s) to use, but I don't yet know how to design that interface (generic? per-backend?) For this reason e.g. merging SDL2 backend: Implement multiple gamepad support #3884 was on hold. Would appreciate suggestions and ideas about how to solve this.

Thanks!

PS: Not sure why you initially created then closed #4407 before this, but note that you can push or force-push to an existing PR branch if you need to make changes to it.

@PeterSilie24
Copy link
Author

PeterSilie24 commented Aug 7, 2021

Thanks for your quick response.

  • Yes, glfwGetGamepadState fixes the latter problems.
  • I took a closer look at the implementation in GLFW 3.3.
    There is a huge list of gamepad mappings for glfwGetGamepadState to solve the mapping problem I mentioned above.
    https://github.com/glfw/glfw/blob/master/src/mappings.h
    Therefore it is actually not a bug, but simply the fact that the default joystick mappings (for an Xbox gampad) under Windows and Linux with Xpad or Xboxdrv differ.
    So whether it works or not depends a lot on the gamepad and the driver.
    I think there is no easier solution than the one implemented in GLFW 3.3.
  • Unfortunately there is no glfwJoystickIsGamepad in GLFW 3.0 either, but the number of axes or buttons could be a good filter for the joysticks.

The suggestion to split this up into two commits sounds reasonable.

@PeterSilie24
Copy link
Author

PeterSilie24 commented Aug 7, 2021

Further investigations of glfwGetGamepadState and https://github.com/glfw/glfw/blob/master/src/mappings.h (using the internal parseMapping function of GLFW) show that in most of the mappings the buttons A, B, X, Y, LB, RB are mapped to the indices 0-5.
In addition, the dpad is almost always essentially read by using glfwGetJoystickHats(...)[0].

This actually seems to work without GLFW 3.3+ on Windows, Linux and probably also on macOS.
However, it should be noted that some less common gamepads may not work, but certainly more than before.

@ocornut
Copy link
Owner

ocornut commented Aug 8, 2021 via email

@PeterSilie24
Copy link
Author

PeterSilie24 commented Aug 8, 2021

Further investigations of glfwGetGamepadState and https://github.com/glfw/glfw/blob/master/src/mappings.h (using the internal parseMapping function of GLFW) show that in most of the mappings the buttons A, B, X, Y, LB, RB are mapped to the indices 0-5.
In addition, the dpad is almost always effectively read by using glfwGetJoystickHats(...)[0].

This actually seems to work without GLFW 3.3+ on Windows, Linux and probably also on macOS.
However, it should be noted that some less common gamepads may not work, but certainly more than before.

I've just implemented the above as a compile-time alternative, which should work most of the time.

Edit: As I unfortunately just had to see, glfwGetJoystickHats was also introduced in glfw 3.3 ... Should have checked that beforehand.

…yboards, etc.) from being reported as joysticks which would prevent gamepads from being used.
@PeterSilie24
Copy link
Author

I have just looked at the two internal functions _glfwPlatformPollJoystick and _glfwInputJoystickHat of GLFW 3.3 and noticed that the hats are mapped in blocks of four at the end of the button array (retrieved by glfwGetJoystickButtons).

The documentation for glfwGetJoystickButtons (https://www.glfw.org/docs/3.3/group__input.html#gadb3cbf44af90a1536f519659a53bddd6) states:

For backward compatibility with earlier versions that did not have glfwGetJoystickHats, the button array also includes all hats, each represented as four buttons. The hats are in the same order as returned by glfwGetJoystickHats and are in the order up, right, down and left. To disable these extra buttons, set the GLFW_JOYSTICK_HAT_BUTTONS init hint before initialization.

Which leads me to the belief that the layout was handled the same way in the previous versions.

  • If you look at the various implementations for Windows (XInput, DirectInput, Winmm) and maxOS for GLFW 3.0+, the hats are always placed as stated above.
  • For Linux I can only confirm this for GLFW 3.3, as I cannot determine an explicit order in previous versions, as the joystick events including the button numbers are directly read via a file descriptor.

Since there is usually only one hat element on a gamepad, the dpad should be mapped to the last 4 buttons, which should be more robust than the current solution.

The current commit contains these changes as a compile time alternative if GLFW 3.3+ is not available.

@ocornut
Copy link
Owner

ocornut commented Nov 29, 2022

Hello @PeterSilie24 , sorry for leaving this dangling.
Commit 3d85433 on January 2022 (after your PR) switched to using glfwGetGamepadState() when available, I believe using this PR I then recalled so could use this API.

As I understand, the rest of your PR was a workaround for situation where GLFW was older than 3.3. Given the fact that dependency update are trivial on systems other than Windows I think it is not worth adding that more-complex heuristic from the PR.

Therefore closing this. Let me know if you believe there's an issue with my logic/decision.

Thanks a lot for the help!

@ocornut ocornut closed this Nov 29, 2022
@ocornut ocornut changed the title Added support for multiple gamepads and preventing devices (such as … GLFW: Added support for multiple gamepads and preventing devices (such as … Jun 30, 2023
@ocornut ocornut reopened this Jun 30, 2023
@ocornut ocornut closed this Jun 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants