I'm trying to make a 1 to 1 mapping between physical game controller device and virtual device (vJoy). What I want to achieve is when button no X is pressed on physical device, make button no X pressed on virtual device.
Since I'm trying to map 1-1 between physical and virtual controller buttons&axes, I shouldn't have any business with mapping. So I decided to use SDL2 library's 'Joystick' related functions instead of 'GameController' related functions.
Input (Physical) => MyApp => Output(vJoy)
I can achieve 1-1 correspondance with almost all of my controllers except one, which is a PS4 controller.
With SDL input read functions(code below), I get these:
When I press press leftshoulder button, event.jbutton.button says 9
When I press press rightshoulder button, event.jbutton.button says 10
(It also says 9 and 10 in game controller mode instead of joystick mode, same. I confirm I'm not mistakenly using controller mode by trying same method with other controllers without changing anything)
SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_EVENTS);
SDL_JoystickEventState(SDL_ENABLE);
SDL_Event event;
while(SDL_WaitEventTimeout(&event, 50))
{
switch(event.type){
case SDL_JOYDEVICEADDED:
m_Joystick = SDL_JoystickOpen(device);
break;
//
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
qDebug() << "JoystickButton(" << QString(SDL_GameControllerGetStringForButton((SDL_GameControllerButton)event.jbutton.button)) << ")" << event.jbutton.button<< " -> " << event.jbutton.state;
break;
}
}
But when I test it with other tools, they give 4 for leftshoulder and 5 for rightshoulder as button numbers.
0-) My App Case
LeftShoulder button => 9
RightShoulder button => 10
1-) Windows' internal game controller utility (index starts from 1, which makes 4-5)
2-) Result from https://gamepad-tester.com/
3-) Result from https://greggman.github.io/html5-gamepad-test/
Since I'm using Joystick related functions, I expect to read inputs as is, without mapping. But SDL2 result seems different from others. Since
1 - What should I do to read joystick input so that it will give the same result with other tools
2 - What is wrong with SDL2 and my code?
SDL2 is trying to protect you from poor standardization of gamepad inputs. It reorders buttons to match its own standard button ordering based on the physical location of each button on the gamepad. The ordering is defined by the SDL_GamepadButton enum. That's where "9" comes from, SDL_GAMEPAD_BUTTON_LEFT_SHOUDLER
has value 9. In SDL2, any correctly-mapped gamepad will have the L1 button at index 9.
Gamepad API (used by online tools) is doing the same thing. Gamepad API defines a Standard Gamepad which has "Top left front button" as buttons[4]
. In Gamepad API, any correctly-mapped gamepad will have the L1 button at index 4.
You can convert between SDL2 and Gamepad API buttons fairly easily because they both define essentially the same set of standard buttons.
The Windows game controller utility (joy.cpl) doesn't try to remap gamepad buttons. It's using DirectInput API, which chooses button indices based on the HID usage identifier assigned to each button input.
Putting it all together:
DS4 | HID usage ID(s) | SDL2 | Gamepad API |
---|---|---|---|
Cross | Button 2 | SDL_GAMEPAD_BUTTON_A (0) |
buttons[0] |
Circle | Button 3 | SDL_GAMEPAD_BUTTON_B (1) |
buttons[1] |
Square | Button 1 | SDL_GAMEPAD_BUTTON_X (2) |
buttons[2] |
Triangle | Button 4 | SDL_GAMEPAD_BUTTON_Y (3) |
buttons[3] |
Share | Button 9 | SDL_GAMEPAD_BUTTON_BACK (4) |
buttons[8] |
PS | Button 13 | SDL_GAMEPAD_BUTTON_GUIDE (5) |
buttons[16] |
Options | Button 10 | SDL_GAMEPAD_BUTTON_START (6) |
buttons[9] |
L3 | Button 11 | SDL_GAMEPAD_BUTTON_LEFT_STICK (7) |
buttons[10] |
R3 | Button 12 | SDL_GAMEPAD_BUTTON_RIGHT_STICK (8) |
buttons[11] |
L1 | Button 5 | SDL_GAMEPAD_BUTTON_LEFT_SHOULDER (9) |
buttons[4] |
R1 | Button 6 | SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER (10) |
buttons[5] |
D-pad Up | Generic Desktop Hat Switch | SDL_GAMEPAD_BUTTON_DPAD_UP (11) |
buttons[12] |
D-pad Down | Generic Desktop Hat Switch | SDL_GAMEPAD_BUTTON_DPAD_DOWN (12) |
buttons[13] |
D-pad Left | Generic Desktop Hat Switch | SDL_GAMEPAD_BUTTON_DPAD_LEFT (13) |
buttons[14] |
D-pad Right | Generic Desktop Hat Switch | SDL_GAMEPAD_BUTTON_DPAD_RIGHT (14) |
buttons[15] |
Touchpad button | Button 14 | SDL_GAMEPAD_BUTTON_TOUCHPAD (20) |
buttons[16] (non-standard) |
Left stick X | Generic Desktop X | SDL_GAMEPAD_AXIS_LEFTX (0) |
axes[0] |
Left stick Y | Generic Desktop Y | SDL_GAMEPAD_AXIS_LEFTY (1) |
axes[1] |
Right stick X | Generic Desktop Z | SDL_GAMEPAD_AXIS_RIGHTX (2) |
axes[2] |
Right stick Y | Generic Desktop Rz | SDL_GAMEPAD_AXIS_RIGHTY (3) |
axes[3] |
L2 trigger | Button 7 (digital), Generic Desktop Rx (analog) | SDL_GAMEPAD_AXIS_LEFT_TRIGGER (4) |
buttons[6] |
R2 trigger | Button 8 (digital), Generic Desktop Ry (analog) | SDL_GAMEPAD_AXIS_RIGHT_TRIGGER (5) |
buttons[7] |
Since I'm using Joystick related functions, I expect to read inputs as is, without mapping.
This is "as-is". SDL2 knows the exact layout of the DS4 input report and reads button and axis values out of the report buffer. There's no remapping happening, the inputs are always correctly mapped.
But SDL2 result seems different from others.
Yes, it uses a standard button ordering that's specific to SDL2.
1 - What should I do to read joystick input so that it will give the same result with other tools
Which other tools? They all are slightly different. If you want to convert between SDL2 and Gamepad API you can use the table above.
2 - What is wrong with SDL2 and my code?
Nothing, gamepads are just complicated and poorly standardized.