2
\$\begingroup\$

I init my controllers like this:

if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) == 1)
    return;
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);

int nJoysticks = SDL_NumJoysticks();
numGamepads = 0;
for (int i = 0; i < nJoysticks; i++)
    if (SDL_IsGameController(i))
        numGamepads++;

if (numGamepads > 0)
{
    for (int i = 0; i < numGamepads; i++)
    {
        SDL_GameController* pad = SDL_GameControllerOpen(i);
        if (SDL_GameControllerGetAttached(pad) == 1)
            joysticks.push_back(pad);
        else
            cout << "SDL_GetError() = " << SDL_GetError() << endl;
    }
    SDL_GameControllerEventState(SDL_ENABLE);
}

and I poll events like this:

SDL_Event sdl_Event;
while (SDL_PollEvent(&sdl_Event))
{
    switch (sdl_Event.type)
    {
    case SDL_CONTROLLERDEVICEADDED:
        cout << "DEVICEADDED cdevice.which = " << sdl_Event.cdevice.which << endl;
        break;
    case SDL_CONTROLLERBUTTONDOWN:
        cout << "BUTTONDOWN cdevice.which = "<< sdl_Event.cdevice.which << endl;
        HandleGamepadButtonsDown(sdl_Event);
        break;
    case SDL_CONTROLLERBUTTONUP:
        HandleGamepadButtonsUp(sdl_Event);
        break;
    case SDL_CONTROLLERAXISMOTION:
        HandleGamepadAxis(sdl_Event);
        break;
    case SDL_QUIT:
        _quit = true;
        break;
    default:
        break;
    }
}

With this I have currently two Xbox 360 controllers connected. On these pads there is a LED indicating if it is player 1, 2, 3, or 4.

Player 1

When I run this my poll loop prints out this when I press a button on this controller:

I press a button on player 1's controller

When I hit a button on the controller with the player 2 LED it prints:

I press a button on player 2's controller

So that is wrong. How can I from inside the code find out what controller corresponds to the controller with the right LED light on?

Also, when I first start it then it prints:

enter image description here

So the controllers are 0 and 1 when I add them but 1 and 2 when I run the game.

\$\endgroup\$
3
\$\begingroup\$

First of all, cdevice isn't safe to use on a SDL_CONTROLLERBUTTONDOWN event. It could contain whatever field of a SDL_ControllerButtonEvent happens to overlap the which field of the SDL_ControllerDeviceEvent structure in the union inside SDL_Event.

So the SDL docs are a little confusing on this, but here's what I could gather:

There are two different "instances" that are passed around from the game controller events. There are game controller instance IDs, which is used in the SDL_CONTROLLERDEVICE* events, and then there are joystick instance IDs, which are used for an example in the SDL_CONTROLLERBUTTONDOWN event.

Here's what the docs say about the which field for button events:

Type: SDL_JoystickID - Name: which - Description: the joystick instance id

and here's the same which parameter on a SDL_ControllerDeviceEvent:

Type: Sint32 - Name: which - the joystick device index for the SDL_CONTROLLERDEVICEADDED event or instance id for the SDL_CONTROLLERDEVICEREMOVED or SDL_CONTROLLERDEVICEREMAPPED event

The biggest (and pretty much only) giveaway here is the different types of the fields.

A game controller in SDL is a combination of multiple joystick instances or something, and this is why it all becomes quite confusing.

So here's what I do in my codebase to make sure the correct player instance receives the correct controller events (so that pressing a button on one controller doesn't make all players perform an action):

Since SDL_CONTROLLERBUTTONDOWN events have a joystick instance field, I compare the which field of the event to SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller));, where controller is a SDL_GameController* that's supposed to control the player.

I do keep track of the which value of the SDL_CONTROLLERDEVICEADDED event too inside my controller-wrapper class, so that I can properly handle SDL_CONTROLLERDEVICEREMOVED events.

So in conclusion, it's a little messy to get things going, but once you have your bool DoesThisEventBelongToThisController(SDL_Event* event, SDL_GameController* controller); function setup, you shouldn't have to worry about this anymore.

\$\endgroup\$
  • \$\begingroup\$ Can you tell me why the cdevice.which id is different in SDL_CONTROLLERDEVICEADDED and SDL_CONTROLLERBUTTONDOWN? \$\endgroup\$ – Lord Wolfenstein Dec 20 '17 at 21:27
  • 1
    \$\begingroup\$ For SDL_CONTROLLERDEVICEADDED cdevice.which is the joystick device index, while with SDL_CONTROLLERBUTTONDOWN cdevice.which probably maps to the same address as cbutton.which (as to how unions work, feel free to look unions up if you're not familiar with them. SDL_Event is essentially one large union.), and cbutton.which is a joystick instance id, not a joystick device index. But as I said, you should not access cdevice in a SDL_CONTROLLERBUTTONDOWN handler. \$\endgroup\$ – Tyyppi_77 Dec 20 '17 at 22:00
  • \$\begingroup\$ I get the same problem when I switch everything to jdevice.which \$\endgroup\$ – Lord Wolfenstein Dec 20 '17 at 22:09
  • 1
    \$\begingroup\$ You switch to what? Anyways, I clearly mention in the answer that the values in cbutton.which dont correspond directly to cdevice.which. They're two different types of values, that have different meanings. And don't switch everything, just use cbutton when handling button events, and use cdevice when handling device events. \$\endgroup\$ – Tyyppi_77 Dec 20 '17 at 22:22
  • \$\begingroup\$ I accepted this answer even though it was not 100% what I was asking for. If you unplug and replug a lot of controllers making "holes" in the controller order according the LEDs. Then the controller ID in SDL will not reflect the LED on the controller. But this was close enough. \$\endgroup\$ – Lord Wolfenstein Mar 3 '18 at 13:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.