-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Updated SDL2 to use the game controller API #8993
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall, I don't see anything wrong really, but a few tweaks might be nice. I'm most concerned this might lose multiple controller support (which I don't know for sure we had before.)
-[Unknown]
|
||
if (SDL_GameControllerAddMappingsFromFile("assets/gamecontrollerdb.txt") == -1) | ||
{ | ||
printf("Failed to load control pad mappings, please place gamecontrollerdb.txt in your assets directory\n"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor, but there's some wrong indentation here. Also, prefer consistent indentation. For new code we're using:
if (foo) {
bar();
} else {
baz();
}
With tabs, but it's fine to leave it - just best not to mix (else {
...)
-[Unknown]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. The previous code had a mix of curly bracket use so I decided to stick with one. I guess I chose the wrong one ;-) Easily fixed though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood... I was just matching indentation used previously... it was mixed too! This of course can be easily fixed ;-)
break; | ||
} | ||
} | ||
if (joyIndex != -1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. I wonder if we ought to fall back to the old handling if none of the joysticks are supported game controllers? Probably the large majority would be, though?
-[Unknown]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old handling only had mappings for ~4 control pads. Also, it applied the same mapping to all control pads connected. This of course could produced odd behaviour if different types of control pads were hot plugged as the mappings were not re-calculated. The SDL2 controller database supports over 50 control pads for Linux, over 25 for Windows and over 10 for OSX.
It would be possible to assign a default mapping to unknown control pad, but this would be guess work as we would know I idea of how to map buttons and axis etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the UI has a way to map buttons. It won't work inside the UI for navigating the UI (otherwise you could trap yourself), but if the game exposes a typical hat the old mapping can be usable. It might be better than not being able to use the controller at all.
-[Unknown]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the UI relied upon SDL for event handling as well as in game (ext/native/base/PCMain.cpp
)? Sorry if I have misunderstood.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, in game uses the game mapping. The NK codes are mapped to PSP ctrl codes.
This is similar, conceptually, to how we have a "virtual touch pad" on screen during the game. It's also not used in the UI. We take all the inputs by device id and allow them to be mapped to PSP buttons, arrows, and analogs - as well as special modifier keys (like fast forward.)
-[Unknown]
} | ||
if (joyIndex != -1) | ||
{ | ||
printf("Player 1 will be using joystick %d, %s\n", joyIndex, SDL_JoystickNameForIndex(joyIndex)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the PSP only supports one player, this might be confusing. Maybe something like Using joystick %d, %s\n
or maybe Pad 1 will be SDL joystick %d, %s\n
(all multiple pads do is let you configure if you have multiple attached)? Sorry to be pedantic.
-[Unknown]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"the PSP only supports one player"
Correct. Here by "Player 1" I mean player 1 of the PSP, not control pad number 1.
When multiple control pads are connected, if the control pad assigned to player 1 is disconnected, then the new code loops through the remaining control pads connected looking for the first recogonised control pad. If one is found then this control pad then becomes assigned to "player 1" (see the SDL_CONTROLLERDEVICEADDED event handling on line 171).
I was assuming that only control pad should be used to control PPSSPP. If you prefer that PPSSP responds to input from any connected control pad then this can be done but I thought that would be confusing to have more than one control pad acting as "player 1"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a gamepad mapping under the Settings which allows you to map controls from specific pads (that's why the deviceId) to certain game input.
For example, you could say that "A" on controller 1 is the PSP's "X", and "A" on controller 2 is the PSP's "O". Then you could play some weird game with two hands or a friend or whatever weird use case you have.
More practically, you can have multiple controllers simultaneously connected, and choose in the settings which ones map to PSP keys. People use this for multiplayer netplay AFAIK.
I also think it would be strange if my phone told me it was displaying a webpage on "Display 1". It only has one display. That's what I'm saying here: it's confusing for the printf (which many Linux users might see, especially when troubleshooting gamepad issues) to go out of its way to say "Player 1". Player 1 as opposed to... Player Zed? Player 9? There's only one player.
-[Unknown]
void SDLJoystick::ProcessInput(SDL_Event &event){ | ||
switch (event.type) { | ||
case SDL_JOYAXISMOTION: | ||
case SDL_CONTROLLERBUTTONDOWN: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: We prefer not indenting cases, as labels, i.e.:
switch (event.type) {
case SDL_CONTROLLERBUTTONDOWN:
foo();
break;
case SDL_JOYAXISMOTION:
bar();
break;
...
}
-[Unknown]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. I'll sort this out together with the indentation as mentioned in previous comment.
key.keyCode = i->second; | ||
key.deviceId = DEVICE_ID_PAD_0 + deviceIndex; | ||
NativeKey(key); | ||
if (event.cbutton.which == player1JoyInstanceID && event.cbutton.state == SDL_PRESSED) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to check the player1JoyInstanceID
here? It seems like we're already using getDeviceIndex()
. If I happen to have two controllers plugged in, ideally both should be mappable (i.e. in case I want to use what SDL considers my second controller.)
-[Unknown]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, player1JoyInstanceID
does need to be checked. This check means that PPSSPP only responds to input for the control pad being used by "player 1". As mentioned above, I thought it would be confusing if input from multiple control pads acted as player 1? Removing this check would mean that any control pad connected could control player 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should also be noted that event.cbutton.which
is the joystick instance ID and not the device ID. For example, if one control pad is connected its device and instance ID will be 0. If the control pad is disconnected and then plugged in again, the device ID will still be 0, but the instance ID will be 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, ALL connected gamepads should report to the native keybinding system. It should sort out what routes to which game presses, as per the user's configuration. That's why it has a deviceId field - this is used to differentiate the different gamepads, mouse, keyboard, and other devices. There are multiple gamepad device ids on purpose.
Ideally the deviceId should be as stable as possible. If another device is connected, it shouldn't change existing deviceIds. In a perfect world, restarting the program should also not alter the deviceIds (since they are saved with the user's configured mapping.)
This is just the cross platform feed to the central keybinding system.
-[Unknown]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On Linux at least, the device ID is not unique to the control pad. For example, if an XBOX control pad is plugged in, this will be assigned device ID 0. Then if this control pad is disconnected and another say PS3 control pad is connected its device ID will be 0.
SDL2 assigns its own GUID to each model of control pad which is how control pad mappings are handled. Of course, this is SDL's own system and may not translate well with other input systems used by PPSSPP.
// for removal events, "which" is the instance ID for SDL_CONTROLLERDEVICEREMOVED | ||
if (event.cdevice.which == player1JoyInstanceID) | ||
{ | ||
printf("control pad removed for player 1\n"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe "pad 1"?
-[Unknown]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, here by player 1 I do not mean control pad number 1... I mean the control pad being used by player 1 of the PSP. Is this ok?
if (player1Controller && SDL_GameControllerGetAttached(player1Controller)) | ||
{ | ||
player1JoyInstanceID = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(player1Controller)); | ||
printf("Player 1 will be using control pad: %s, instance ID: %d\n", SDL_GameControllerName(player1Controller), player1JoyInstanceID); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too. Hmm, can we reuse the other code in a function?
-[Unknown]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think you are right. I'll see what I can do later.
SDL_Joystick *joy; | ||
for (int i = 0; i < numjoys; i++) | ||
{ | ||
joy = SDL_JoystickOpen(i); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, is this fast? It might be nice if we could keep around the instance ids for activated controllers in an array since we handle add/remove anyway. I guess this probably isn't so slow as to add any significant latency to pad events...
-[Unknown]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is fast, but I agree storing the instance IDs would be better. I'll see what I can do later.
I'm most concerned this might lose multiple controller support (which I don't know for sure we had before.) Previously, the same control pad mapping was applied to all control pads so unless you had the same model of control pad connected you would get odd behaviour. I thought it made more sense that only one control pad acted as player 1 of the PSP? If not, this can be changed pretty easily. What do the authors of PPSSPP prefer? Also, the hot plugging code I have added assigns the next available control pad to player 1 if the control pad acting as player 1 is disconnected. |
Well, there's ever only a Player 1 of the PSP, as it's a portable device there can't be a Player 2. But maybe someone would play using some bizarre combination of controllers, who knows :) I'm not really sure how I prefer this to work.. |
Well, we already have an issue open where users are complaining that the Windows code for XInput - #8250 (DirectInput works fine) only supports one controller. So I prefer not introducing that same issue on SDL, because then we'll just have another open issue. -[Unknown] |
That's fair. I should really go and fix XInput, it's not like it's hard... |
I've made the modifications as suggested. Let me know what you think. |
Turns out that a number of mappings for well known control pads are also contained within the SDL2 library. |
Thanks to @dajamu for reviewing the latest code also. |
SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER); | ||
} | ||
|
||
auto dbPath = File::GetExeDirectory() + "assets/gamecontrollerdb.txt"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xsacha will this work on Qt? I think it bundles the assets into the executable?
-[Unknown]
It was designed to work alongside Qt, completely independently, so I don't see why not. |
Well, I just meant since Qt uses SDL for controller support. Okay, cool. LGTM. -[Unknown] |
Qt does bundle the assets and you can set an ifdef to check the inbuilt assets. That's designed so you don't need the additional assets directory but it is entirely optional whether you use that feature. |
For QT, is it just a case of updating Qt/assets.qrc to include an entry for the gamecontrollerdb.txt file? |
Since this isn't using the assetreader class to fetch the file, you would either need to use that or use an #ifdef USING_QT_UI And yeah probably just desktop_assets.qrc because I don't think the SDL code runs on mobile version. |
@xsacha I tried adding the gamecontrollerdb.txt to desktop_assets.qrc but I fear I am missing something else as after building the QT version I do not see any evidence that the SDLJoystick class is being used at all? Apologies if I have missed something. |
SDL in Qt only compiles in on linux and Mac when sdl2 package is found. See: https://github.com/hrydgard/ppsspp/blob/master/Qt/PPSSPP.pro |
My mistake, I thought my CMAKE variables were enough to pick up my install of SDL2 for the QT build. After setting the PKG_CONFIG environment variable I now have a QT build of PPSSPP linked against SDL 2.0.4, however, it seg faults at start-up. Note: I have added an IFDEF to avoid calling the Here is the stack trace from GDB, using QT4:
|
Looks like it is broken. Not sure where cause I haven't tested that code in a couple of years. |
@xsacha Without SDL it runs fine. I should also add that when using the code from the main PPSSPP repository that it also crashes at the same point so I don't think my mods are to blame. |
Replaced existing SDL2 joystick code and with SDL2's game controller API. This removes the need for any hard coded mappings in the code and instead means SDL2's game controller database can be used instead.
The game controller API is much easier to user and also much cleaner than using the joystick API.
Pressing the Home/Guide button on a control pad will also now bring up the PPSSPP in game menu also.
At least SDL 2.0.4 is required.
Tested under Fedora 23 (x86_64) with GCC and SDL 2.0.4 using PS3 and XBOX 360 control pads.
Hopefully this pull request should address issue: #8986