Jump to content

SDL_Joystick and SDL_GameController


Recommended Posts

This is a wip branch that adds support for the SDL_Joystick and SDL_GameController sub-systems to T3D: https://github.com/OTHGMars/Torque3D/tree/SDL_Joy_wip

Hopefully with some code-reviews/community input/testing this can be improved and a clean PR made so T3D will have device independent joystick and gamepad support.


Testing

To encourage testing, there's a testing module that creates an event viewer gui so you can view events that your devices generate in real-time (it's more fun than stepping through the event loop in the debugger). It also has a settings window so you can see the effect the prefs have on how devices are opened if they are connected or disconnected. After building the engine and installing BaseGame, copy the inputTest directory from Templates/Modules to your game/data directory. It will add a button for the event viewer to the main menu. Please throw any gaming-type input device you have at it and post your results. I do not have any devices with a trackball, so those events are completely untested.


The Details

The input model under SDL is different than the existing DInputDevice platform code. Under SDL all input devices other than keyboard/mouse are considered Joysticks. Game Controllers are a subset of Joysticks. The advantage to using Game Controllers is the SDL database of controller mappings. Recognized gamepad devices can be mapped to the xbox input scheme so a single set of ActionMap defaults can be identical buttons on different devices. Additional mappings can be loaded from script or file. More info at https://wiki.libsdl.org/SDL_GameControllerAddMappingsFromFile


There's a new global, $pref::Input::sdlControllerEnabled. If true, any device that SDL recognizes as a Game Controller will be automatically opened as a controller and these events will be used: https://github.com/OTHGMars/Torque3D/blob/SDL_Joy_wip/Engine/source/platform/input/event.cpp#L392-L430.

The existing global $pref::Input::JoystickEnabled is still used. If true, any device that cannot be recognized as a Game Controller (or all devices if pref::Input::sdlControllerEnabled is false) will be automatically opened as a Joystick and these events will be used: https://github.com/OTHGMars/Torque3D/blob/SDL_Joy_wip/Engine/source/platform/input/event.cpp#L309-L390.

If both globals are false, no joystick devices will be opened. There is a script interface for opening/closing and getting device details that closely mirrors the SDL_Joystick and SDL_GameController function sets if you do decide to take manual control of opening/closing devices from script.

New events were added for trackballs and the guide button on gamepads.

There are 2 new script callbacks that fire anytime a device is connected or disconnected, onSDLDeviceConnected() and onSDLDeviceDisconneceted(). The connected callback is called before the device is opened by the InputManager so you can modify default behavior if you need to.


Unresolved Questions

Is it possible to use the DECLARE_CALLBACK IMPLEMENT_CALLBACK macros with a class that's not a CONOBJECT? If so, that would be cleaner and easier to document than the current Con::executef() method, but I couldn't figure out how to do it.

Edit: I used the IMPLEMENT_GLOBAL_CALLBACK macro and was able to implement the connect/disconnect callbacks.


I left this enumeration commented out https://github.com/OTHGMars/Torque3D/blob/SDL_Joy_wip/Engine/source/platformSDL/sdlInputManager.cpp#L30-L42. It worked as the return value here https://github.com/OTHGMars/Torque3D/blob/SDL_Joy_wip/Engine/source/platformSDL/sdlInputManager.cpp#L990, but I could not find the correct way to get the string from the value here https://github.com/OTHGMars/Torque3D/blob/SDL_Joy_wip/Engine/source/platformSDL/sdlInputManager.cpp#L1044. Lookup is complicated by the fact that the enumeration contains a negative value. So, I went with a simple static lookup function https://github.com/OTHGMars/Torque3D/blob/SDL_Joy_wip/Engine/source/platformSDL/sdlInputManager.cpp#L81. I'm looking for suggestions for a better way.

Edit: castConsoleTypeToString() is the function I was looking for.


This if block, https://github.com/OTHGMars/Torque3D/blob/SDL_Joy_wip/Engine/source/windowManager/sdl/sdlWindowMgr.cpp#L262-L268 could be where it is now, or it could be in the default branch of the switch statement just below it. Is there a preference? Is one better?

Edit: Left as is.


As is sometimes the case with T3D, you go to start something and realize something else needs fixed first. I liked the LOG_INPUT define in the old platform layer, so I defined it when starting on this, but the engine wouldn't build under windows. I 'fixed' that as the first commit. I wouldn't use it anymore since the event monitor gives as much information. But...Since it's in the code, I really should verify it works under Linux or take it back out. I don't have a Linux test environment atm, so if anyone could help out with that, it would be greatly appreciated.

Edit: Removed from the code.


