Added support for parsing the Xbox report descriptor
This gives us more robust handling of Bluetooth Xbox controllers which may vary the report format between firmware versions. Firmware versions tested: Xbox One S: 3.1.1, 4.8.1923, 5.13.3143 Xbox One S/X: 5.11.3118, 5.23.6 Xbox Elite Series 2: 5.22.16, 5.23.6 Fixes https://github.com/libsdl-org/SDL/issues/14597
This commit is contained in:
@@ -469,6 +469,7 @@
|
|||||||
<ClInclude Include="..\..\src\joystick\controller_type.h" />
|
<ClInclude Include="..\..\src\joystick\controller_type.h" />
|
||||||
<ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapijoystick_c.h" />
|
<ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapijoystick_c.h" />
|
||||||
<ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapi_rumble.h" />
|
<ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapi_rumble.h" />
|
||||||
|
<ClInclude Include="..\..\src\joystick\hidapi\SDL_report_descriptor.h" />
|
||||||
<ClInclude Include="..\..\src\joystick\SDL_gamepad_c.h" />
|
<ClInclude Include="..\..\src\joystick\SDL_gamepad_c.h" />
|
||||||
<ClInclude Include="..\..\src\joystick\SDL_gamepad_db.h" />
|
<ClInclude Include="..\..\src\joystick\SDL_gamepad_db.h" />
|
||||||
<ClInclude Include="..\..\src\joystick\SDL_joystick_c.h" />
|
<ClInclude Include="..\..\src\joystick\SDL_joystick_c.h" />
|
||||||
@@ -744,6 +745,7 @@
|
|||||||
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xboxone.c" />
|
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xboxone.c" />
|
||||||
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_lg4ff.c" />
|
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_lg4ff.c" />
|
||||||
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_zuiki.c" />
|
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_zuiki.c" />
|
||||||
|
<ClCompile Include="..\..\src\joystick\hidapi\SDL_report_descriptor.c" />
|
||||||
<ClCompile Include="..\..\src\joystick\SDL_gamepad.c" />
|
<ClCompile Include="..\..\src\joystick\SDL_gamepad.c" />
|
||||||
<ClCompile Include="..\..\src\joystick\SDL_joystick.c" />
|
<ClCompile Include="..\..\src\joystick\SDL_joystick.c" />
|
||||||
<ClCompile Include="..\..\src\joystick\SDL_steam_virtual_gamepad.c" />
|
<ClCompile Include="..\..\src\joystick\SDL_steam_virtual_gamepad.c" />
|
||||||
|
|||||||
@@ -88,6 +88,7 @@
|
|||||||
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xboxone.c" />
|
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xboxone.c" />
|
||||||
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_lg4ff.c" />
|
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_lg4ff.c" />
|
||||||
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_zuiki.c" />
|
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_zuiki.c" />
|
||||||
|
<ClCompile Include="..\..\src\joystick\hidapi\SDL_report_descriptor.c" />
|
||||||
<ClCompile Include="..\..\src\joystick\SDL_gamepad.c" />
|
<ClCompile Include="..\..\src\joystick\SDL_gamepad.c" />
|
||||||
<ClCompile Include="..\..\src\joystick\SDL_joystick.c" />
|
<ClCompile Include="..\..\src\joystick\SDL_joystick.c" />
|
||||||
<ClCompile Include="..\..\src\joystick\SDL_steam_virtual_gamepad.c" />
|
<ClCompile Include="..\..\src\joystick\SDL_steam_virtual_gamepad.c" />
|
||||||
@@ -372,6 +373,7 @@
|
|||||||
<ClInclude Include="..\..\src\joystick\controller_type.h" />
|
<ClInclude Include="..\..\src\joystick\controller_type.h" />
|
||||||
<ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapijoystick_c.h" />
|
<ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapijoystick_c.h" />
|
||||||
<ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapi_rumble.h" />
|
<ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapi_rumble.h" />
|
||||||
|
<ClInclude Include="..\..\src\joystick\hidapi\SDL_report_descriptor.h" />
|
||||||
<ClInclude Include="..\..\src\joystick\SDL_gamepad_c.h" />
|
<ClInclude Include="..\..\src\joystick\SDL_gamepad_c.h" />
|
||||||
<ClInclude Include="..\..\src\joystick\SDL_gamepad_db.h" />
|
<ClInclude Include="..\..\src\joystick\SDL_gamepad_db.h" />
|
||||||
<ClInclude Include="..\..\src\joystick\SDL_joystick_c.h" />
|
<ClInclude Include="..\..\src\joystick\SDL_joystick_c.h" />
|
||||||
|
|||||||
@@ -383,6 +383,7 @@
|
|||||||
<ClInclude Include="..\..\src\joystick\controller_type.h" />
|
<ClInclude Include="..\..\src\joystick\controller_type.h" />
|
||||||
<ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapijoystick_c.h" />
|
<ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapijoystick_c.h" />
|
||||||
<ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapi_rumble.h" />
|
<ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapi_rumble.h" />
|
||||||
|
<ClInclude Include="..\..\src\joystick\hidapi\SDL_report_descriptor.h" />
|
||||||
<ClInclude Include="..\..\src\joystick\SDL_gamepad_c.h" />
|
<ClInclude Include="..\..\src\joystick\SDL_gamepad_c.h" />
|
||||||
<ClInclude Include="..\..\src\joystick\SDL_gamepad_db.h" />
|
<ClInclude Include="..\..\src\joystick\SDL_gamepad_db.h" />
|
||||||
<ClInclude Include="..\..\src\joystick\SDL_joystick_c.h" />
|
<ClInclude Include="..\..\src\joystick\SDL_joystick_c.h" />
|
||||||
@@ -635,6 +636,7 @@
|
|||||||
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xboxone.c" />
|
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xboxone.c" />
|
||||||
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_lg4ff.c" />
|
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_lg4ff.c" />
|
||||||
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_zuiki.c" />
|
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_zuiki.c" />
|
||||||
|
<ClCompile Include="..\..\src\joystick\hidapi\SDL_report_descriptor.c" />
|
||||||
<ClCompile Include="..\..\src\joystick\SDL_gamepad.c" />
|
<ClCompile Include="..\..\src\joystick\SDL_gamepad.c" />
|
||||||
<ClCompile Include="..\..\src\joystick\SDL_joystick.c" />
|
<ClCompile Include="..\..\src\joystick\SDL_joystick.c" />
|
||||||
<ClCompile Include="..\..\src\joystick\SDL_steam_virtual_gamepad.c" />
|
<ClCompile Include="..\..\src\joystick\SDL_steam_virtual_gamepad.c" />
|
||||||
|
|||||||
@@ -675,6 +675,9 @@
|
|||||||
<ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapi_rumble.h">
|
<ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapi_rumble.h">
|
||||||
<Filter>joystick\hidapi</Filter>
|
<Filter>joystick\hidapi</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\joystick\hidapi\SDL_report_descriptor.h">
|
||||||
|
<Filter>joystick\hidapi</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\joystick\windows\SDL_dinputjoystick_c.h">
|
<ClInclude Include="..\..\src\joystick\windows\SDL_dinputjoystick_c.h">
|
||||||
<Filter>joystick\windows</Filter>
|
<Filter>joystick\windows</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -1305,6 +1308,9 @@
|
|||||||
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapijoystick.c">
|
<ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapijoystick.c">
|
||||||
<Filter>joystick\hidapi</Filter>
|
<Filter>joystick\hidapi</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\joystick\hidapi\SDL_report_descriptor.c">
|
||||||
|
<Filter>joystick\hidapi</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\joystick\windows\SDL_dinputjoystick.c">
|
<ClCompile Include="..\..\src\joystick\windows\SDL_dinputjoystick.c">
|
||||||
<Filter>joystick\windows</Filter>
|
<Filter>joystick\windows</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 54;
|
objectVersion = 55;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXAggregateTarget section */
|
/* Begin PBXAggregateTarget section */
|
||||||
@@ -414,8 +414,8 @@
|
|||||||
F386F6F02884663E001840AA /* SDL_utils_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F386F6E52884663E001840AA /* SDL_utils_c.h */; };
|
F386F6F02884663E001840AA /* SDL_utils_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F386F6E52884663E001840AA /* SDL_utils_c.h */; };
|
||||||
F386F6F92884663E001840AA /* SDL_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = F386F6E62884663E001840AA /* SDL_utils.c */; };
|
F386F6F92884663E001840AA /* SDL_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = F386F6E62884663E001840AA /* SDL_utils.c */; };
|
||||||
F388C95528B5F6F700661ECF /* SDL_hidapi_ps3.c in Sources */ = {isa = PBXBuildFile; fileRef = F388C95428B5F6F600661ECF /* SDL_hidapi_ps3.c */; };
|
F388C95528B5F6F700661ECF /* SDL_hidapi_ps3.c in Sources */ = {isa = PBXBuildFile; fileRef = F388C95428B5F6F600661ECF /* SDL_hidapi_ps3.c */; };
|
||||||
F39344CE2E99771B0056986F /* SDL_dlopennote.h in Headers */ = {isa = PBXBuildFile; fileRef = F39344CD2E99771B0056986F /* SDL_dlopennote.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
|
||||||
F38C72492CEEB1DE000B0A90 /* SDL_hidapi_steam_triton.c in Sources */ = {isa = PBXBuildFile; fileRef = F38C72482CEEB1DE000B0A90 /* SDL_hidapi_steam_triton.c */; };
|
F38C72492CEEB1DE000B0A90 /* SDL_hidapi_steam_triton.c in Sources */ = {isa = PBXBuildFile; fileRef = F38C72482CEEB1DE000B0A90 /* SDL_hidapi_steam_triton.c */; };
|
||||||
|
F39344CE2E99771B0056986F /* SDL_dlopennote.h in Headers */ = {isa = PBXBuildFile; fileRef = F39344CD2E99771B0056986F /* SDL_dlopennote.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
F395BF6525633B2400942BFF /* SDL_crc32.c in Sources */ = {isa = PBXBuildFile; fileRef = F395BF6425633B2400942BFF /* SDL_crc32.c */; };
|
F395BF6525633B2400942BFF /* SDL_crc32.c in Sources */ = {isa = PBXBuildFile; fileRef = F395BF6425633B2400942BFF /* SDL_crc32.c */; };
|
||||||
F395C1932569C68F00942BFF /* SDL_iokitjoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F395C1912569C68E00942BFF /* SDL_iokitjoystick_c.h */; };
|
F395C1932569C68F00942BFF /* SDL_iokitjoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F395C1912569C68E00942BFF /* SDL_iokitjoystick_c.h */; };
|
||||||
F395C19C2569C68F00942BFF /* SDL_iokitjoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = F395C1922569C68E00942BFF /* SDL_iokitjoystick.c */; };
|
F395C19C2569C68F00942BFF /* SDL_iokitjoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = F395C1922569C68E00942BFF /* SDL_iokitjoystick.c */; };
|
||||||
@@ -530,6 +530,10 @@
|
|||||||
F3DDCC5B2AFD42B600B0842B /* SDL_video_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC522AFD42B600B0842B /* SDL_video_c.h */; };
|
F3DDCC5B2AFD42B600B0842B /* SDL_video_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC522AFD42B600B0842B /* SDL_video_c.h */; };
|
||||||
F3DDCC5D2AFD42B600B0842B /* SDL_rect_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC542AFD42B600B0842B /* SDL_rect_impl.h */; };
|
F3DDCC5D2AFD42B600B0842B /* SDL_rect_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC542AFD42B600B0842B /* SDL_rect_impl.h */; };
|
||||||
F3E5A6EB2AD5E0E600293D83 /* SDL_properties.c in Sources */ = {isa = PBXBuildFile; fileRef = F3E5A6EA2AD5E0E600293D83 /* SDL_properties.c */; };
|
F3E5A6EB2AD5E0E600293D83 /* SDL_properties.c in Sources */ = {isa = PBXBuildFile; fileRef = F3E5A6EA2AD5E0E600293D83 /* SDL_properties.c */; };
|
||||||
|
F3E6C3932EE9F20000A6B39E /* SDL_report_descriptor.c in Sources */ = {isa = PBXBuildFile; fileRef = F3E6C3922EE9F20000A6B39E /* SDL_report_descriptor.c */; };
|
||||||
|
F3E6C3942EE9F20000A6B39E /* SDL_hidapi_flydigi.h in Headers */ = {isa = PBXBuildFile; fileRef = F3E6C38F2EE9F20000A6B39E /* SDL_hidapi_flydigi.h */; };
|
||||||
|
F3E6C3952EE9F20000A6B39E /* SDL_hidapi_sinput.h in Headers */ = {isa = PBXBuildFile; fileRef = F3E6C3902EE9F20000A6B39E /* SDL_hidapi_sinput.h */; };
|
||||||
|
F3E6C3962EE9F20000A6B39E /* SDL_report_descriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = F3E6C3912EE9F20000A6B39E /* SDL_report_descriptor.h */; };
|
||||||
F3EFA5ED2D5AB97300BCF22F /* SDL_stb_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3EFA5EA2D5AB97300BCF22F /* SDL_stb_c.h */; };
|
F3EFA5ED2D5AB97300BCF22F /* SDL_stb_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3EFA5EA2D5AB97300BCF22F /* SDL_stb_c.h */; };
|
||||||
F3EFA5EE2D5AB97300BCF22F /* stb_image.h in Headers */ = {isa = PBXBuildFile; fileRef = F3EFA5EC2D5AB97300BCF22F /* stb_image.h */; };
|
F3EFA5EE2D5AB97300BCF22F /* stb_image.h in Headers */ = {isa = PBXBuildFile; fileRef = F3EFA5EC2D5AB97300BCF22F /* stb_image.h */; };
|
||||||
F3EFA5EF2D5AB97300BCF22F /* SDL_surface_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3EFA5EB2D5AB97300BCF22F /* SDL_surface_c.h */; };
|
F3EFA5EF2D5AB97300BCF22F /* SDL_surface_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3EFA5EB2D5AB97300BCF22F /* SDL_surface_c.h */; };
|
||||||
@@ -998,8 +1002,8 @@
|
|||||||
F386F6E52884663E001840AA /* SDL_utils_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_utils_c.h; sourceTree = "<group>"; };
|
F386F6E52884663E001840AA /* SDL_utils_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_utils_c.h; sourceTree = "<group>"; };
|
||||||
F386F6E62884663E001840AA /* SDL_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_utils.c; sourceTree = "<group>"; };
|
F386F6E62884663E001840AA /* SDL_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_utils.c; sourceTree = "<group>"; };
|
||||||
F388C95428B5F6F600661ECF /* SDL_hidapi_ps3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_ps3.c; sourceTree = "<group>"; };
|
F388C95428B5F6F600661ECF /* SDL_hidapi_ps3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_ps3.c; sourceTree = "<group>"; };
|
||||||
F39344CD2E99771B0056986F /* SDL_dlopennote.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_dlopennote.h; sourceTree = "<group>"; };
|
|
||||||
F38C72482CEEB1DE000B0A90 /* SDL_hidapi_steam_triton.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_steam_triton.c; sourceTree = "<group>"; };
|
F38C72482CEEB1DE000B0A90 /* SDL_hidapi_steam_triton.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_steam_triton.c; sourceTree = "<group>"; };
|
||||||
|
F39344CD2E99771B0056986F /* SDL_dlopennote.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_dlopennote.h; sourceTree = "<group>"; };
|
||||||
F395BF6425633B2400942BFF /* SDL_crc32.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_crc32.c; sourceTree = "<group>"; };
|
F395BF6425633B2400942BFF /* SDL_crc32.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_crc32.c; sourceTree = "<group>"; };
|
||||||
F395C1912569C68E00942BFF /* SDL_iokitjoystick_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_iokitjoystick_c.h; sourceTree = "<group>"; };
|
F395C1912569C68E00942BFF /* SDL_iokitjoystick_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_iokitjoystick_c.h; sourceTree = "<group>"; };
|
||||||
F395C1922569C68E00942BFF /* SDL_iokitjoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_iokitjoystick.c; sourceTree = "<group>"; };
|
F395C1922569C68E00942BFF /* SDL_iokitjoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_iokitjoystick.c; sourceTree = "<group>"; };
|
||||||
@@ -1113,6 +1117,10 @@
|
|||||||
F3DDCC522AFD42B600B0842B /* SDL_video_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_video_c.h; sourceTree = "<group>"; };
|
F3DDCC522AFD42B600B0842B /* SDL_video_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_video_c.h; sourceTree = "<group>"; };
|
||||||
F3DDCC542AFD42B600B0842B /* SDL_rect_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_rect_impl.h; sourceTree = "<group>"; };
|
F3DDCC542AFD42B600B0842B /* SDL_rect_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_rect_impl.h; sourceTree = "<group>"; };
|
||||||
F3E5A6EA2AD5E0E600293D83 /* SDL_properties.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_properties.c; sourceTree = "<group>"; };
|
F3E5A6EA2AD5E0E600293D83 /* SDL_properties.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_properties.c; sourceTree = "<group>"; };
|
||||||
|
F3E6C38F2EE9F20000A6B39E /* SDL_hidapi_flydigi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_hidapi_flydigi.h; sourceTree = "<group>"; };
|
||||||
|
F3E6C3902EE9F20000A6B39E /* SDL_hidapi_sinput.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_hidapi_sinput.h; sourceTree = "<group>"; };
|
||||||
|
F3E6C3912EE9F20000A6B39E /* SDL_report_descriptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_report_descriptor.h; sourceTree = "<group>"; };
|
||||||
|
F3E6C3922EE9F20000A6B39E /* SDL_report_descriptor.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_report_descriptor.c; sourceTree = "<group>"; };
|
||||||
F3EFA5E92D5AB97300BCF22F /* SDL_stb.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_stb.c; sourceTree = "<group>"; };
|
F3EFA5E92D5AB97300BCF22F /* SDL_stb.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SDL_stb.c; sourceTree = "<group>"; };
|
||||||
F3EFA5EA2D5AB97300BCF22F /* SDL_stb_c.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_stb_c.h; sourceTree = "<group>"; };
|
F3EFA5EA2D5AB97300BCF22F /* SDL_stb_c.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_stb_c.h; sourceTree = "<group>"; };
|
||||||
F3EFA5EB2D5AB97300BCF22F /* SDL_surface_c.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_surface_c.h; sourceTree = "<group>"; };
|
F3EFA5EB2D5AB97300BCF22F /* SDL_surface_c.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_surface_c.h; sourceTree = "<group>"; };
|
||||||
@@ -1948,6 +1956,7 @@
|
|||||||
children = (
|
children = (
|
||||||
F3395BA72D9A5971007246C8 /* SDL_hidapi_8bitdo.c */,
|
F3395BA72D9A5971007246C8 /* SDL_hidapi_8bitdo.c */,
|
||||||
F32305FE28939F6400E66D30 /* SDL_hidapi_combined.c */,
|
F32305FE28939F6400E66D30 /* SDL_hidapi_combined.c */,
|
||||||
|
F3E6C38F2EE9F20000A6B39E /* SDL_hidapi_flydigi.h */,
|
||||||
F3395BA72D9A5971007246C9 /* SDL_hidapi_flydigi.c */,
|
F3395BA72D9A5971007246C9 /* SDL_hidapi_flydigi.c */,
|
||||||
A7D8A7C923E2513E00DCD162 /* SDL_hidapi_gamecube.c */,
|
A7D8A7C923E2513E00DCD162 /* SDL_hidapi_gamecube.c */,
|
||||||
F3B6B8092DC3EA54004954FD /* SDL_hidapi_gip.c */,
|
F3B6B8092DC3EA54004954FD /* SDL_hidapi_gip.c */,
|
||||||
@@ -1960,6 +1969,7 @@
|
|||||||
A75FDBC323EA380300529352 /* SDL_hidapi_rumble.h */,
|
A75FDBC323EA380300529352 /* SDL_hidapi_rumble.h */,
|
||||||
A75FDBC423EA380300529352 /* SDL_hidapi_rumble.c */,
|
A75FDBC423EA380300529352 /* SDL_hidapi_rumble.c */,
|
||||||
9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */,
|
9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */,
|
||||||
|
F3E6C3902EE9F20000A6B39E /* SDL_hidapi_sinput.h */,
|
||||||
02D6A1C128A84B8F00A7F001 /* SDL_hidapi_sinput.c */,
|
02D6A1C128A84B8F00A7F001 /* SDL_hidapi_sinput.c */,
|
||||||
F3984CCF25BCC92800374F43 /* SDL_hidapi_stadia.c */,
|
F3984CCF25BCC92800374F43 /* SDL_hidapi_stadia.c */,
|
||||||
A75FDAAC23E2795C00529352 /* SDL_hidapi_steam.c */,
|
A75FDAAC23E2795C00529352 /* SDL_hidapi_steam.c */,
|
||||||
@@ -1975,6 +1985,8 @@
|
|||||||
63124A412E5C357500A53610 /* SDL_hidapi_zuiki.c */,
|
63124A412E5C357500A53610 /* SDL_hidapi_zuiki.c */,
|
||||||
A7D8A7C423E2513E00DCD162 /* SDL_hidapijoystick.c */,
|
A7D8A7C423E2513E00DCD162 /* SDL_hidapijoystick.c */,
|
||||||
A7D8A7C723E2513E00DCD162 /* SDL_hidapijoystick_c.h */,
|
A7D8A7C723E2513E00DCD162 /* SDL_hidapijoystick_c.h */,
|
||||||
|
F3E6C3912EE9F20000A6B39E /* SDL_report_descriptor.h */,
|
||||||
|
F3E6C3922EE9F20000A6B39E /* SDL_report_descriptor.c */,
|
||||||
);
|
);
|
||||||
path = hidapi;
|
path = hidapi;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -2554,6 +2566,9 @@
|
|||||||
F3D46ADB2D20625800D9CBDF /* SDL_pen.h in Headers */,
|
F3D46ADB2D20625800D9CBDF /* SDL_pen.h in Headers */,
|
||||||
F3D46ADC2D20625800D9CBDF /* SDL_render.h in Headers */,
|
F3D46ADC2D20625800D9CBDF /* SDL_render.h in Headers */,
|
||||||
F3D46ADD2D20625800D9CBDF /* SDL_assert.h in Headers */,
|
F3D46ADD2D20625800D9CBDF /* SDL_assert.h in Headers */,
|
||||||
|
F3E6C3942EE9F20000A6B39E /* SDL_hidapi_flydigi.h in Headers */,
|
||||||
|
F3E6C3952EE9F20000A6B39E /* SDL_hidapi_sinput.h in Headers */,
|
||||||
|
F3E6C3962EE9F20000A6B39E /* SDL_report_descriptor.h in Headers */,
|
||||||
F3D46ADE2D20625800D9CBDF /* SDL_atomic.h in Headers */,
|
F3D46ADE2D20625800D9CBDF /* SDL_atomic.h in Headers */,
|
||||||
F3D46ADF2D20625800D9CBDF /* SDL_begin_code.h in Headers */,
|
F3D46ADF2D20625800D9CBDF /* SDL_begin_code.h in Headers */,
|
||||||
F3D46AE02D20625800D9CBDF /* SDL_log.h in Headers */,
|
F3D46AE02D20625800D9CBDF /* SDL_log.h in Headers */,
|
||||||
@@ -2923,6 +2938,7 @@
|
|||||||
F3C1BD752D1F1A3000846529 /* SDL_tray_utils.c in Sources */,
|
F3C1BD752D1F1A3000846529 /* SDL_tray_utils.c in Sources */,
|
||||||
F382071D284F362F004DD584 /* SDL_guid.c in Sources */,
|
F382071D284F362F004DD584 /* SDL_guid.c in Sources */,
|
||||||
A7D8BB8D23E2514500DCD162 /* SDL_touch.c in Sources */,
|
A7D8BB8D23E2514500DCD162 /* SDL_touch.c in Sources */,
|
||||||
|
F3E6C3932EE9F20000A6B39E /* SDL_report_descriptor.c in Sources */,
|
||||||
F31A92D228D4CB39003BFD6A /* SDL_offscreenopengles.c in Sources */,
|
F31A92D228D4CB39003BFD6A /* SDL_offscreenopengles.c in Sources */,
|
||||||
A1626A3E2617006A003F1973 /* SDL_triangle.c in Sources */,
|
A1626A3E2617006A003F1973 /* SDL_triangle.c in Sources */,
|
||||||
A7D8B3F223E2514300DCD162 /* SDL_thread.c in Sources */,
|
A7D8B3F223E2514300DCD162 /* SDL_thread.c in Sources */,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include "../SDL_sysjoystick.h"
|
#include "../SDL_sysjoystick.h"
|
||||||
#include "SDL_hidapijoystick_c.h"
|
#include "SDL_hidapijoystick_c.h"
|
||||||
#include "SDL_hidapi_rumble.h"
|
#include "SDL_hidapi_rumble.h"
|
||||||
|
#include "SDL_report_descriptor.h"
|
||||||
|
|
||||||
#ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
|
#ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
|
||||||
|
|
||||||
@@ -33,7 +34,9 @@
|
|||||||
// #define DEBUG_JOYSTICK
|
// #define DEBUG_JOYSTICK
|
||||||
|
|
||||||
// Define this if you want to log all packets from the controller
|
// Define this if you want to log all packets from the controller
|
||||||
// #define DEBUG_XBOX_PROTOCOL
|
#if 0
|
||||||
|
#define DEBUG_XBOX_PROTOCOL
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
|
#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
|
||||||
#define XBOX_ONE_DRIVER_ACTIVE 1
|
#define XBOX_ONE_DRIVER_ACTIVE 1
|
||||||
@@ -134,6 +137,8 @@ typedef struct
|
|||||||
bool has_unmapped_state;
|
bool has_unmapped_state;
|
||||||
bool has_trigger_rumble;
|
bool has_trigger_rumble;
|
||||||
bool has_share_button;
|
bool has_share_button;
|
||||||
|
bool has_separate_back_button;
|
||||||
|
bool has_separate_guide_button;
|
||||||
Uint8 last_paddle_state;
|
Uint8 last_paddle_state;
|
||||||
Uint8 low_frequency_rumble;
|
Uint8 low_frequency_rumble;
|
||||||
Uint8 high_frequency_rumble;
|
Uint8 high_frequency_rumble;
|
||||||
@@ -142,6 +147,7 @@ typedef struct
|
|||||||
SDL_XboxOneRumbleState rumble_state;
|
SDL_XboxOneRumbleState rumble_state;
|
||||||
Uint64 rumble_time;
|
Uint64 rumble_time;
|
||||||
bool rumble_pending;
|
bool rumble_pending;
|
||||||
|
SDL_ReportDescriptor *descriptor;
|
||||||
Uint8 last_state[USB_PACKET_LENGTH];
|
Uint8 last_state[USB_PACKET_LENGTH];
|
||||||
Uint8 *chunk_buffer;
|
Uint8 *chunk_buffer;
|
||||||
Uint32 chunk_length;
|
Uint32 chunk_length;
|
||||||
@@ -375,6 +381,32 @@ static bool HIDAPI_DriverXboxOne_InitDevice(SDL_HIDAPI_Device *device)
|
|||||||
|
|
||||||
device->context = ctx;
|
device->context = ctx;
|
||||||
|
|
||||||
|
Uint8 descriptor[1024];
|
||||||
|
int descriptor_len = SDL_hid_get_report_descriptor(device->dev, descriptor, sizeof(descriptor));
|
||||||
|
if (descriptor_len > 0) {
|
||||||
|
HIDAPI_DumpPacket("Xbox One report descriptor: size = %d", descriptor, descriptor_len);
|
||||||
|
|
||||||
|
ctx->descriptor = SDL_ParseReportDescriptor(descriptor, descriptor_len);
|
||||||
|
if (ctx->descriptor) {
|
||||||
|
if (!SDL_DescriptorHasUsage(ctx->descriptor, USB_USAGEPAGE_GENERIC_DESKTOP, USB_USAGE_GENERIC_X) ||
|
||||||
|
!SDL_DescriptorHasUsage(ctx->descriptor, USB_USAGEPAGE_GENERIC_DESKTOP, USB_USAGE_GENERIC_Y) ||
|
||||||
|
!SDL_DescriptorHasUsage(ctx->descriptor, USB_USAGEPAGE_GENERIC_DESKTOP, USB_USAGE_GENERIC_Z) ||
|
||||||
|
!SDL_DescriptorHasUsage(ctx->descriptor, USB_USAGEPAGE_GENERIC_DESKTOP, USB_USAGE_GENERIC_RZ) ||
|
||||||
|
!SDL_DescriptorHasUsage(ctx->descriptor, USB_USAGEPAGE_SIMULATION, USB_USAGE_SIMULATION_BRAKE) ||
|
||||||
|
!SDL_DescriptorHasUsage(ctx->descriptor, USB_USAGEPAGE_SIMULATION, USB_USAGE_SIMULATION_ACCELERATOR) ||
|
||||||
|
!SDL_DescriptorHasUsage(ctx->descriptor, USB_USAGEPAGE_BUTTON, 1) ||
|
||||||
|
!SDL_DescriptorHasUsage(ctx->descriptor, USB_USAGEPAGE_BUTTON, 15)) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Xbox report descriptor missing expected usages, ignoring");
|
||||||
|
SDL_DestroyDescriptor(ctx->descriptor);
|
||||||
|
ctx->descriptor = NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Couldn't parse Xbox report descriptor: %s", SDL_GetError());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "Xbox report descriptor not available");
|
||||||
|
}
|
||||||
|
|
||||||
ctx->vendor_id = device->vendor_id;
|
ctx->vendor_id = device->vendor_id;
|
||||||
ctx->product_id = device->product_id;
|
ctx->product_id = device->product_id;
|
||||||
ctx->start_time = SDL_GetTicks();
|
ctx->start_time = SDL_GetTicks();
|
||||||
@@ -583,6 +615,260 @@ static bool HIDAPI_DriverXboxOne_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *de
|
|||||||
return SDL_Unsupported();
|
return SDL_Unsupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void HIDAPI_DriverXboxOne_HandleBatteryState(SDL_Joystick *joystick, unsigned int flags)
|
||||||
|
{
|
||||||
|
bool on_usb = (((flags & 0x0C) >> 2) == 0);
|
||||||
|
SDL_PowerState state;
|
||||||
|
int percent = 0;
|
||||||
|
|
||||||
|
// Mapped percentage value from:
|
||||||
|
// https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/reference/input/gameinput/interfaces/igameinputdevice/methods/igameinputdevice_getbatterystate
|
||||||
|
switch (flags & 0x03) {
|
||||||
|
case 0:
|
||||||
|
percent = 10;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
percent = 40;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
percent = 70;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
percent = 100;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (on_usb) {
|
||||||
|
state = SDL_POWERSTATE_CHARGING;
|
||||||
|
} else {
|
||||||
|
state = SDL_POWERSTATE_ON_BATTERY;
|
||||||
|
}
|
||||||
|
SDL_SendJoystickPowerInfo(joystick, state, percent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HIDAPI_DriverXboxOne_HandleDescriptorReport(SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
|
||||||
|
{
|
||||||
|
const SDL_ReportDescriptor *descriptor = ctx->descriptor;
|
||||||
|
Uint64 timestamp = SDL_GetTicksNS();
|
||||||
|
|
||||||
|
// Skip the report ID
|
||||||
|
const Uint8 report_id = *data;
|
||||||
|
++data;
|
||||||
|
--size;
|
||||||
|
|
||||||
|
for (int i = 0; i < descriptor->field_count; ++i) {
|
||||||
|
DescriptorInputField *field = &descriptor->fields[i];
|
||||||
|
if (field->report_id != report_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int value;
|
||||||
|
if (!SDL_ReadReportData(data, size, field->bit_offset, field->bit_size, &value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (field->usage) {
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_GENERIC_DESKTOP, USB_USAGE_GENERIC_X):
|
||||||
|
{
|
||||||
|
Sint16 axis = (Sint16)((int)value - 0x8000);
|
||||||
|
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_GENERIC_DESKTOP, USB_USAGE_GENERIC_Y):
|
||||||
|
{
|
||||||
|
Sint16 axis = (Sint16)((int)value - 0x8000);
|
||||||
|
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_GENERIC_DESKTOP, USB_USAGE_GENERIC_Z):
|
||||||
|
{
|
||||||
|
Sint16 axis = (Sint16)((int)value - 0x8000);
|
||||||
|
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_GENERIC_DESKTOP, USB_USAGE_GENERIC_RZ):
|
||||||
|
{
|
||||||
|
Sint16 axis = (Sint16)((int)value - 0x8000);
|
||||||
|
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_SIMULATION, USB_USAGE_SIMULATION_BRAKE):
|
||||||
|
{
|
||||||
|
Sint16 axis = (Sint16)(((int)value * 64) - 32768);
|
||||||
|
if (axis == 32704) {
|
||||||
|
axis = 32767;
|
||||||
|
}
|
||||||
|
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_SIMULATION, USB_USAGE_SIMULATION_ACCELERATOR):
|
||||||
|
{
|
||||||
|
Sint16 axis = (Sint16)(((int)value * 64) - 32768);
|
||||||
|
if (axis == 32704) {
|
||||||
|
axis = 32767;
|
||||||
|
}
|
||||||
|
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_GENERIC_DESKTOP, USB_USAGE_GENERIC_HAT):
|
||||||
|
{
|
||||||
|
Uint8 hat;
|
||||||
|
|
||||||
|
switch (value) {
|
||||||
|
case 1:
|
||||||
|
hat = SDL_HAT_UP;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
hat = SDL_HAT_RIGHTUP;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
hat = SDL_HAT_RIGHT;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
hat = SDL_HAT_RIGHTDOWN;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
hat = SDL_HAT_DOWN;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
hat = SDL_HAT_LEFTDOWN;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
hat = SDL_HAT_LEFT;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
hat = SDL_HAT_LEFTUP;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
hat = SDL_HAT_CENTERED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_BUTTON, 1):
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_BUTTON, 2):
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_BUTTON, 3):
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_BUTTON, 4):
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_BUTTON, 5):
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_BUTTON, 6):
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_BUTTON, 7):
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_BUTTON, 8):
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_BUTTON, 9):
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_BUTTON, 10):
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_BUTTON, 11):
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_BUTTON, 12):
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_BUTTON, 13):
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_BUTTON, 14):
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_BUTTON, 15):
|
||||||
|
{
|
||||||
|
static const SDL_GamepadButton button_map[] = {
|
||||||
|
// 0x0001
|
||||||
|
SDL_GAMEPAD_BUTTON_SOUTH,
|
||||||
|
// 0x0002
|
||||||
|
SDL_GAMEPAD_BUTTON_EAST,
|
||||||
|
// 0x0004
|
||||||
|
SDL_GAMEPAD_BUTTON_INVALID,
|
||||||
|
// 0x0008
|
||||||
|
SDL_GAMEPAD_BUTTON_WEST,
|
||||||
|
// 0x0010
|
||||||
|
SDL_GAMEPAD_BUTTON_NORTH,
|
||||||
|
// 0x0020
|
||||||
|
SDL_GAMEPAD_BUTTON_INVALID,
|
||||||
|
// 0x0040
|
||||||
|
SDL_GAMEPAD_BUTTON_LEFT_SHOULDER,
|
||||||
|
// 0x0080
|
||||||
|
SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER,
|
||||||
|
// 0x0100
|
||||||
|
SDL_GAMEPAD_BUTTON_INVALID,
|
||||||
|
// 0x0200
|
||||||
|
SDL_GAMEPAD_BUTTON_INVALID,
|
||||||
|
// 0x0400
|
||||||
|
SDL_GAMEPAD_BUTTON_BACK,
|
||||||
|
// 0x0800
|
||||||
|
SDL_GAMEPAD_BUTTON_START,
|
||||||
|
// 0x1000
|
||||||
|
SDL_GAMEPAD_BUTTON_GUIDE,
|
||||||
|
// 0x2000
|
||||||
|
SDL_GAMEPAD_BUTTON_LEFT_STICK,
|
||||||
|
// 0x4000
|
||||||
|
SDL_GAMEPAD_BUTTON_RIGHT_STICK,
|
||||||
|
};
|
||||||
|
|
||||||
|
int button_index = (field->usage - MAKE_USAGE(USB_USAGEPAGE_BUTTON, 1));
|
||||||
|
SDL_GamepadButton button = button_map[button_index];
|
||||||
|
if (button == SDL_GAMEPAD_BUTTON_INVALID) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (button == SDL_GAMEPAD_BUTTON_BACK && ctx->has_separate_back_button) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (button == SDL_GAMEPAD_BUTTON_GUIDE && ctx->has_separate_guide_button) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pressed = (value != 0);
|
||||||
|
SDL_SendJoystickButton(timestamp, joystick, button, pressed);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_CONSUMER, USB_USAGE_CONSUMER_AC_BACK):
|
||||||
|
{
|
||||||
|
bool pressed = (value != 0);
|
||||||
|
if (pressed) {
|
||||||
|
ctx->has_separate_back_button = true;
|
||||||
|
}
|
||||||
|
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, pressed);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_CONSUMER, USB_USAGE_CONSUMER_AC_HOME):
|
||||||
|
{
|
||||||
|
bool pressed = (value != 0);
|
||||||
|
if (pressed) {
|
||||||
|
ctx->has_separate_guide_button = true;
|
||||||
|
}
|
||||||
|
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, pressed);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_CONSUMER, USB_USAGE_CONSUMER_RECORD):
|
||||||
|
{
|
||||||
|
if (ctx->has_share_button) {
|
||||||
|
bool pressed = (value != 0);
|
||||||
|
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_XBOX_SHARE_BUTTON, pressed);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_CONSUMER, USB_USAGE_CONSUMER_ORDER_MOVIE):
|
||||||
|
{
|
||||||
|
// This value is the currently selected profile
|
||||||
|
ctx->has_unmapped_state = (value == 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_CONSUMER, USB_USAGE_CONSUMER_ASSIGN_SELECTION):
|
||||||
|
{
|
||||||
|
if (ctx->has_paddles) {
|
||||||
|
if (!ctx->has_unmapped_state) {
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint8 button = (Uint8)(SDL_GAMEPAD_BUTTON_XBOX_SHARE_BUTTON + ctx->has_share_button); // Next available button
|
||||||
|
SDL_SendJoystickButton(timestamp, joystick, button++, ((value & 0x1) != 0));
|
||||||
|
SDL_SendJoystickButton(timestamp, joystick, button++, ((value & 0x2) != 0));
|
||||||
|
SDL_SendJoystickButton(timestamp, joystick, button++, ((value & 0x4) != 0));
|
||||||
|
SDL_SendJoystickButton(timestamp, joystick, button++, ((value & 0x8) != 0));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MAKE_USAGE(USB_USAGEPAGE_DEVICE_CONTROLS, USB_USAGE_DEVICE_CONTROLS_BATTERY_STRENGTH):
|
||||||
|
{
|
||||||
|
HIDAPI_DriverXboxOne_HandleBatteryState(joystick, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The Xbox One Elite controller with 5.13+ firmware sends the unmapped state in a separate packet.
|
* The Xbox One Elite controller with 5.13+ firmware sends the unmapped state in a separate packet.
|
||||||
* We can use this to send the paddle state when they aren't mapped
|
* We can use this to send the paddle state when they aren't mapped
|
||||||
@@ -1066,33 +1352,7 @@ static void HIDAPI_DriverXboxOneBluetooth_HandleGuidePacket(SDL_Joystick *joysti
|
|||||||
|
|
||||||
static void HIDAPI_DriverXboxOneBluetooth_HandleBatteryPacket(SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, const Uint8 *data, int size)
|
static void HIDAPI_DriverXboxOneBluetooth_HandleBatteryPacket(SDL_Joystick *joystick, SDL_DriverXboxOne_Context *ctx, const Uint8 *data, int size)
|
||||||
{
|
{
|
||||||
Uint8 flags = data[1];
|
HIDAPI_DriverXboxOne_HandleBatteryState(joystick, data[1]);
|
||||||
bool on_usb = (((flags & 0x0C) >> 2) == 0);
|
|
||||||
SDL_PowerState state;
|
|
||||||
int percent = 0;
|
|
||||||
|
|
||||||
// Mapped percentage value from:
|
|
||||||
// https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/reference/input/gameinput/interfaces/igameinputdevice/methods/igameinputdevice_getbatterystate
|
|
||||||
switch (flags & 0x03) {
|
|
||||||
case 0:
|
|
||||||
percent = 10;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
percent = 40;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
percent = 70;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
percent = 100;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (on_usb) {
|
|
||||||
state = SDL_POWERSTATE_CHARGING;
|
|
||||||
} else {
|
|
||||||
state = SDL_POWERSTATE_ON_BATTERY;
|
|
||||||
}
|
|
||||||
SDL_SendJoystickPowerInfo(joystick, state, percent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void HIDAPI_DriverXboxOne_HandleSerialIDPacket(SDL_DriverXboxOne_Context *ctx, const Uint8 *data, int size)
|
static void HIDAPI_DriverXboxOne_HandleSerialIDPacket(SDL_DriverXboxOne_Context *ctx, const Uint8 *data, int size)
|
||||||
@@ -1588,7 +1848,12 @@ static bool HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device)
|
|||||||
#ifdef DEBUG_XBOX_PROTOCOL
|
#ifdef DEBUG_XBOX_PROTOCOL
|
||||||
HIDAPI_DumpPacket("Xbox One packet: size = %d", data, size);
|
HIDAPI_DumpPacket("Xbox One packet: size = %d", data, size);
|
||||||
#endif
|
#endif
|
||||||
if (device->is_bluetooth) {
|
if (ctx->descriptor) {
|
||||||
|
if (!joystick) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
HIDAPI_DriverXboxOne_HandleDescriptorReport(joystick, ctx, data, size);
|
||||||
|
} else if (device->is_bluetooth) {
|
||||||
switch (data[0]) {
|
switch (data[0]) {
|
||||||
case 0x01:
|
case 0x01:
|
||||||
if (!joystick) {
|
if (!joystick) {
|
||||||
@@ -1647,6 +1912,8 @@ static void HIDAPI_DriverXboxOne_FreeDevice(SDL_HIDAPI_Device *device)
|
|||||||
{
|
{
|
||||||
SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
|
SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
|
||||||
|
|
||||||
|
SDL_DestroyDescriptor(ctx->descriptor);
|
||||||
|
|
||||||
HIDAPI_GIP_DestroyChunkBuffer(ctx);
|
HIDAPI_GIP_DestroyChunkBuffer(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
616
src/joystick/hidapi/SDL_report_descriptor.c
Normal file
616
src/joystick/hidapi/SDL_report_descriptor.c
Normal file
@@ -0,0 +1,616 @@
|
|||||||
|
/*
|
||||||
|
Simple DirectMedia Layer
|
||||||
|
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*/
|
||||||
|
#include "SDL_internal.h"
|
||||||
|
|
||||||
|
#include "SDL_report_descriptor.h"
|
||||||
|
|
||||||
|
// This is a very simple (and non-compliant!) report descriptor parser
|
||||||
|
// used to quickly parse Xbox Bluetooth reports
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
DescriptorItemTypeMain = 0,
|
||||||
|
DescriptorItemTypeGlobal = 1,
|
||||||
|
DescriptorItemTypeLocal = 2,
|
||||||
|
DescriptorItemTypeReserved = 3,
|
||||||
|
} ItemType;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MainTagInput = 0x8,
|
||||||
|
MainTagOutput = 0x9,
|
||||||
|
MainTagFeature = 0xb,
|
||||||
|
MainTagCollection = 0xa,
|
||||||
|
MainTagEndCollection = 0xc,
|
||||||
|
} MainTag;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MainFlagConstant = 0x0001,
|
||||||
|
MainFlagVariable = 0x0002,
|
||||||
|
MainFlagRelative = 0x0004,
|
||||||
|
MainFlagWrap = 0x0008,
|
||||||
|
MainFlagNonLinear = 0x0010,
|
||||||
|
MainFlagNoPreferred = 0x0020,
|
||||||
|
MainFlagNullState = 0x0040,
|
||||||
|
MainFlagVolatile = 0x0080,
|
||||||
|
MainFlagBufferedBytes = 0x0100,
|
||||||
|
} MainFlag;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GlobalTagUsagePage = 0x0,
|
||||||
|
GlobalTagLogicalMinimum = 0x1,
|
||||||
|
GlobalTagLogicalMaximum = 0x2,
|
||||||
|
GlobalTagPhysicalMinimum = 0x3,
|
||||||
|
GlobalTagPhysicalMaximum = 0x4,
|
||||||
|
GlobalTagUnitExponent = 0x5,
|
||||||
|
GlobalTagUnit = 0x6,
|
||||||
|
GlobalTagReportSize = 0x7,
|
||||||
|
GlobalTagReportID = 0x8,
|
||||||
|
GlobalTagReportCount = 0x9,
|
||||||
|
GlobalTagPush = 0xa,
|
||||||
|
GlobalTagPop = 0xb,
|
||||||
|
} GlobalTag;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
LocalTagUsage = 0x0,
|
||||||
|
LocalTagUsageMinimum = 0x1,
|
||||||
|
LocalTagUsageMaximum = 0x2,
|
||||||
|
LocalTagDesignatorIndex = 0x3,
|
||||||
|
LocalTagDesignatorMinimum = 0x4,
|
||||||
|
LocalTagDesignatorMaximum = 0x5,
|
||||||
|
LocalTagStringIndex = 0x7,
|
||||||
|
LocalTagStringMinimum = 0x8,
|
||||||
|
LocalTagStringMaximum = 0x9,
|
||||||
|
LocalTagDelimiter = 0xa,
|
||||||
|
} LocalTag;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned int usage_page;
|
||||||
|
unsigned int report_size;
|
||||||
|
unsigned int report_count;
|
||||||
|
unsigned int report_id;
|
||||||
|
} DescriptorGlobalState;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned int usage_minimum;
|
||||||
|
unsigned int usage_maximum;
|
||||||
|
int usage_maxcount;
|
||||||
|
int usage_count;
|
||||||
|
Uint32 *usages;
|
||||||
|
} DescriptorLocalState;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int collection_depth;
|
||||||
|
DescriptorGlobalState global;
|
||||||
|
DescriptorLocalState local;
|
||||||
|
int field_maxcount;
|
||||||
|
int field_count;
|
||||||
|
int field_offset;
|
||||||
|
DescriptorInputField *fields;
|
||||||
|
} DescriptorContext;
|
||||||
|
|
||||||
|
static void DebugDescriptor(DescriptorContext *ctx, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_DESCRIPTOR
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
char *message = NULL;
|
||||||
|
SDL_vasprintf(&message, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
if (ctx->collection_depth > 0) {
|
||||||
|
size_t len = 4 * ctx->collection_depth + SDL_strlen(message) + 1;
|
||||||
|
char *output = (char *)SDL_malloc(len);
|
||||||
|
if (output) {
|
||||||
|
SDL_memset(output, ' ', 4 * ctx->collection_depth);
|
||||||
|
output[4 * ctx->collection_depth] = '\0';
|
||||||
|
SDL_strlcat(output, message, len);
|
||||||
|
SDL_free(message);
|
||||||
|
message = output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_Log("%s", message);
|
||||||
|
SDL_free(message);
|
||||||
|
#endif // DEBUG_DESCRIPTOR
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DebugMainTag(DescriptorContext *ctx, const char *tag, unsigned int flags)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_DESCRIPTOR
|
||||||
|
char message[1024] = { 0 };
|
||||||
|
|
||||||
|
SDL_strlcat(message, tag, sizeof(message));
|
||||||
|
SDL_strlcat(message, "(", sizeof(message));
|
||||||
|
if (flags & MainFlagConstant) {
|
||||||
|
SDL_strlcat(message, " Constant", sizeof(message));
|
||||||
|
} else {
|
||||||
|
SDL_strlcat(message, " Data", sizeof(message));
|
||||||
|
}
|
||||||
|
if (flags & MainFlagVariable) {
|
||||||
|
SDL_strlcat(message, " Variable", sizeof(message));
|
||||||
|
} else {
|
||||||
|
SDL_strlcat(message, " Array", sizeof(message));
|
||||||
|
}
|
||||||
|
if (flags & MainFlagRelative) {
|
||||||
|
SDL_strlcat(message, " Relative", sizeof(message));
|
||||||
|
} else {
|
||||||
|
SDL_strlcat(message, " Absolute", sizeof(message));
|
||||||
|
}
|
||||||
|
if (flags & MainFlagWrap) {
|
||||||
|
SDL_strlcat(message, " Wrap", sizeof(message));
|
||||||
|
} else {
|
||||||
|
SDL_strlcat(message, " No Wrap", sizeof(message));
|
||||||
|
}
|
||||||
|
if (flags & MainFlagNonLinear) {
|
||||||
|
SDL_strlcat(message, " Non Linear", sizeof(message));
|
||||||
|
} else {
|
||||||
|
SDL_strlcat(message, " Linear", sizeof(message));
|
||||||
|
}
|
||||||
|
if (flags & MainFlagNoPreferred) {
|
||||||
|
SDL_strlcat(message, " No Preferred", sizeof(message));
|
||||||
|
} else {
|
||||||
|
SDL_strlcat(message, " Preferred State", sizeof(message));
|
||||||
|
}
|
||||||
|
if (flags & MainFlagNullState) {
|
||||||
|
SDL_strlcat(message, " Null State", sizeof(message));
|
||||||
|
} else {
|
||||||
|
SDL_strlcat(message, " No Null Position", sizeof(message));
|
||||||
|
}
|
||||||
|
if (flags & MainFlagVolatile) {
|
||||||
|
SDL_strlcat(message, " Volatile", sizeof(message));
|
||||||
|
} else {
|
||||||
|
SDL_strlcat(message, " Non Volatile", sizeof(message));
|
||||||
|
}
|
||||||
|
if (flags & MainFlagBufferedBytes) {
|
||||||
|
SDL_strlcat(message, " Buffered Bytes", sizeof(message));
|
||||||
|
} else {
|
||||||
|
SDL_strlcat(message, " Bit Field", sizeof(message));
|
||||||
|
}
|
||||||
|
SDL_strlcat(message, " )", sizeof(message));
|
||||||
|
|
||||||
|
DebugDescriptor(ctx, "%s", message);
|
||||||
|
|
||||||
|
#endif // DEBUG_DESCRIPTOR
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int ReadValue(const Uint8 *data, int size)
|
||||||
|
{
|
||||||
|
unsigned int value = 0;
|
||||||
|
|
||||||
|
int shift = 0;
|
||||||
|
while (size--) {
|
||||||
|
value |= ((unsigned int)*data++) << shift;
|
||||||
|
shift += 8;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ResetLocalState(DescriptorContext *ctx)
|
||||||
|
{
|
||||||
|
ctx->local.usage_minimum = 0;
|
||||||
|
ctx->local.usage_maximum = 0;
|
||||||
|
ctx->local.usage_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool AddUsage(DescriptorContext *ctx, unsigned int usage)
|
||||||
|
{
|
||||||
|
if (ctx->local.usage_count == ctx->local.usage_maxcount) {
|
||||||
|
int usage_maxcount = ctx->local.usage_maxcount + 4;
|
||||||
|
Uint32 *usages = (Uint32 *)SDL_realloc(ctx->local.usages, usage_maxcount * sizeof(*usages));
|
||||||
|
if (!usages) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ctx->local.usages = usages;
|
||||||
|
ctx->local.usage_maxcount = usage_maxcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usage <= 0xFFFF) {
|
||||||
|
usage |= (ctx->global.usage_page << 16);
|
||||||
|
}
|
||||||
|
ctx->local.usages[ctx->local.usage_count++] = usage;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool AddInputField(DescriptorContext *ctx, unsigned int usage, int bit_size)
|
||||||
|
{
|
||||||
|
if (ctx->field_count == ctx->field_maxcount) {
|
||||||
|
int field_maxcount = ctx->field_maxcount + 4;
|
||||||
|
DescriptorInputField *fields = (DescriptorInputField *)SDL_realloc(ctx->fields, field_maxcount * sizeof(*fields));
|
||||||
|
if (!fields) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ctx->fields = fields;
|
||||||
|
ctx->field_maxcount = field_maxcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
DescriptorInputField *field = &ctx->fields[ctx->field_count++];
|
||||||
|
field->report_id = (Uint8)ctx->global.report_id;
|
||||||
|
field->usage = usage;
|
||||||
|
field->bit_offset = ctx->field_offset;
|
||||||
|
field->bit_size = bit_size;
|
||||||
|
|
||||||
|
DebugDescriptor(ctx, "Adding report %d field 0x%.8x size %d bits at bit offset %d", field->report_id, field->usage, field->bit_size, field->bit_offset);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool AddInputFields(DescriptorContext *ctx)
|
||||||
|
{
|
||||||
|
unsigned int usage = 0;
|
||||||
|
|
||||||
|
if (ctx->global.report_count == 0 || ctx->global.report_size == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->local.usage_count == 0 &&
|
||||||
|
ctx->local.usage_minimum > 0 &&
|
||||||
|
ctx->local.usage_maximum >= ctx->local.usage_minimum) {
|
||||||
|
for (usage = ctx->local.usage_minimum; usage <= ctx->local.usage_maximum; ++usage) {
|
||||||
|
if (!AddUsage(ctx, usage)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int usage_index = 0;
|
||||||
|
for (unsigned int i = 0; i < ctx->global.report_count; ++i) {
|
||||||
|
if (usage_index < ctx->local.usage_count) {
|
||||||
|
usage = ctx->local.usages[usage_index];
|
||||||
|
if (usage_index < (ctx->local.usage_count - 1)) {
|
||||||
|
++usage_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = (int)ctx->global.report_size;
|
||||||
|
if (usage > 0) {
|
||||||
|
if (!AddInputField(ctx, usage, size)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx->field_offset += size;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ParseMainItem(DescriptorContext *ctx, int tag, int size, const Uint8 *data)
|
||||||
|
{
|
||||||
|
unsigned int flags;
|
||||||
|
|
||||||
|
switch (tag) {
|
||||||
|
case MainTagInput:
|
||||||
|
flags = ReadValue(data, size);
|
||||||
|
DebugMainTag(ctx, "MainTagInput", flags);
|
||||||
|
AddInputFields(ctx);
|
||||||
|
break;
|
||||||
|
case MainTagOutput:
|
||||||
|
flags = ReadValue(data, size);
|
||||||
|
DebugMainTag(ctx, "MainTagOutput", flags);
|
||||||
|
break;
|
||||||
|
case MainTagFeature:
|
||||||
|
flags = ReadValue(data, size);
|
||||||
|
DebugMainTag(ctx, "MainTagFeature", flags);
|
||||||
|
break;
|
||||||
|
case MainTagCollection:
|
||||||
|
DebugDescriptor(ctx, "MainTagCollection");
|
||||||
|
switch (*data) {
|
||||||
|
case 0x00:
|
||||||
|
DebugDescriptor(ctx, "Physical");
|
||||||
|
break;
|
||||||
|
case 0x01:
|
||||||
|
DebugDescriptor(ctx, "Application");
|
||||||
|
break;
|
||||||
|
case 0x02:
|
||||||
|
DebugDescriptor(ctx, "Logical");
|
||||||
|
break;
|
||||||
|
case 0x03:
|
||||||
|
DebugDescriptor(ctx, "Report");
|
||||||
|
break;
|
||||||
|
case 0x04:
|
||||||
|
DebugDescriptor(ctx, "Named Array");
|
||||||
|
break;
|
||||||
|
case 0x05:
|
||||||
|
DebugDescriptor(ctx, "Usage Switch");
|
||||||
|
break;
|
||||||
|
case 0x06:
|
||||||
|
DebugDescriptor(ctx, "Usage Modifier");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++ctx->collection_depth;
|
||||||
|
break;
|
||||||
|
case MainTagEndCollection:
|
||||||
|
if (ctx->collection_depth > 0) {
|
||||||
|
--ctx->collection_depth;
|
||||||
|
}
|
||||||
|
DebugDescriptor(ctx, "MainTagEndCollection");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DebugDescriptor(ctx, "Unknown main tag: %d", tag);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetLocalState(ctx);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ParseGlobalItem(DescriptorContext *ctx, int tag, int size, const Uint8 *data)
|
||||||
|
{
|
||||||
|
unsigned int value;
|
||||||
|
|
||||||
|
switch (tag) {
|
||||||
|
case GlobalTagUsagePage:
|
||||||
|
ctx->global.usage_page = ReadValue(data, size);
|
||||||
|
DebugDescriptor(ctx, "GlobalTagUsagePage: 0x%.4x", ctx->global.usage_page);
|
||||||
|
break;
|
||||||
|
case GlobalTagLogicalMinimum:
|
||||||
|
value = ReadValue(data, size);
|
||||||
|
DebugDescriptor(ctx, "GlobalTagLogicalMinimum: %u", value);
|
||||||
|
break;
|
||||||
|
case GlobalTagLogicalMaximum:
|
||||||
|
value = ReadValue(data, size);
|
||||||
|
DebugDescriptor(ctx, "GlobalTagLogicalMaximum: %u", value);
|
||||||
|
break;
|
||||||
|
case GlobalTagPhysicalMinimum:
|
||||||
|
value = ReadValue(data, size);
|
||||||
|
DebugDescriptor(ctx, "GlobalTagPhysicalMinimum: %u", value);
|
||||||
|
break;
|
||||||
|
case GlobalTagPhysicalMaximum:
|
||||||
|
value = ReadValue(data, size);
|
||||||
|
DebugDescriptor(ctx, "GlobalTagPhysicalMaximum: %u", value);
|
||||||
|
break;
|
||||||
|
case GlobalTagUnitExponent:
|
||||||
|
DebugDescriptor(ctx, "GlobalTagUnitExponent");
|
||||||
|
break;
|
||||||
|
case GlobalTagUnit:
|
||||||
|
DebugDescriptor(ctx, "GlobalTagUnit");
|
||||||
|
break;
|
||||||
|
case GlobalTagReportSize:
|
||||||
|
ctx->global.report_size = ReadValue(data, size);
|
||||||
|
DebugDescriptor(ctx, "GlobalTagReportSize: %u", ctx->global.report_size);
|
||||||
|
break;
|
||||||
|
case GlobalTagReportID:
|
||||||
|
ctx->global.report_id = ReadValue(data, size);
|
||||||
|
ctx->field_offset = 0;
|
||||||
|
DebugDescriptor(ctx, "GlobalTagReportID: %u", ctx->global.report_id);
|
||||||
|
break;
|
||||||
|
case GlobalTagReportCount:
|
||||||
|
ctx->global.report_count = ReadValue(data, size);
|
||||||
|
DebugDescriptor(ctx, "GlobalTagReportCount: %u", ctx->global.report_count);
|
||||||
|
break;
|
||||||
|
case GlobalTagPush:
|
||||||
|
DebugDescriptor(ctx, "GlobalTagPush");
|
||||||
|
break;
|
||||||
|
case GlobalTagPop:
|
||||||
|
DebugDescriptor(ctx, "GlobalTagPop");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DebugDescriptor(ctx, "Unknown global tag");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ParseLocalItem(DescriptorContext *ctx, int tag, int size, const Uint8 *data)
|
||||||
|
{
|
||||||
|
unsigned int value;
|
||||||
|
|
||||||
|
switch (tag) {
|
||||||
|
case LocalTagUsage:
|
||||||
|
value = ReadValue(data, size);
|
||||||
|
AddUsage(ctx, value);
|
||||||
|
DebugDescriptor(ctx, "LocalTagUsage: 0x%.4x", value);
|
||||||
|
break;
|
||||||
|
case LocalTagUsageMinimum:
|
||||||
|
ctx->local.usage_minimum = ReadValue(data, size);
|
||||||
|
DebugDescriptor(ctx, "LocalTagUsageMinimum: 0x%.4x", ctx->local.usage_minimum);
|
||||||
|
break;
|
||||||
|
case LocalTagUsageMaximum:
|
||||||
|
ctx->local.usage_maximum = ReadValue(data, size);
|
||||||
|
DebugDescriptor(ctx, "LocalTagUsageMaximum: 0x%.4x", ctx->local.usage_maximum);
|
||||||
|
break;
|
||||||
|
case LocalTagDesignatorIndex:
|
||||||
|
DebugDescriptor(ctx, "LocalTagDesignatorIndex");
|
||||||
|
break;
|
||||||
|
case LocalTagDesignatorMinimum:
|
||||||
|
DebugDescriptor(ctx, "LocalTagDesignatorMinimum");
|
||||||
|
break;
|
||||||
|
case LocalTagDesignatorMaximum:
|
||||||
|
DebugDescriptor(ctx, "LocalTagDesignatorMaximum");
|
||||||
|
break;
|
||||||
|
case LocalTagStringIndex:
|
||||||
|
DebugDescriptor(ctx, "LocalTagStringIndex");
|
||||||
|
break;
|
||||||
|
case LocalTagStringMinimum:
|
||||||
|
DebugDescriptor(ctx, "LocalTagStringMinimum");
|
||||||
|
break;
|
||||||
|
case LocalTagStringMaximum:
|
||||||
|
DebugDescriptor(ctx, "LocalTagStringMaximum");
|
||||||
|
break;
|
||||||
|
case LocalTagDelimiter:
|
||||||
|
DebugDescriptor(ctx, "LocalTagDelimiter");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DebugDescriptor(ctx, "Unknown local tag");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParseDescriptor(DescriptorContext *ctx, const Uint8 *descriptor, int descriptor_size)
|
||||||
|
{
|
||||||
|
SDL_zerop(ctx);
|
||||||
|
|
||||||
|
for (const Uint8 *here = descriptor; here < descriptor + descriptor_size; ) {
|
||||||
|
static const int sizes[4] = { 0, 1, 2, 4 };
|
||||||
|
Uint8 data = *here++;
|
||||||
|
int size = sizes[(data & 0x3)];
|
||||||
|
int type = ((data >> 2) & 0x3);
|
||||||
|
int tag = (data >> 4);
|
||||||
|
|
||||||
|
if ((here + size) > (descriptor + descriptor_size)) {
|
||||||
|
return SDL_SetError("Invalid descriptor");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_DESCRIPTOR
|
||||||
|
SDL_Log("Data: 0x%.2x, size: %d, type: %d, tag: %d", data, size, type, tag);
|
||||||
|
#endif
|
||||||
|
switch (type) {
|
||||||
|
case DescriptorItemTypeMain:
|
||||||
|
if (!ParseMainItem(ctx, tag, size, here)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DescriptorItemTypeGlobal:
|
||||||
|
if (!ParseGlobalItem(ctx, tag, size, here)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DescriptorItemTypeLocal:
|
||||||
|
if (!ParseLocalItem(ctx, tag, size, here)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DescriptorItemTypeReserved:
|
||||||
|
// Long items are currently unsupported
|
||||||
|
return SDL_Unsupported();
|
||||||
|
}
|
||||||
|
|
||||||
|
here += size;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CleanupContext(DescriptorContext *ctx)
|
||||||
|
{
|
||||||
|
SDL_free(ctx->local.usages);
|
||||||
|
SDL_free(ctx->fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_ReportDescriptor *SDL_ParseReportDescriptor(const Uint8 *descriptor, int descriptor_size)
|
||||||
|
{
|
||||||
|
SDL_ReportDescriptor *result = NULL;
|
||||||
|
|
||||||
|
DescriptorContext ctx;
|
||||||
|
if (ParseDescriptor(&ctx, descriptor, descriptor_size)) {
|
||||||
|
result = (SDL_ReportDescriptor *)SDL_malloc(sizeof(*result));
|
||||||
|
if (result) {
|
||||||
|
result->field_count = ctx.field_count;
|
||||||
|
result->fields = ctx.fields;
|
||||||
|
ctx.fields = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CleanupContext(&ctx);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDL_DescriptorHasUsage(SDL_ReportDescriptor *descriptor, Uint16 usage_page, Uint16 usage)
|
||||||
|
{
|
||||||
|
if (!descriptor) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint32 full_usage = (((Uint32)usage_page << 16) | usage);
|
||||||
|
for (int i = 0; i < descriptor->field_count; ++i) {
|
||||||
|
if (descriptor->fields[i].usage == full_usage) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDL_DestroyDescriptor(SDL_ReportDescriptor *descriptor)
|
||||||
|
{
|
||||||
|
if (descriptor) {
|
||||||
|
SDL_free(descriptor->fields);
|
||||||
|
SDL_free(descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDL_ReadReportData(const Uint8 *data, int size, int bit_offset, int bit_size, unsigned int *value)
|
||||||
|
{
|
||||||
|
int offset = (bit_offset / 8);
|
||||||
|
if (offset >= size) {
|
||||||
|
*value = 0;
|
||||||
|
return SDL_SetError("Out of bounds reading report data");
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = ReadValue(data + offset, (bit_size + 7) / 8);
|
||||||
|
|
||||||
|
int shift = (bit_offset % 8);
|
||||||
|
if (shift > 0) {
|
||||||
|
*value >>= shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (bit_size) {
|
||||||
|
case 1:
|
||||||
|
*value &= 0x1;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
*value &= 0xf;
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
*value &= 0x3ff;
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
*value &= 0x7fff;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SDL_assert((bit_size % 8) == 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TEST_MAIN
|
||||||
|
|
||||||
|
#include <SDL3/SDL_main.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
const char *file = argv[1];
|
||||||
|
if (argc < 2) {
|
||||||
|
SDL_Log("Usage: %s file", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t descriptor_size = 0;
|
||||||
|
Uint8 *descriptor = SDL_LoadFile(argv[1], &descriptor_size);
|
||||||
|
if (!descriptor) {
|
||||||
|
SDL_Log("Couldn't load %s: %s", argv[1], SDL_GetError());
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
DescriptorContext ctx;
|
||||||
|
if (!ParseDescriptor(&ctx, descriptor, descriptor_size)) {
|
||||||
|
SDL_Log("Couldn't parse %s: %s", argv[1], SDL_GetError());
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // TEST_MAIN
|
||||||
40
src/joystick/hidapi/SDL_report_descriptor.h
Normal file
40
src/joystick/hidapi/SDL_report_descriptor.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
Simple DirectMedia Layer
|
||||||
|
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*/
|
||||||
|
#include "SDL_internal.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Uint8 report_id;
|
||||||
|
Uint32 usage;
|
||||||
|
int bit_offset;
|
||||||
|
int bit_size;
|
||||||
|
} DescriptorInputField;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int field_count;
|
||||||
|
DescriptorInputField *fields;
|
||||||
|
} SDL_ReportDescriptor;
|
||||||
|
|
||||||
|
extern SDL_ReportDescriptor *SDL_ParseReportDescriptor(const Uint8 *descriptor, int descriptor_size);
|
||||||
|
extern bool SDL_DescriptorHasUsage(SDL_ReportDescriptor *descriptor, Uint16 usage_page, Uint16 usage);
|
||||||
|
extern void SDL_DestroyDescriptor(SDL_ReportDescriptor *descriptor);
|
||||||
|
extern bool SDL_ReadReportData(const Uint8 *data, int size, int bit_offset, int bit_size, unsigned int *value);
|
||||||
@@ -182,7 +182,10 @@
|
|||||||
|
|
||||||
// USB usage pages
|
// USB usage pages
|
||||||
#define USB_USAGEPAGE_GENERIC_DESKTOP 0x0001
|
#define USB_USAGEPAGE_GENERIC_DESKTOP 0x0001
|
||||||
|
#define USB_USAGEPAGE_SIMULATION 0x0002
|
||||||
|
#define USB_USAGEPAGE_DEVICE_CONTROLS 0x0006
|
||||||
#define USB_USAGEPAGE_BUTTON 0x0009
|
#define USB_USAGEPAGE_BUTTON 0x0009
|
||||||
|
#define USB_USAGEPAGE_CONSUMER 0x000C
|
||||||
#define USB_USAGEPAGE_VENDOR_FLYDIGI 0xFFA0
|
#define USB_USAGEPAGE_VENDOR_FLYDIGI 0xFFA0
|
||||||
|
|
||||||
// USB usages for USAGE_PAGE_GENERIC_DESKTOP
|
// USB usages for USAGE_PAGE_GENERIC_DESKTOP
|
||||||
@@ -204,6 +207,22 @@
|
|||||||
#define USB_USAGE_GENERIC_WHEEL 0x0038
|
#define USB_USAGE_GENERIC_WHEEL 0x0038
|
||||||
#define USB_USAGE_GENERIC_HAT 0x0039
|
#define USB_USAGE_GENERIC_HAT 0x0039
|
||||||
|
|
||||||
|
// USB usages for USB_USAGEPAGE_SIMULATION
|
||||||
|
#define USB_USAGE_SIMULATION_ACCELERATOR 0x00C4
|
||||||
|
#define USB_USAGE_SIMULATION_BRAKE 0x00C5
|
||||||
|
|
||||||
|
// USB usages for USB_USAGEPAGE_DEVICE_CONTROLS
|
||||||
|
#define USB_USAGE_DEVICE_CONTROLS_BATTERY_STRENGTH 0x0020
|
||||||
|
|
||||||
|
// USB usages for USB_USAGEPAGE_CONSUMER
|
||||||
|
#define USB_USAGE_CONSUMER_ASSIGN_SELECTION 0x0081
|
||||||
|
#define USB_USAGE_CONSUMER_ORDER_MOVIE 0x0085
|
||||||
|
#define USB_USAGE_CONSUMER_RECORD 0x00B2
|
||||||
|
#define USB_USAGE_CONSUMER_AC_HOME 0x0223
|
||||||
|
#define USB_USAGE_CONSUMER_AC_BACK 0x0224
|
||||||
|
|
||||||
|
#define MAKE_USAGE(PAGE, USAGE) (((Uint32)PAGE) << 16 | USAGE)
|
||||||
|
|
||||||
/* Bluetooth SIG assigned Company Identifiers
|
/* Bluetooth SIG assigned Company Identifiers
|
||||||
https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/ */
|
https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/ */
|
||||||
#define BLUETOOTH_VENDOR_AMAZON 0x0171
|
#define BLUETOOTH_VENDOR_AMAZON 0x0171
|
||||||
|
|||||||
Reference in New Issue
Block a user