@@ -224,6 +224,7 @@ void setPlayerJoystick(Joystick* stick, short cid)
224224 if (pJoystick[cid] != nullptr ) {
225225 mprintf ((" Using '%s' as Joy-%i\n " , pJoystick[cid]->getName ().c_str (), cid));
226226 mprintf ((" \n " ));
227+ mprintf ((" Is gamepad: %s\n " , pJoystick[cid]->isGamepad () ? " YES" : " NO" ));
227228 mprintf ((" Number of axes: %d\n " , pJoystick[cid]->numAxes ()));
228229 mprintf ((" Number of buttons: %d\n " , pJoystick[cid]->numButtons ()));
229230 mprintf ((" Number of hats: %d\n " , pJoystick[cid]->numHats ()));
@@ -573,7 +574,12 @@ namespace joystick
573574 Joystick::Joystick (int id) :
574575 _id (id)
575576 {
576- _joystick = SDL_OpenJoystick (id);
577+ if (SDL_IsGamepad (id)) {
578+ _gamepad = SDL_OpenGamepad (id);
579+ _joystick = SDL_GetGamepadJoystick (_gamepad);
580+ } else {
581+ _joystick = SDL_OpenJoystick (id);
582+ }
577583
578584 if (_joystick == nullptr ) {
579585 SCP_stringstream msg;
@@ -585,14 +591,20 @@ namespace joystick
585591 }
586592
587593 Joystick::Joystick (Joystick &&other) noexcept :
588- _joystick (nullptr )
594+ _joystick (nullptr ), _gamepad( nullptr )
589595 {
590596 *this = std::move (other);
591597 }
592598
593599 Joystick::~Joystick ()
594600 {
595- if (_joystick != nullptr )
601+ if (_gamepad != nullptr )
602+ {
603+ SDL_CloseGamepad (_gamepad); // also closes joystick
604+ _gamepad = nullptr ;
605+ _joystick = nullptr ;
606+ }
607+ else if (_joystick != nullptr )
596608 {
597609 SDL_CloseJoystick (_joystick);
598610 _joystick = nullptr ;
@@ -606,6 +618,7 @@ namespace joystick
606618
607619 std::swap (_id, other._id );
608620 std::swap (_joystick, other._joystick );
621+ std::swap (_gamepad, other._gamepad );
609622
610623 fillValues ();
611624
@@ -614,7 +627,7 @@ namespace joystick
614627
615628 bool Joystick::isAttached () const
616629 {
617- return SDL_JoystickConnected (_joystick);
630+ return isGamepad () ? SDL_GamepadConnected (_gamepad) : SDL_JoystickConnected (_joystick);
618631 }
619632
620633 bool Joystick::isHaptic () const
@@ -776,22 +789,35 @@ namespace joystick
776789
777790 void Joystick::fillValues ()
778791 {
792+ // To avoid some weirdness and build compatiblity issues we always use
793+ // _joystick here rather than comparable _gamepad functions
794+
779795 _name.assign (SDL_GetJoystickName (_joystick));
780796 _guidStr = getJoystickGUID (_joystick);
781797 _isHaptic = SDL_IsJoystickHaptic (_joystick);
782798 _isGamepad = SDL_IsGamepad (_id);
783799
784800 // Initialize values of the axes
785- auto numSticks = SDL_GetNumJoystickAxes (_joystick);
786- if (numSticks >= 0 ) {
787- _axisValues.resize (static_cast <size_t >(numSticks));
788- for (auto i = 0 ; i < numSticks; ++i) {
789- _axisValues[i] = SDL_GetJoystickAxis (_joystick, i);
801+ if (_isGamepad) {
802+ // gamepads may not have all axes, but they don't necessarily match
803+ // the number or index of what's reported by the joystick api either
804+ _axisValues.resize (static_cast <size_t >(SDL_GAMEPAD_AXIS_COUNT));
805+ for (size_t i = 0 ; i < _axisValues.size (); ++i) {
806+ // will return 0 (center) if axis not supported
807+ _axisValues[i] = SDL_GetGamepadAxis (_gamepad, static_cast <SDL_GamepadAxis>(i));
790808 }
791-
792809 } else {
793- _axisValues.resize (0 );
794- mprintf ((" Failed to get number of axes for joystick %s: %s\n " , _name.c_str (), SDL_GetError ()));
810+ auto numSticks = SDL_GetNumJoystickAxes (_joystick);
811+ if (numSticks >= 0 ) {
812+ _axisValues.resize (static_cast <size_t >(numSticks));
813+ for (auto i = 0 ; i < numSticks; ++i) {
814+ _axisValues[i] = SDL_GetJoystickAxis (_joystick, i);
815+ }
816+
817+ } else {
818+ _axisValues.resize (0 );
819+ mprintf ((" Failed to get number of axes for joystick %s: %s\n " , _name.c_str (), SDL_GetError ()));
820+ }
795821 }
796822
797823 // Initialize ball values
@@ -812,30 +838,41 @@ namespace joystick
812838 }
813839
814840 // Initialize buttons
815- auto buttonNum = SDL_GetNumJoystickButtons (_joystick);
816- if (buttonNum >= 0 ) {
817- _button.resize (static_cast <size_t >(buttonNum));
818- for (auto i = 0 ; i < buttonNum; ++i) {
819- if (SDL_GetJoystickButton (_joystick, i)) {
841+ if (_isGamepad) {
842+ // gamepads may not support all buttons, but they don't necessarily match
843+ // the number or index of what's reported by the joystick api either
844+ _button.resize (static_cast <size_t >(SDL_GAMEPAD_BUTTON_COUNT));
845+ for (size_t i = 0 ; i < _button.size (); ++i) {
846+ if (SDL_GetGamepadButton (_gamepad, static_cast <SDL_GamepadButton>(i))) {
820847 _button[i].DownTimestamp = ui_timestamp ();
821-
822848 } else {
823849 _button[i].DownTimestamp = UI_TIMESTAMP::invalid ();
824850 }
825851 }
826-
827852 } else {
828- _button.resize (0 );
829- mprintf ((" Failed to get number of buttons for joystick %s: %s\n " , _name.c_str (), SDL_GetError ()));
853+ auto buttonNum = SDL_GetNumJoystickButtons (_joystick);
854+ if (buttonNum >= 0 ) {
855+ _button.resize (static_cast <size_t >(buttonNum));
856+ for (auto i = 0 ; i < buttonNum; ++i) {
857+ if (SDL_GetJoystickButton (_joystick, i)) {
858+ _button[i].DownTimestamp = ui_timestamp ();
859+ } else {
860+ _button[i].DownTimestamp = UI_TIMESTAMP::invalid ();
861+ }
862+ }
863+
864+ } else {
865+ _button.resize (0 );
866+ mprintf ((" Failed to get number of buttons for joystick %s: %s\n " , _name.c_str (), SDL_GetError ()));
867+ }
830868 }
831869
832- // Initialize hats
833- auto hatNum = SDL_GetNumJoystickHats (_joystick);
870+ // Initialize hats (consider gamepads to always have one hat)
871+ auto hatNum = _isGamepad ? 1 : SDL_GetNumJoystickHats (_joystick);
834872 if (hatNum >= 0 ) {
835873 _hat.resize (static_cast <size_t >(hatNum));
836874 for (auto i = 0 ; i < hatNum; ++i) {
837- std::bitset<4 > hatset = SDL_GetJoystickHat (_joystick, i);
838- auto hatval = convertSDLHat (SDL_GetJoystickHat (_joystick, i));
875+ auto hatval = _isGamepad ? HAT_CENTERED : convertSDLHat (SDL_GetJoystickHat (_joystick, i));
839876 _hat[i].Value = hatval;
840877
841878 // Reset timestampts
@@ -847,21 +884,23 @@ namespace joystick
847884 }
848885
849886 if (_hat[i].Value != HAT_CENTERED) {
850- // Set the 4-pos timestamp(s)
851- if ((hatset[HAT_DOWN])) {
852- _hat[i].DownTimestamp4 [HAT_DOWN] = ui_timestamp ();
853- }
854- if ((hatset[HAT_UP])) {
855- _hat[i].DownTimestamp4 [HAT_UP] = ui_timestamp ();
856- }
857- if ((hatset[HAT_LEFT])) {
858- _hat[i].DownTimestamp4 [HAT_LEFT] = ui_timestamp ();
859- }
860- if ((hatset[HAT_RIGHT])) {
861- _hat[i].DownTimestamp4 [HAT_RIGHT] = ui_timestamp ();
862- }
887+ std::bitset<4 > hatset = SDL_GetJoystickHat (_joystick, i);
863888
864- // Set the 8-pos timestamp
889+ // Set the 4-pos timestamp(s)
890+ if ((hatset[HAT_DOWN])) {
891+ _hat[i].DownTimestamp4 [HAT_DOWN] = ui_timestamp ();
892+ }
893+ if ((hatset[HAT_UP])) {
894+ _hat[i].DownTimestamp4 [HAT_UP] = ui_timestamp ();
895+ }
896+ if ((hatset[HAT_LEFT])) {
897+ _hat[i].DownTimestamp4 [HAT_LEFT] = ui_timestamp ();
898+ }
899+ if ((hatset[HAT_RIGHT])) {
900+ _hat[i].DownTimestamp4 [HAT_RIGHT] = ui_timestamp ();
901+ }
902+
903+ // Set the 8-pos timestamp
865904 _hat[i].DownTimestamp8 [hatval] = ui_timestamp ();
866905 }
867906 }
@@ -871,30 +910,50 @@ namespace joystick
871910 }
872911 }
873912
874- SDL_Joystick *Joystick::getDevice ()
913+ SDL_Joystick *Joystick::getJoystick ()
875914 {
876915 return _joystick;
877916 }
878917
918+ SDL_Gamepad *Joystick::getGamepad ()
919+ {
920+ return _gamepad;
921+ }
922+
879923 void Joystick::handleJoyEvent (const SDL_Event &evt)
880924 {
881- switch (evt.type )
882- {
883- case SDL_EVENT_JOYSTICK_AXIS_MOTION:
884- handleAxisEvent (evt.jaxis );
885- break ;
886- case SDL_EVENT_JOYSTICK_BALL_MOTION:
887- handleBallEvent (evt.jball );
888- break ;
889- case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
890- case SDL_EVENT_JOYSTICK_BUTTON_UP:
891- handleButtonEvent (evt.jbutton );
892- break ;
893- case SDL_EVENT_JOYSTICK_HAT_MOTION:
894- handleHatEvent (evt.jhat );
895- break ;
896- default :
897- break ;
925+ // gamepads also get joy events, so make sure we ignore those
926+ if (isGamepad ()) {
927+ switch (evt.type ) {
928+ case SDL_EVENT_GAMEPAD_AXIS_MOTION:
929+ handleAxisEvent (evt.gaxis );
930+ break ;
931+ case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
932+ case SDL_EVENT_GAMEPAD_BUTTON_UP:
933+ handleButtonEvent (evt.gbutton );
934+ break ;
935+ default :
936+ break ;
937+ }
938+ } else {
939+ switch (evt.type )
940+ {
941+ case SDL_EVENT_JOYSTICK_AXIS_MOTION:
942+ handleAxisEvent (evt.jaxis );
943+ break ;
944+ case SDL_EVENT_JOYSTICK_BALL_MOTION:
945+ handleBallEvent (evt.jball );
946+ break ;
947+ case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
948+ case SDL_EVENT_JOYSTICK_BUTTON_UP:
949+ handleButtonEvent (evt.jbutton );
950+ break ;
951+ case SDL_EVENT_JOYSTICK_HAT_MOTION:
952+ handleHatEvent (evt.jhat );
953+ break ;
954+ default :
955+ break ;
956+ }
898957 }
899958 }
900959
@@ -907,6 +966,28 @@ namespace joystick
907966 _axisValues[axis] = evt.value ;
908967 }
909968
969+ // gamepad version of event
970+ void Joystick::handleAxisEvent (const SDL_GamepadAxisEvent &evt)
971+ {
972+ auto axis = evt.axis ;
973+ auto value = evt.value ;
974+
975+ Assertion (axis < numAxes (), " SDL event contained invalid axis index!" );
976+
977+ // Triggers range from 0..32767 so we need to scale the value for FSO
978+ // since it expects a full -32768..32767 range. Note that precision is
979+ // lost in the scaling so only scale if it's not a min/max trigger value
980+ if ((axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)) {
981+ if (value == 0 ) {
982+ value = -32768 ;
983+ } else if (value < 32767 ) {
984+ value = static_cast <decltype (value)>((value * 2 ) - 32768 );
985+ }
986+ }
987+
988+ _axisValues[axis] = value;
989+ }
990+
910991 void Joystick::handleButtonEvent (const SDL_JoyButtonEvent &evt)
911992 {
912993 auto button = evt.button ;
@@ -922,6 +1003,58 @@ namespace joystick
9221003 }
9231004 }
9241005
1006+ // gamepad version of event (we deal with dpad->hat translation here too)
1007+ void Joystick::handleButtonEvent (const SDL_GamepadButtonEvent &evt)
1008+ {
1009+ auto button = evt.button ;
1010+ auto down = evt.down ;
1011+
1012+ Assertion (button < numButtons (), " SDL event contained invalid button index!" );
1013+
1014+ // treat dpad as hat
1015+ if (button >= SDL_GAMEPAD_BUTTON_DPAD_UP && button <= SDL_GAMEPAD_BUTTON_DPAD_RIGHT) {
1016+ HatPosition hatpos;
1017+
1018+ if (numHats () != 1 ) {
1019+ return ;
1020+ }
1021+
1022+ switch (button) {
1023+ case SDL_GAMEPAD_BUTTON_DPAD_UP:
1024+ hatpos = HAT_UP;
1025+ break ;
1026+ case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
1027+ hatpos = HAT_DOWN;
1028+ break ;
1029+ case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
1030+ hatpos = HAT_LEFT;
1031+ break ;
1032+ case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
1033+ hatpos = HAT_RIGHT;
1034+ break ;
1035+ default :
1036+ return ;
1037+ }
1038+
1039+ // Set current values
1040+ _hat[0 ].Value = hatpos;
1041+
1042+ _hat[0 ].DownTimestamp4 [hatpos] = down ? ui_timestamp () : UI_TIMESTAMP::invalid ();
1043+ _hat[0 ].DownTimestamp8 [hatpos] = down ? ui_timestamp () : UI_TIMESTAMP::invalid ();
1044+
1045+ if (down) {
1046+ ++_hat[0 ].DownCount4 [hatpos];
1047+ ++_hat[0 ].DownCount8 [hatpos];
1048+ }
1049+ } else {
1050+ _button[button].DownTimestamp = down ? ui_timestamp () : UI_TIMESTAMP::invalid ();
1051+
1052+ if (down) {
1053+ ++_button[button].DownCount ;
1054+ }
1055+ }
1056+ }
1057+
9251058 void Joystick::handleHatEvent (const SDL_JoyHatEvent &evt)
9261059 {
9271060 auto hat = evt.hat ;
@@ -1019,15 +1152,13 @@ namespace joystick
10191152
10201153 mprintf ((" Initializing Joystick...\n " ));
10211154
1022- if ( !SDL_InitSubSystem (SDL_INIT_JOYSTICK) )
1155+ // NOTE: gamepad depends on joystick, so this handles both
1156+ if ( !SDL_InitSubSystem (SDL_INIT_GAMEPAD) )
10231157 {
10241158 mprintf ((" Could not initialize joystick: %s\n " , SDL_GetError ()));
10251159 return false ;
10261160 }
10271161
1028- // enable event processing of the joystick
1029- SDL_SetJoystickEventsEnabled (true );
1030-
10311162 if ( !SDL_HasJoystick () )
10321163 {
10331164 mprintf ((" No joysticks found\n " ));
@@ -1053,6 +1184,12 @@ namespace joystick
10531184 addEventListener (SDL_EVENT_JOYSTICK_ADDED, DEFAULT_LISTENER_WEIGHT, device_event_handler);
10541185 addEventListener (SDL_EVENT_JOYSTICK_REMOVED, DEFAULT_LISTENER_WEIGHT, device_event_handler);
10551186
1187+ // Gamepad events. NOTE: This is on top of joystick events, so both will be fired for gamepads!
1188+ // (we can ignore add/remove events here since the normal joystick ones will do it)
1189+ addEventListener (SDL_EVENT_GAMEPAD_AXIS_MOTION, DEFAULT_LISTENER_WEIGHT, axis_event_handler);
1190+ addEventListener (SDL_EVENT_GAMEPAD_BUTTON_DOWN, DEFAULT_LISTENER_WEIGHT, button_event_handler);
1191+ addEventListener (SDL_EVENT_GAMEPAD_BUTTON_UP, DEFAULT_LISTENER_WEIGHT, button_event_handler);
1192+
10561193 // Search for the correct stick
10571194 if (Using_in_game_options)
10581195 {
@@ -1141,7 +1278,7 @@ namespace joystick
11411278 // Automatically frees joystick resources
11421279 joysticks.clear ();
11431280
1144- SDL_QuitSubSystem (SDL_INIT_JOYSTICK );
1281+ SDL_QuitSubSystem (SDL_INIT_GAMEPAD );
11451282 }
11461283
11471284 json_t * getJsonArray () {
0 commit comments