Because of the way they're generalized, these functions https://github.com/GarageGames/Torque3D/blob/development/Engine/source/app/game.cpp#L70-L88 will activate and deactivate the sdl input manager...I'm not sure if duplicates should be made with general names or if it should left as is? If the legacy platform code is going to be depreciated at 4.0 in favor of all sdl, the activation/deactivation could be cleaned up and some functions that only exist to match the old structure could be removed.

Edited by OTHGMars
Link to post
Share on other sites

Thanks a lot for the effort. It works well for my Trust compact gamepad gtx24. It detects it as joystick.


All the buttons work as normal except, the horizontal of the right stick is detected as both "zaxis" and "rxaxis".

But that may be because some faulty drivers I tried to install for an old controller so, what would be the ingame behaviour of a key that throws simultaneously two inputs?


As side note, the keyboard is detected as the default English layout instead of my Spanish one, so my ñ key is detected as ; and < between left shift and z isn't recognized (throws a "" as key name).

http://ascii-table.com/img/keyboard-071.png


But, well, Torque hasn't supported non-English layouts as long as I recall. So I don't think it's very important.

If anything I guess the azerty or devorak users have it worse.



Ah, F1 doesn't work for me to show the config menu (console throws: pushDialog(): Invalid control: HelpDlg).

But I can get it to work using

Canvas.pushDialog(JoystickSettingsDlg)

Link to post
Share on other sites

@irei1as Thanks for testing and giving feedback!

If the driver is actually generating 2 events and the ActionMap has 2 bindings, both would get called.


I forgot that F1 has a GlobalActionMap bind in the Full template. I should have picked a different key for that. Anything with a bind in the GlobalActionMap gets pulled out of the event loop before it gets to the gui. Glad you found the work-around.


I've never looked at sdl keyboard layout handling, but I made a note to check it out the next time I'm in the docs.

Link to post
Share on other sites

Hey, thanks @OTHGMars, it's sure nice having a community that happens to be working on exactly what I need! 8-)


I took a chance and hacked your changes straight into my main build (not all the way up to date with head by any stretch) and it seemed to work out fine. With my Logitech Extreme3D Pro I get all the buttons working as expected. I had some trouble between the hat and the slider though, where rxaxis (hat horizontal) works but ryaxis (hat vertical) doesn't seem to give me anything, and the slider (throttle) seems to think it is the ryaxis. Results varied, not sure what all was up there.


I haven't tested the main joystick axes yet.


EDIT: for the main axes, X and Y seemed to work but I'm not getting anything for Z (twist) yet.

Link to post
Share on other sites

It reported as 4 axis, and your fix seems to have gotten the main stick reporting accurately, however now my slider (throttle) reports as RXaxis and I don't get anything from the hat, which I would expect to be RX and RY.


Also, I feel like I already know this, but how does one pull an actual value out of these controls as opposed to just getting a callback? Having a minor memory malfunction...

Link to post
Share on other sites

@chriscalef This is probably the function that you're remembering: https://github.com/GarageGames/Torque3D/blob/development/Engine/source/platformWin32/winDirectInput.cpp#L852. SDL does have equivalent commands for polling individual device states but they are not exposed as console functions in this branch. I'll be sure to get those added before a PR is made.


I would expect the hat to generate dpad events though, https://github.com/GarageGames/Torque3D/blob/development/Engine/source/platform/input/event.cpp#L374-L377. Were you able to run the input monitor to see if the hat was generating any events?

Link to post
Share on other sites

Ah sorry, I was in a mad rush so once I added your engine code and realized I was getting button callbacks, I didn't bother to install your scripts. :oops: Now I'm all out of weekend, but I'll try to get back to that tonight.


Meanwhile, though, I do know that the hat does something, because before I fixed the 4 axis thing I was getting reliable RXaxis returns from the horizontal axis. However, the vertical axis and the slider got mixed up with each other.

Link to post
Share on other sites
  • 1 month later...

This code has been updated for SDL 2.0.8, cleaned up and PR'd as #2256. The new branch is: https://github.com/OTHGMars/Torque3D/tree/SDL_Joystick. The original post has been updated with the answers to a couple of the questions I had there. The input test module (useful for discovering device capabilities and monitoring input events) has also been updated and is now available here: https://github.com/OTHGMars/Torque3D/tree/InputMonitor.

Link to post
Share on other sites
  • 2 months later...

The joystick branch now includes a keyboard event refactor that eliminates keycode and modifier conversions. This opens the possibility of supporting ANSI and ISO keyboards in any layout that SDL recognizes. Help is needed in order to get this properly tested and working in time for 4.0. If you use a non US keyboard layout or have any type of gaming input device, please see testing below and post your results.


Testing

