Basic design - using SDL with joysticks that come and go during runtime (fwd)


#1

I’m in the process of making my (Win/Linux) game detect add/remove events for
joysticks. The original code assumed that joysticks would be plugged in before
the game was launched and would stay connected. If you needed to swap out
controllers, you had to relaunch the game. This is antiquated.

Now that I bring device added/removed events into the mix, a few new "gotchas"
come into play:

  1. If an SDL_Joystick pointer is opened, it keeps pointing to the same
    structure even when the physical device is disconnected from the system. You
    can keep making calls using the pointer and things like axis readings appear to
    continue working but the SDL joystick basically “goes dead”. That’s a good
    thing, IMHO… you don’t want to have to deal with “fatal error” handling for
    every single call to do anything with an SDL_Joystick.

  2. If you had an SDL_Joystick pointer opened, and the device got disconnected
    and reconnected, the pointer would remain pointing to an old structure that is
    "dead". If you call SDL_JoystickGetAttached, it will return SDL_FALSE but it
    will never return SDL_TRUE even after the device gets reconnected. This was
    suprising. SDL doesn’t “reacquire” a device that was temporarily lost (like
    XINPUT would do for play #1 whose battery died or who got excited and pulled
    their USB out).

  3. If a device is disconnected and reconnected, the same joystick gets a new,
    unique instance ID… so any events are for a “different” device now. I had
    thought the “instance ID” was the solution to the problem of figuring out which
    device is which, but it makes sense now… it’s just a auto-incremented number
    for each device encountered (whether it was seen before or not).

  4. The index numbers get shuffled around when devices are added/removed - which
    is as expected according to the docs. However, this leads to the situation
    where if you can’t use the same index number for the same device, and you can’t
    use the same instance ID for the same device, then you can never tell
    "definitively" that even a single device was disconnected and reconnected,
    except by maybe using the GUID for a DINPUT device (not very good for 3 of the
    same device type), or looking for “#1”, “#2”, etc., for an XINPUT device.

So given all that, I have to take a step back and ask, how are we supposed to
design our code to deal with devices coming and going?

So far, I’ve basically rewritten my code to close out all controllers and
reopen all controllers every time there is a device added/removed event (and
deal with matching the game code structures with the proper SDL_Joystick and
getting my game’s event mapping system re-setup properly, etc.). This certainly
works, but it somehow seems like the… “wrong way”.

Ed

Ed Phillips ed@udel.edu University of Delaware (302) 831-6082
Systems Programmer IV, Network and Systems Services


#2

The problem is, even on Windows, we don’t know it’s the same device. It might be in a different part of the USB tree, it might be someone’s friend coming in with the same PS4 controller model, etc. It’s possible Xinput, behind the scenes, uses a device-unique serial number to reassociate the connection, but literally no other API offers that.

In any case, a user might unplug a controller because the controller sucks and she wants to try a totally different one, and that’s worth handling too.

It might be useful to have a “Press Start to Join!” message somewhere on the screen when you see a new device added (like a old coin op arcade machine might say “press Player 2 button!” when there are extra quarters inserted). If they press it, either drop them in immediately or pause the game and let them get sorted. Let whatever available controller presses Start first become the user’s new controller.

If an active player disconnects, maybe you should pause the game until they come back in any case.

Ymmv by game design, of course, but the tl;dr on this, in my opinion, is: “it’s a UI problem.”

(But better solutions, including API changes, are welcome!)


#3

Okay Ryan… that makes sense. It’s good to know I haven’t completely
lost my mind and am missing the “good way” to handle when devices are
added/removed.

Thanks,

Ed

#4

Yup, you’re not the only one who’s been left scratching his head over this issue. I eventually settled on something similar to what Ryan suggested.