Skip to content

Gamepads without a GLFW mapping are completely invisible; expose raw joystick access #17

Description

@JoeStrout

Summary

Many perfectly ordinary gamepads report nothing at all through the current IsGamepadButtonDown / GetGamepadAxisMovement intrinsics — no buttons, no D-pad, no sticks — even though IsGamepadAvailable returns true and GetGamepadName returns the right name. This is a limitation of raylib's GLFW backend, not of our bindings, but it means raylib-miniscript currently can't use hardware that works fine in Mini Micro.

I'd like to propose exposing raw joystick access to work around it.

What happens

Found while adding key.axis and joystick button support to Soda. With a USB gamepad connected on macOS:

slot 0: available = 1, name = USB,2-axis 8-button gamepad  , axisCount = 6

...and then nothing. Mashing every button and the D-pad produces no change in any button or axis, forever. The only non-zero readings are axis4 = -1 and axis5 = -1, constant.

The same gamepad works correctly in Mini Micro, which reports the D-pad as JoyAxis1/JoyAxis2 and buttons 0-5.

Why

raylib reads gamepad state exclusively through glfwGetGamepadState() (rcore_desktop_glfw.c:1325). That function returns GLFW_FALSE unless GLFW holds a valid SDL-style mapping for the device:

// glfw/src/input.c:1401
if (!js->mapping)
    return GLFW_FALSE;

When it fails, raylib zeroes every button and axis and parks the two triggers at -1.0f — exactly the readings above. So the constant -1s are raylib's "no data" sentinel, not hardware.

Two independent things then conspire to deny this device a mapping:

  1. Platform filtering. GLFW rejects any mapping whose platform: field doesn't match the host (Mac OS X via _glfwGetMappingNameCocoa, cocoa_joystick.m:469). The only bundled entry for this device (03000000830500006020000000000000,Retro Controller,...) is tagged platform:Windows.

  2. All-or-nothing element validation. findValidMapping (input.c:100) discards the entire mapping if it references any element the device lacks. The bundled entry reaches for rightshoulder:b8 and righttrigger:b9, but this pad's HID descriptor reports only 2 axes and 8 buttons (b0-b7). One out-of-range index kills the whole pad.

I confirmed the diagnosis by supplying a corrected mapping via SetGamepadMappings — retagged platform:Mac OS X and restricted to a0-a1/b0-b7. The gamepad then works fully.

Worth noting: SetGamepadMappings returns 1 even when every line it was given is rejected — glfwUpdateGamepadMappings returns GLFW_TRUE unconditionally. So the current API gives a caller no way to tell whether a mapping was actually accepted.

Also, GetGamepadAxisCount is misleading: raylib hardcodes it to GLFW_GAMEPAD_AXIS_LAST + 1 (always 6, rcore_desktop_glfw.c:1396), so it never reflects the real device.

Why mappings alone aren't a fix

Shipping gamecontrollerdb.txt and feeding it to SetGamepadMappings is the usual advice, and it would help many devices. It does not help this one: the community entry is not merely wrong-platform, it's written for a 10-button variant sharing the same vendor/product ID. Cheap generic pads are both the most likely to have sloppy database entries and the most likely to be plugged into a hobbyist game engine.

Fundamentally, the gamepad mapping layer is designed to normalize devices into an Xbox-shaped abstraction. When it can't, raylib's API offers no fallback — the device becomes invisible even though GLFW is still perfectly happy to report its raw axes and buttons.

Proposal: raw joystick intrinsics

Expose the raw side of GLFW's joystick API, which needs no mapping:

Intrinsic Wraps Returns
IsJoystickPresent(joystick=0) glfwJoystickPresent 0/1
GetJoystickName(joystick=0) glfwGetJoystickName string
GetJoystickAxes(joystick=0) glfwGetJoystickAxes list of numbers, -1..1
GetJoystickButtons(joystick=0) glfwGetJoystickButtons list of 0/1
GetJoystickHats(joystick=0) glfwGetJoystickHats list of bitmasks
GetJoystickGUID(joystick=0) glfwGetJoystickGUID string

This matches how Unity (and therefore Mini Micro) reads controllers, and it makes any device usable without database entries. GetJoystickGUID is valuable on its own — right now there's no way for a script to discover the GUID it would need in order to write a mapping.

The tradeoffs are real and worth discussing before anyone writes code:

  • It reaches past raylib to GLFW directly. raylib deliberately doesn't expose these. It means including GLFW/glfw3.h and depending on the desktop GLFW backend specifically.
  • It won't work on the web build. Emscripten uses a different input path; these would need stubbing or a separate implementation.
  • Raw axis/button indices are device-specific, which is exactly what the mapping layer exists to avoid. This is a fallback for when mapping fails, not a replacement — the existing gamepad API should stay the recommended path.

If reaching into GLFW is unappealing, a narrower alternative would be to expose GetGamepadGUID plus a SetGamepadMappings that reports how many mappings were actually accepted. That would at least let a script detect the failure and supply its own mapping, rather than silently seeing a dead controller.

Happy to take a run at whichever direction you prefer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions