30
30
#include "controller_type.h"
31
31
#include "usb_ids.h"
32
32
#include "hidapi/SDL_hidapi_nintendo.h"
33
+ #include "hidapi/SDL_hidapi_sinput.h"
33
34
#include "../events/SDL_events_c.h"
34
35
35
36
54
55
#define SDL_GAMEPAD_SDKLE_FIELD "sdk<=:"
55
56
#define SDL_GAMEPAD_SDKLE_FIELD_SIZE SDL_strlen(SDL_GAMEPAD_SDKLE_FIELD)
56
57
58
+ // Helper function to add button mapping
59
+ #ifndef ADD_BUTTON_MAPPING
60
+ #define SDL_ADD_BUTTON_MAPPING (sdl_name , button_id , maxlen ) \
61
+ do { \
62
+ char temp[32]; \
63
+ (void)SDL_snprintf(temp, sizeof(temp), "%s:b%d,", sdl_name, button_id); \
64
+ SDL_strlcat(mapping_string, temp, maxlen); \
65
+ } while (0)
66
+ #endif
67
+
68
+ // Helper function to add axis mapping
69
+ #ifndef ADD_AXIS_MAPPING
70
+ #define SDL_ADD_AXIS_MAPPING (sdl_name , axis_id , maxlen ) \
71
+ do { \
72
+ char temp[32]; \
73
+ (void)SDL_snprintf(temp, sizeof(temp), "%s:a%d,", sdl_name, axis_id); \
74
+ SDL_strlcat(mapping_string, temp, maxlen); \
75
+ } while (0)
76
+ #endif
77
+
57
78
static bool SDL_gamepads_initialized ;
58
79
static SDL_Gamepad * SDL_gamepads SDL_GUARDED_BY (SDL_joystick_lock ) = NULL ;
59
80
@@ -688,6 +709,268 @@ static GamepadMapping_t *SDL_CreateMappingForAndroidGamepad(SDL_GUID guid)
688
709
}
689
710
#endif // SDL_PLATFORM_ANDROID
690
711
712
+ /*
713
+ * Helper function to apply SInput decoded styles to the mapping string
714
+ */
715
+ static inline void SDL_SInputStylesMapExtraction (SDL_SInputStyles_t * styles , char * mapping_string , size_t mapping_string_len )
716
+ {
717
+ int current_button = 0 ;
718
+ int current_axis = 0 ;
719
+ bool digital_triggers = false;
720
+ bool bumpers = false;
721
+ bool left_stick = false;
722
+ bool right_stick = false;
723
+ bool paddle_second_pair = false;
724
+
725
+ // Analog joysticks (always come first in axis mapping)
726
+ switch (styles -> analog_style ) {
727
+ case SINPUT_ANALOGSTYLE_LEFTONLY :
728
+ SDL_ADD_AXIS_MAPPING ("leftx" , current_axis ++ , mapping_string_len );
729
+ SDL_ADD_AXIS_MAPPING ("lefty" , current_axis ++ , mapping_string_len );
730
+ left_stick = true;
731
+ break ;
732
+
733
+ case SINPUT_ANALOGSTYLE_LEFTRIGHT :
734
+ SDL_ADD_AXIS_MAPPING ("leftx" , current_axis ++ , mapping_string_len );
735
+ SDL_ADD_AXIS_MAPPING ("lefty" , current_axis ++ , mapping_string_len );
736
+ SDL_ADD_AXIS_MAPPING ("rightx" , current_axis ++ , mapping_string_len );
737
+ SDL_ADD_AXIS_MAPPING ("righty" , current_axis ++ , mapping_string_len );
738
+ left_stick = true;
739
+ right_stick = true;
740
+ break ;
741
+
742
+ case SINPUT_ANALOGSTYLE_RIGHTONLY :
743
+ SDL_ADD_AXIS_MAPPING ("rightx" , current_axis ++ , mapping_string_len );
744
+ SDL_ADD_AXIS_MAPPING ("righty" , current_axis ++ , mapping_string_len );
745
+ right_stick = true;
746
+ break ;
747
+
748
+ default :
749
+ break ;
750
+ }
751
+
752
+ // Analog triggers
753
+ switch (styles -> trigger_style ) {
754
+ // Analog triggers + bumpers
755
+ case SINPUT_TRIGGERSTYLE_ANALOG :
756
+ SDL_ADD_AXIS_MAPPING ("lefttrigger" , current_axis ++ , mapping_string_len );
757
+ SDL_ADD_AXIS_MAPPING ("righttrigger" , current_axis ++ , mapping_string_len );
758
+ break ;
759
+
760
+ // Digital triggers + bumpers
761
+ case SINPUT_TRIGGERSTYLE_DIGITAL :
762
+ digital_triggers = true;
763
+ bumpers = true;
764
+ break ;
765
+
766
+ // Only bumpers
767
+ case SINPUT_TRIGGERSTYLE_BUMPERS :
768
+ bumpers = true;
769
+ break ;
770
+
771
+ default :
772
+ break ;
773
+ }
774
+
775
+ // BAYX buttons (East, South, North, West)
776
+ SDL_ADD_BUTTON_MAPPING ("b" , current_button ++ , mapping_string_len ); // East (typically B on Xbox, Circle on PlayStation)
777
+ SDL_ADD_BUTTON_MAPPING ("a" , current_button ++ , mapping_string_len ); // South (typically A on Xbox, X on PlayStation)
778
+ SDL_ADD_BUTTON_MAPPING ("y" , current_button ++ , mapping_string_len ); // North (typically Y on Xbox, Triangle on PlayStation)
779
+ SDL_ADD_BUTTON_MAPPING ("x" , current_button ++ , mapping_string_len ); // West (typically X on Xbox, Square on PlayStation)
780
+
781
+ // DPad
782
+ SDL_strlcat (mapping_string , "dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2," , mapping_string_len );
783
+
784
+ // Left and Right stick buttons
785
+ if (left_stick ) {
786
+ SDL_ADD_BUTTON_MAPPING ("leftstick" , current_button ++ , mapping_string_len );
787
+ }
788
+ if (right_stick ) {
789
+ SDL_ADD_BUTTON_MAPPING ("rightstick" , current_button ++ , mapping_string_len );
790
+ }
791
+
792
+ // Digital shoulder buttons (L/R Shoulder)
793
+ if (bumpers ) {
794
+ SDL_ADD_BUTTON_MAPPING ("leftshoulder" , current_button ++ , mapping_string_len );
795
+ SDL_ADD_BUTTON_MAPPING ("rightshoulder" , current_button ++ , mapping_string_len );
796
+ }
797
+
798
+ // Digital trigger buttons (capability overrides analog)
799
+ if (digital_triggers ) {
800
+ SDL_ADD_BUTTON_MAPPING ("lefttrigger" , current_button ++ , mapping_string_len );
801
+ SDL_ADD_BUTTON_MAPPING ("righttrigger" , current_button ++ , mapping_string_len );
802
+ }
803
+
804
+ // Paddle 1/2
805
+ switch (styles -> paddle_style ) {
806
+ case SINPUT_PADDLESTYLE_TWO :
807
+ SDL_ADD_BUTTON_MAPPING ("paddle1" , current_button ++ , mapping_string_len );
808
+ SDL_ADD_BUTTON_MAPPING ("paddle2" , current_button ++ , mapping_string_len );
809
+ break ;
810
+
811
+ case SINPUT_PADDLESTYLE_FOUR :
812
+ SDL_ADD_BUTTON_MAPPING ("paddle1" , current_button ++ , mapping_string_len );
813
+ SDL_ADD_BUTTON_MAPPING ("paddle2" , current_button ++ , mapping_string_len );
814
+ paddle_second_pair = true;
815
+ break ;
816
+
817
+ default :
818
+ break ;
819
+ }
820
+
821
+ // Start/Plus & Select/Back
822
+ SDL_ADD_BUTTON_MAPPING ("start" , current_button ++ , mapping_string_len );
823
+ SDL_ADD_BUTTON_MAPPING ("back" , current_button ++ , mapping_string_len );
824
+
825
+ switch (styles -> meta_style ) {
826
+ case SINPUT_METASTYLE_GUIDE :
827
+ SDL_ADD_BUTTON_MAPPING ("guide" , current_button ++ , mapping_string_len );
828
+ break ;
829
+
830
+ case SINPUT_METASTYLE_GUIDESHARE :
831
+ SDL_ADD_BUTTON_MAPPING ("guide" , current_button ++ , mapping_string_len );
832
+ SDL_ADD_BUTTON_MAPPING ("misc1" , current_button ++ , mapping_string_len );
833
+ break ;
834
+
835
+ default :
836
+ break ;
837
+ }
838
+
839
+ // Paddle 3/4
840
+ if (paddle_second_pair ) {
841
+ SDL_ADD_BUTTON_MAPPING ("paddle3" , current_button ++ , mapping_string_len );
842
+ SDL_ADD_BUTTON_MAPPING ("paddle4" , current_button ++ , mapping_string_len );
843
+ }
844
+
845
+ // Touchpad buttons
846
+ switch (styles -> touch_style ) {
847
+ case SINPUT_TOUCHSTYLE_SINGLE :
848
+ SDL_ADD_BUTTON_MAPPING ("touchpad" , current_button ++ , mapping_string_len );
849
+ break ;
850
+
851
+ case SINPUT_TOUCHSTYLE_DOUBLE :
852
+ SDL_ADD_BUTTON_MAPPING ("touchpad" , current_button ++ , mapping_string_len );
853
+ SDL_ADD_BUTTON_MAPPING ("misc2" , current_button ++ , mapping_string_len );
854
+ break ;
855
+
856
+ default :
857
+ break ;
858
+ }
859
+
860
+ switch (styles -> misc_style ) {
861
+ case SINPUT_MISCSTYLE_1 :
862
+ SDL_ADD_BUTTON_MAPPING ("misc3" , current_button ++ , mapping_string_len );
863
+ break ;
864
+
865
+ case SINPUT_MISCSTYLE_2 :
866
+ SDL_ADD_BUTTON_MAPPING ("misc3" , current_button ++ , mapping_string_len );
867
+ SDL_ADD_BUTTON_MAPPING ("misc4" , current_button ++ , mapping_string_len );
868
+ break ;
869
+
870
+ case SINPUT_MISCSTYLE_3 :
871
+ SDL_ADD_BUTTON_MAPPING ("misc3" , current_button ++ , mapping_string_len );
872
+ SDL_ADD_BUTTON_MAPPING ("misc4" , current_button ++ , mapping_string_len );
873
+ SDL_ADD_BUTTON_MAPPING ("misc5" , current_button ++ , mapping_string_len );
874
+ break ;
875
+
876
+ case SINPUT_MISCSTYLE_4 :
877
+ SDL_ADD_BUTTON_MAPPING ("misc3" , current_button ++ , mapping_string_len );
878
+ SDL_ADD_BUTTON_MAPPING ("misc4" , current_button ++ , mapping_string_len );
879
+ SDL_ADD_BUTTON_MAPPING ("misc5" , current_button ++ , mapping_string_len );
880
+ SDL_ADD_BUTTON_MAPPING ("misc6" , current_button ++ , mapping_string_len );
881
+ break ;
882
+
883
+ default :
884
+ break ;
885
+ }
886
+
887
+ // Remove trailing comma
888
+ size_t len = SDL_strlen (mapping_string );
889
+ if (len > 0 && mapping_string [len - 1 ] == ',' ) {
890
+ mapping_string [len - 1 ] = '\0' ;
891
+ }
892
+ }
893
+
894
+ /*
895
+ * Helper function to decode SInput features information packed into version
896
+ */
897
+ static bool SDL_CreateMappingStringForSInputGamepad (Uint16 vendor , Uint16 product , Uint8 sub_product , Uint16 version , Uint8 face_style , char * mapping_string , size_t mapping_string_len )
898
+ {
899
+ SDL_SInputStyles_t decoded = { 0 };
900
+
901
+ switch (face_style ) {
902
+ default :
903
+ SDL_strlcat (mapping_string , "face:abxy," , mapping_string_len );
904
+ break ;
905
+ case 2 :
906
+ SDL_strlcat (mapping_string , "face:axby," , mapping_string_len );
907
+ break ;
908
+ case 3 :
909
+ SDL_strlcat (mapping_string , "face:bayx," , mapping_string_len );
910
+ break ;
911
+ case 4 :
912
+ SDL_strlcat (mapping_string , "face:sony," , mapping_string_len );
913
+ break ;
914
+ }
915
+
916
+ switch (product ) {
917
+ case USB_PRODUCT_HANDHELDLEGEND_PROGCC :
918
+ switch (sub_product ) {
919
+ default :
920
+ // ProGCC Primary Mapping
921
+ SDL_strlcat (mapping_string , "a:b1,b:b0,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b4,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b10,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1," , mapping_string_len );
922
+ break ;
923
+ }
924
+ return true;
925
+
926
+ case USB_PRODUCT_HANDHELDLEGEND_GCULTIMATE :
927
+ switch (sub_product ) {
928
+ default :
929
+ // GC Ultimate Primary Map
930
+ SDL_strlcat (mapping_string , "a:b0,b:b2,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b4,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b13,misc2:b14,rightshoulder:b7,rightstick:b5,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b1,y:b3,misc3:b8,misc4:b9,hint:!SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS:=1," , mapping_string_len );
931
+ break ;
932
+ }
933
+ return true;
934
+
935
+ case USB_PRODUCT_HANDHELDLEGEND_SINPUT_GENERIC :
936
+ switch (sub_product ) {
937
+ default :
938
+ case SINPUT_GENERIC_DEVMAP :
939
+ // Default Fully Exposed Mapping (Development Purposes)
940
+ SDL_strlcat (mapping_string , "leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,b:b0,a:b1,y:b2,x:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b4,rightstick:b5,leftshoulder:b6,rightshoulder:b7,paddle1:b10,paddle2:b11,start:b12,back:b13,guide:b14,misc1:b15,paddle3:b16,paddle4:b17,touchpad:b18,misc2:b19,misc3:b20,misc4:b21,misc5:b22,misc6:b23" , mapping_string_len );
941
+ break ;
942
+
943
+ case SINPUT_GENERIC_DYNMAP :
944
+ // Decode styles for correct dynamic features
945
+ decoded .misc_style = (SINPUT_MISC_STYLE_E )(version % SINPUT_MISCSTYLE_MAX );
946
+ version /= SINPUT_MISCSTYLE_MAX ;
947
+
948
+ decoded .touch_style = (SINPUT_TOUCH_STYLE_E )(version % SINPUT_TOUCHSTYLE_MAX );
949
+ version /= SINPUT_TOUCHSTYLE_MAX ;
950
+
951
+ decoded .meta_style = (SINPUT_META_STYLE_E )(version % SINPUT_METASTYLE_MAX );
952
+ version /= SINPUT_METASTYLE_MAX ;
953
+
954
+ decoded .paddle_style = (SINPUT_PADDLE_STYLE_E )(version % SINPUT_PADDLESTYLE_MAX );
955
+ version /= SINPUT_PADDLESTYLE_MAX ;
956
+
957
+ decoded .trigger_style = (SINPUT_TRIGGER_STYLE_E )(version % SINPUT_TRIGGERSTYLE_MAX );
958
+ version /= SINPUT_TRIGGERSTYLE_MAX ;
959
+
960
+ decoded .analog_style = (SINPUT_ANALOG_STYLE_E )(version % SINPUT_ANALOGSTYLE_MAX );
961
+
962
+ SDL_SInputStylesMapExtraction (& decoded , mapping_string , mapping_string_len );
963
+ break ;
964
+ }
965
+ return true;
966
+
967
+ case USB_PRODUCT_BONZIRICHANNEL_FIREBIRD :
968
+ default :
969
+ // Unmapped device
970
+ return false;
971
+ }
972
+ }
973
+
691
974
/*
692
975
* Helper function to guess at a mapping for HIDAPI gamepads
693
976
*/
@@ -697,10 +980,11 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
697
980
char mapping_string [1024 ];
698
981
Uint16 vendor ;
699
982
Uint16 product ;
983
+ Uint16 version ;
700
984
701
985
SDL_strlcpy (mapping_string , "none,*," , sizeof (mapping_string ));
702
986
703
- SDL_GetJoystickGUIDInfo (guid , & vendor , & product , NULL , NULL );
987
+ SDL_GetJoystickGUIDInfo (guid , & vendor , & product , & version , NULL );
704
988
705
989
if (SDL_IsJoystickWheel (vendor , product )) {
706
990
// We don't want to pick up Logitech FFB wheels here
@@ -799,54 +1083,11 @@ static GamepadMapping_t *SDL_CreateMappingForHIDAPIGamepad(SDL_GUID guid)
799
1083
// This controller has no guide button
800
1084
SDL_strlcat (mapping_string , "a:b1,b:b0,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1," , sizeof (mapping_string ));
801
1085
} else if (SDL_IsJoystickSInputController (vendor , product )) {
802
- Uint8 face_style = (guid .data [15 ] & 0xE0 ) >> 5 ;
803
- Uint8 sub_type = guid .data [15 ] & 0x1F ;
804
1086
805
- // Apply face style according to gamepad response
806
- switch (face_style ) {
807
- default :
808
- SDL_strlcat (mapping_string , "face:abxy," , sizeof (mapping_string ));
809
- break ;
810
- case 2 :
811
- SDL_strlcat (mapping_string , "face:axby," , sizeof (mapping_string ));
812
- break ;
813
- case 3 :
814
- SDL_strlcat (mapping_string , "face:bayx," , sizeof (mapping_string ));
815
- break ;
816
- case 4 :
817
- SDL_strlcat (mapping_string , "face:sony," , sizeof (mapping_string ));
818
- break ;
819
- }
1087
+ Uint8 face_style = (guid .data [15 ] & 0xE0 ) >> 5 ;
1088
+ Uint8 sub_product = guid .data [15 ] & 0x1F ;
820
1089
821
- switch (product ) {
822
- case USB_PRODUCT_HANDHELDLEGEND_PROGCC :
823
- switch (sub_type ) {
824
- default :
825
- // ProGCC Primary Mapping
826
- SDL_strlcat (mapping_string , "a:b1,b:b0,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b4,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b10,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1," , sizeof (mapping_string ));
827
- break ;
828
- }
829
- break ;
830
- case USB_PRODUCT_HANDHELDLEGEND_GCULTIMATE :
831
- switch (sub_type ) {
832
- default :
833
- // GC Ultimate Primary Map
834
- SDL_strlcat (mapping_string , "a:b0,b:b2,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b4,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b13,misc2:b14,rightshoulder:b7,rightstick:b5,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b1,y:b3,misc3:b8,misc4:b9,hint:!SDL_GAMECONTROLLER_USE_GAMECUBE_LABELS:=1," , sizeof (mapping_string ));
835
- break ;
836
- }
837
- break ;
838
- case USB_PRODUCT_HANDHELDLEGEND_SINPUT_GENERIC :
839
- switch (sub_type ) {
840
- default :
841
- // Default Fully Exposed Mapping (Development Purposes)
842
- SDL_strlcat (mapping_string , "leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,b:b0,a:b1,y:b2,x:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b4,rightstick:b5,leftshoulder:b6,rightshoulder:b7,paddle1:b10,paddle2:b11,start:b12,back:b13,guide:b14,misc1:b15,paddle3:b16,paddle4:b17,touchpad:b18,misc2:b19,misc3:b20,misc4:b21,misc5:b22,misc6:b23" , sizeof (mapping_string ));
843
- break ;
844
- }
845
- break ;
846
-
847
- case USB_PRODUCT_BONZIRICHANNEL_FIREBIRD :
848
- default :
849
- // Unmapped device
1090
+ if (!SDL_CreateMappingStringForSInputGamepad (vendor , product , sub_product , version , face_style , mapping_string , sizeof (mapping_string ))) {
850
1091
return NULL ;
851
1092
}
852
1093
} else {
0 commit comments