To test, pull in the joystick branch and test module (install BaseGame with one of the Gameplay and inputTest modules). It will allow you to see events that would otherwise require you to set binds & breakpoints or use the remap gui. Test every standard key on your keyboard. Any keys bound in the global action map like the Grave Accent on US layout will execute that function instead of appearing in the input monitor. You can still see an event generated by using a modifier key with them. If any key does not produce an event name in the monitor, open the console and there will be an error message ("ActionMap::getKeyString: no string for action %d") in the console. Please post the number printed, the key pressed, your layout and OS in this thread. The action number will correspond to this enumeration. No, we don't support every scancode in the USB standard so there's no bind for the mute function etc. of modern keyboards. If you find any key that you think should be added, please bring it up. The text input of all printable characters can be tested in the console gui.


When initially testing a joystick device, open the settings window (f2 from the monitor) and turn the deadzone up past half-way. That makes it easier to verify the individual axis events in the monitor.


New Joystick Options

POV Hat data is provided as a 4 bit mask value and interpreted to produce the standard upov, dpov, lpov and rpov events. Set pref::Input::JoystickPOVMask true to receive a povmask event with the current hat state every time it changes. This defaults to false so standard behavior is consistent with the previous direct input event handling.


A joystick device that only has 4 axes will always report axes 0-3. In torque mapping this becomes xaxis, yaxis, zaxis and rxaxis. There's an option, pref::Input::JoystickSplitAxesLR, that if true will map axes 2/3 to rxaxis/ryaxis instead of zaxis/rxaxis. This option only affects 4 axis joysticks.


Checkboxes have been added to the settings gui in the test module so you can see the effects of these settings without needing to type them out in the console.


My Test Results

US keyboard layout, all keys and modifier combinations produced the correct bindable event. The only other devices I have are a Logitech RumblePad 2 (c2007) and an Xbox One controller. SDL has built-in controller mapping for the logitech so I was able to open both as joystick or gamepad. Both devices produced the correct events for all inputs. The Logitech was not detected as a haptic device (it is) and the wired/battery status returned unknown. Every feature of the Xbox controller was detected correctly.


K4OzrOa.jpg

Link to post
Share on other sites

Just had a quick play with SDL_Joystick branch (minus testing branch). UK keyboard, X-Box Controller.


Xbox Gamepad:

Y axis on both thumbsticks are reversed (forwards=back, up=down). X axis works fine. Buttons work fine.


Keyboard:

Console is under Esc (which is not tilde on UK keyboard) but that is where everyone wants it. All other keys work as expected.

Link to post
Share on other sites

Swapping the +/- around in default.bind.cs does the trick

 

function gamePadMoveY( %val )
{
   if(%val > 0)
   {
      $mvForwardAction = -%val * $movementSpeed;//swapped to negative for sdl
      $mvBackwardAction = 0;
   }
   else
   {
      $mvForwardAction = 0;
      $mvBackwardAction = %val * $movementSpeed;//swapped to positive for sdl
   }
}
 
//...
 
function gamepadPitch(%val)
{
   %pitchAdj = getGamepadAdjustAmount(%val);
   if(ServerConnection.isControlObjectRotDampedCamera())
   {
      // Clamp and scale
      %pitchAdj = mClamp(%pitchAdj, -m2Pi()+0.01, m2Pi()-0.01);
      %pitchAdj *= 0.5;
   }
 
	if(%pitchAdj > 0)
   {
      $mvPitchDownSpeed = -%pitchAdj;//swapped to negative for sdl
      $mvPitchUpSpeed = 0;
   }
   else
   {
      $mvPitchDownSpeed = 0;
      $mvPitchUpSpeed = %pitchAdj;//swap to positive for sdl
   }
}

And then all is right with the world once more 8-)

Link to post
Share on other sites

And a prettier way of doing it ...

 

function gamePadMoveY( %val )
{
   if(%val > 0)
   {
      $mvForwardAction = 0;
      $mvBackwardAction = %val * $movementSpeed;
   }
   else
   {
      $mvForwardAction = -%val * $movementSpeed;
      $mvBackwardAction = 0;
   }
}
 
//...
 
function gamepadPitch(%val)
{
   %pitchAdj = getGamepadAdjustAmount(%val);
   if(ServerConnection.isControlObjectRotDampedCamera())
   {
      // Clamp and scale
      %pitchAdj = mClamp(%pitchAdj, -m2Pi()+0.01, m2Pi()-0.01);
      %pitchAdj *= 0.5;
   }
 
	if(%pitchAdj > 0)
   {
      $mvPitchDownSpeed = 0;
      $mvPitchUpSpeed = %pitchAdj;//swap to positive for sdl
   }
   else
   {
      $mvPitchDownSpeed = -%pitchAdj;//swapped to negative for sdl
      $mvPitchUpSpeed = 0;
   }
}
Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...