@@ -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,43 @@ 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+
860+ } else {
861+ _button[i].DownTimestamp = UI_TIMESTAMP::invalid ();
862+ }
863+ }
864+
865+ } else {
866+ _button.resize (0 );
867+ mprintf ((" Failed to get number of buttons for joystick %s: %s\n " , _name.c_str (), SDL_GetError ()));
868+ }
830869 }
831870
832871 // Initialize hats
833872 auto hatNum = SDL_GetNumJoystickHats (_joystick);
873+ if (_isGamepad) hatNum = 1 ; // consider gamepads to always have one hat
834874 if (hatNum >= 0 ) {
835875 _hat.resize (static_cast <size_t >(hatNum));
836876 for (auto i = 0 ; i < hatNum; ++i) {
837- std::bitset<4 > hatset = SDL_GetJoystickHat (_joystick, i);
838- auto hatval = convertSDLHat (SDL_GetJoystickHat (_joystick, i));
877+ auto hatval = _isGamepad ? HAT_CENTERED : convertSDLHat (SDL_GetJoystickHat (_joystick, i));
839878 _hat[i].Value = hatval;
840879
841880 // Reset timestampts
@@ -847,21 +886,23 @@ namespace joystick
847886 }
848887
849888 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- }
889+ std::bitset<4 > hatset = SDL_GetJoystickHat (_joystick, i);
890+
891+ // Set the 4-pos timestamp(s)
892+ if ((hatset[HAT_DOWN])) {
893+ _hat[i].DownTimestamp4 [HAT_DOWN] = ui_timestamp ();
894+ }
895+ if ((hatset[HAT_UP])) {
896+ _hat[i].DownTimestamp4 [HAT_UP] = ui_timestamp ();
897+ }
898+ if ((hatset[HAT_LEFT])) {
899+ _hat[i].DownTimestamp4 [HAT_LEFT] = ui_timestamp ();
900+ }
901+ if ((hatset[HAT_RIGHT])) {
902+ _hat[i].DownTimestamp4 [HAT_RIGHT] = ui_timestamp ();
903+ }
863904
864- // Set the 8-pos timestamp
905+ // Set the 8-pos timestamp
865906 _hat[i].DownTimestamp8 [hatval] = ui_timestamp ();
866907 }
867908 }
@@ -871,30 +912,50 @@ namespace joystick
871912 }
872913 }
873914
874- SDL_Joystick *Joystick::getDevice ()
915+ SDL_Joystick *Joystick::getJoystick ()
875916 {
876917 return _joystick;
877918 }
878919
920+ SDL_Gamepad *Joystick::getGamepad ()
921+ {
922+ return _gamepad;
923+ }
924+
879925 void Joystick::handleJoyEvent (const SDL_Event &evt)
880926 {
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 ;
927+ // gamepads also get joy events, so make sure we ignore those
928+ if (isGamepad ()) {
929+ switch (evt.type ) {
930+ case SDL_EVENT_GAMEPAD_AXIS_MOTION:
931+ handleAxisEvent (evt.gaxis );
932+ break ;
933+ case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
934+ case SDL_EVENT_GAMEPAD_BUTTON_UP:
935+ handleButtonEvent (evt.gbutton );
936+ break ;
937+ default :
938+ break ;
939+ }
940+ } else {
941+ switch (evt.type )
942+ {
943+ case SDL_EVENT_JOYSTICK_AXIS_MOTION:
944+ handleAxisEvent (evt.jaxis );
945+ break ;
946+ case SDL_EVENT_JOYSTICK_BALL_MOTION:
947+ handleBallEvent (evt.jball );
948+ break ;
949+ case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
950+ case SDL_EVENT_JOYSTICK_BUTTON_UP:
951+ handleButtonEvent (evt.jbutton );
952+ break ;
953+ case SDL_EVENT_JOYSTICK_HAT_MOTION:
954+ handleHatEvent (evt.jhat );
955+ break ;
956+ default :
957+ break ;
958+ }
898959 }
899960 }
900961
@@ -907,6 +968,16 @@ namespace joystick
907968 _axisValues[axis] = evt.value ;
908969 }
909970
971+ // gamepad version of event (same code, but different struct)
972+ void Joystick::handleAxisEvent (const SDL_GamepadAxisEvent &evt)
973+ {
974+ auto axis = evt.axis ;
975+
976+ Assertion (axis < numAxes (), " SDL event contained invalid axis index!" );
977+
978+ _axisValues[axis] = evt.value ;
979+ }
980+
910981 void Joystick::handleButtonEvent (const SDL_JoyButtonEvent &evt)
911982 {
912983 auto button = evt.button ;
@@ -922,6 +993,58 @@ namespace joystick
922993 }
923994 }
924995
996+ // gamepad version of event (we deal with dpad->hat translation here too)
997+ void Joystick::handleButtonEvent (const SDL_GamepadButtonEvent &evt)
998+ {
999+ auto button = evt.button ;
1000+ auto down = evt.down ;
1001+
1002+ Assertion (button < numButtons (), " SDL event contained invalid button index!" );
1003+
1004+ // treat dpad as hat
1005+ if (button >= SDL_GAMEPAD_BUTTON_DPAD_UP && button <= SDL_GAMEPAD_BUTTON_DPAD_RIGHT) {
1006+ HatPosition hatpos;
1007+
1008+ if (numHats () != 1 ) {
1009+ return ;
1010+ }
1011+
1012+ switch (button) {
1013+ case SDL_GAMEPAD_BUTTON_DPAD_UP:
1014+ hatpos = HAT_UP;
1015+ break ;
1016+ case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
1017+ hatpos = HAT_DOWN;
1018+ break ;
1019+ case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
1020+ hatpos = HAT_LEFT;
1021+ break ;
1022+ case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
1023+ hatpos = HAT_RIGHT;
1024+ break ;
1025+ default :
1026+ return ;
1027+ }
1028+
1029+ // Set current values
1030+ _hat[0 ].Value = hatpos;
1031+
1032+ _hat[0 ].DownTimestamp4 [hatpos] = down ? ui_timestamp () : UI_TIMESTAMP::invalid ();
1033+ _hat[0 ].DownTimestamp8 [hatpos] = down ? ui_timestamp () : UI_TIMESTAMP::invalid ();
1034+
1035+ if (down) {
1036+ ++_hat[0 ].DownCount4 [hatpos];
1037+ ++_hat[0 ].DownCount8 [hatpos];
1038+ }
1039+ } else {
1040+ _button[button].DownTimestamp = down ? ui_timestamp () : UI_TIMESTAMP::invalid ();
1041+
1042+ if (down) {
1043+ ++_button[button].DownCount ;
1044+ }
1045+ }
1046+ }
1047+
9251048 void Joystick::handleHatEvent (const SDL_JoyHatEvent &evt)
9261049 {
9271050 auto hat = evt.hat ;
@@ -1019,15 +1142,13 @@ namespace joystick
10191142
10201143 mprintf ((" Initializing Joystick...\n " ));
10211144
1022- if ( !SDL_InitSubSystem (SDL_INIT_JOYSTICK) )
1145+ // NOTE: gamepad depends on joystick, so this handles both
1146+ if ( !SDL_InitSubSystem (SDL_INIT_GAMEPAD) )
10231147 {
10241148 mprintf ((" Could not initialize joystick: %s\n " , SDL_GetError ()));
10251149 return false ;
10261150 }
10271151
1028- // enable event processing of the joystick
1029- SDL_SetJoystickEventsEnabled (true );
1030-
10311152 if ( !SDL_HasJoystick () )
10321153 {
10331154 mprintf ((" No joysticks found\n " ));
@@ -1053,6 +1174,12 @@ namespace joystick
10531174 addEventListener (SDL_EVENT_JOYSTICK_ADDED, DEFAULT_LISTENER_WEIGHT, device_event_handler);
10541175 addEventListener (SDL_EVENT_JOYSTICK_REMOVED, DEFAULT_LISTENER_WEIGHT, device_event_handler);
10551176
1177+ // Gamepad events. NOTE: This is on top of joystick events, so both will be fired for gamepads!
1178+ // (we can ignore add/remove events here since the normal joystick ones will do it)
1179+ addEventListener (SDL_EVENT_GAMEPAD_AXIS_MOTION, DEFAULT_LISTENER_WEIGHT, axis_event_handler);
1180+ addEventListener (SDL_EVENT_GAMEPAD_BUTTON_DOWN, DEFAULT_LISTENER_WEIGHT, button_event_handler);
1181+ addEventListener (SDL_EVENT_GAMEPAD_BUTTON_UP, DEFAULT_LISTENER_WEIGHT, button_event_handler);
1182+
10561183 // Search for the correct stick
10571184 if (Using_in_game_options)
10581185 {
@@ -1141,7 +1268,7 @@ namespace joystick
11411268 // Automatically frees joystick resources
11421269 joysticks.clear ();
11431270
1144- SDL_QuitSubSystem (SDL_INIT_JOYSTICK );
1271+ SDL_QuitSubSystem (SDL_INIT_GAMEPAD );
11451272 }
11461273
11471274 json_t * getJsonArray () {
0 commit comments