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

[rcore] Gamepad no longer detected after removing and adding when using SDL2 backend #4722

Closed
ZachIsAGardner opened this issue Jan 23, 2025 · 5 comments · Fixed by #4724
Closed

Comments

@ZachIsAGardner
Copy link

ZachIsAGardner commented Jan 23, 2025

Issue description

I am using SDL2 as my backend for my project, because GLFW doesn't support controller rumble. I've noticed that while the project is running if I unplug my controller and plug it back in it no longer works (sometimes it takes more than one unplug/plug back in). I've tried this also with my 8BitDo Pro 2 controller without it's battery plugged in, so I don't think it's a bluetooth issue. I tested this with the GLFW backend and this problem never occurs. I also tested working with just SDL2 directly and I don't think it's an SDL2 problem.

Environment

Windows, Desktop. SDL2 2.30.10, Raylib 5.5

Issue Screenshot

Here I start the .exe, plug in PS4 Controller, unplug, plug it back in (still works), and then when I unplug this time raylib still thinks the controller is plugged in. On plugging in any controller after this no gamepad input works.
https://github.com/user-attachments/assets/2e7dff57-0e9a-4832-9bab-9eca6942ed21

Code Example

I've attached a zip of my project.
raylib_c_test.zip

Below is the main file.

#include "sdl2/raylib.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
    // Initialization
    //--------------------------------------------------------------------------------------
    const int screenWidth = 800;
    const int screenHeight = 450;
    int test = 999;
    char message[20];


    InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window");

    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
    //--------------------------------------------------------------------------------------

    // Main game loop
    while (true)    // Detect window close button or ESC key
    {
        // Update
        //----------------------------------------------------------------------------------
        // TODO: Update your variables here
        //----------------------------------------------------------------------------------
        test = GetGamepadButtonPressed();
        sprintf(message, "%d", test);
        const char* name = GetGamepadName(0);
        const char* name2 = GetGamepadName(1);
        const char* name3 = GetGamepadName(2);
        const char* name4 = GetGamepadName(3);

        // Draw
        //----------------------------------------------------------------------------------
        BeginDrawing();

            ClearBackground(BLACK);

            DrawText(message, 190, 140, 20, WHITE);

            DrawText("1:", 190 - 20, 200, 20, WHITE);
            DrawText(name, 190, 200, 20, WHITE);
            DrawText("2:", 190 - 20, 230, 20, WHITE);
            DrawText(name2, 190, 230, 20, WHITE);
            DrawText("3:", 190 - 20, 260, 20, WHITE);
            DrawText(name3, 190, 260, 20, WHITE);
            DrawText("4:", 190 - 20, 290, 20, WHITE);
            DrawText(name4, 190, 290, 20, WHITE);

        EndDrawing();
        //----------------------------------------------------------------------------------
    }

    // De-Initialization
    //--------------------------------------------------------------------------------------
    CloseWindow();        // Close window and OpenGL context
    //--------------------------------------------------------------------------------------

    return 0;
}

@raysan5
Copy link
Owner

raysan5 commented Jan 24, 2025

@ZachIsAGardner I'm afraid the information provided is not enough to review this issue. Did you try debugging related code on raylib? Afaik, it calls directly SDL2 functionality, there is no magic in raylib that could generate that issue...

@ZachIsAGardner
Copy link
Author

I put trace logs in rcore_desktop_sdl.c...

Here.

case SDL_JOYDEVICEADDED:
{
    int jid = event.jdevice.which;
    TRACELOG(LOG_INFO, "ADDED: %i", jid);
    // ...

And here.

case SDL_JOYDEVICEREMOVED:
{
    int jid = event.jdevice.which;
    TRACELOG(LOG_INFO, "REMOVED: %i", jid);
    // ...

This gif might help show the problem. Here I am plugging and unplugging my controller a few times. The event.jdevice.which under SDL_JOYDEVICEADDED stays the same. But the event.jdevice.which under SDL_JOYDEVICEREMOVED keeps going up every time.

Image

I am very new to C so it's a little difficult for me to look into why that's happening.

@ZachIsAGardner
Copy link
Author

I also received an email notification with a reply from @asdqwe, I'm not sure what happened to their post in this thread, but it sounds like they were onto something...

The issue is also reproducible on the core_input_gamepad.c example (ref).

When the first SDL_JOYDEVICEADDED event happen the event.jdevice.which will be 0.
Then when SDL_JOYDEVICEREMOVED event happen the event.jdevice.which will be 0.
When the next SDL_JOYDEVICEADDED event happen the event.jdevice.which will be 0.
But then when the next SDL_JOYDEVICEREMOVED event happen the event.jdevice.which will be 1.
Then 0 and 2. Then 0 and 3. And so on.

That happens likely because: "The index passed as an argument refers to the N'th game controller on the system. This index is not the value which will identify this controller in future controller events. The joystick's instance id (SDL_JoystickID) will be used there instead." (ref, ref).

So the problem is likely raylib's implementation since platform.gamepad[] (ref) is using the event.jdevice.which to match the platform.gamepad[] array (ref, ref) and that will mismatch when connecting/disconnecting.

I believe that to solve this issue the entire gamepad handling would have to be reviewed so platform holds the actual id from SDL_JoystickGetDeviceInstanceID() (ref) and use that id to track things instead.

@asdqwe
Copy link
Contributor

asdqwe commented Jan 24, 2025

@ZachIsAGardner Yeah, sorry, I removed the post because after looking at what I'd have to change to fix this I just gave up.

The SDL2 doc mentions Sint32 which; /**< The joystick device index for the ADDED event, instance id for the REMOVED or REMAPPED event */, and that's what's happening here. During SDL_JOYDEVICEADDED event.jdevice.which will hold the device_index. But during SDL_JOYDEVICEREMOVED event.jdevice.which holds the instance_id, which are different things.

The problem is that raylib is using the device_index to fill/order the platform.gamepad[] array. However, it should track the instance_id instead (on a different var) to known which gamepad is which. And, soon after, you'll notice you'll also have to change the remaining of the gamepad handling to get this fixed properly.

Since I don't use rcore (or most of raylib at this point), I could not justify the amount of time required to properly fix/test/validade it. So I'm passing on this one. Good luck tho.

@asdqwe
Copy link
Contributor

asdqwe commented Jan 24, 2025

I really have to stop doing these, but I've sent a PR that hopefully should address the core issue of the problem. With it, you should be able to connect/disconnect the gamepads as much as necessary and it should close/deinitialize them properly.

Note that the gamepad handling on PLATFORM_DESKTOP_SDL still has other issues, that PR does not specifically address them. But it lays the groundwork necessary to do so.

EDIT: The last update of the #4724 should (hopefully) fix this issue completely.

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

Successfully merging a pull request may close this issue.

3 participants