Added HIDAPI joystick drivers for more consistent support for Xbox, PS4 and Nintendo Switch Pro controller support across platforms.
authorSam Lantinga <slouken@libsdl.org>
Thu, 09 Aug 2018 16:00:17 -0700
changeset 12088399cc39583cc
parent 12087 cb546477e34f
child 12089 d544367b1ef0
Added HIDAPI joystick drivers for more consistent support for Xbox, PS4 and Nintendo Switch Pro controller support across platforms.
Added SDL_GameControllerRumble() and SDL_JoystickRumble() for simple force feedback outside of the SDL haptics API
Android.mk
VisualC/SDL/SDL.vcxproj
VisualC/SDL/SDL.vcxproj.filters
Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj
Xcode/SDL/SDL.xcodeproj/project.pbxproj
android-project/app/src/main/java/org/libsdl/app/HIDDevice.java
android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java
android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java
android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
configure
configure.in
include/SDL_config.h.in
include/SDL_config_android.h
include/SDL_config_iphoneos.h
include/SDL_config_macosx.h
include/SDL_config_windows.h
include/SDL_gamecontroller.h
include/SDL_haptic.h
include/SDL_hints.h
include/SDL_joystick.h
include/SDL_stdinc.h
src/dynapi/SDL_dynapi_overrides.h
src/dynapi/SDL_dynapi_procs.h
src/joystick/SDL_gamecontroller.c
src/joystick/SDL_gamecontrollerdb.h
src/joystick/SDL_joystick.c
src/joystick/SDL_joystick_c.h
src/joystick/SDL_sysjoystick.h
src/joystick/android/SDL_sysjoystick.c
src/joystick/android/SDL_sysjoystick_c.h
src/joystick/darwin/SDL_sysjoystick.c
src/joystick/darwin/SDL_sysjoystick_c.h
src/joystick/dummy/SDL_sysjoystick.c
src/joystick/hidapi/SDL_hidapi_ps4.c
src/joystick/hidapi/SDL_hidapi_switch.c
src/joystick/hidapi/SDL_hidapi_xbox360.c
src/joystick/hidapi/SDL_hidapi_xboxone.c
src/joystick/hidapi/SDL_hidapijoystick.c
src/joystick/hidapi/SDL_hidapijoystick_c.h
src/joystick/iphoneos/SDL_sysjoystick.m
src/joystick/iphoneos/SDL_sysjoystick_c.h
src/joystick/linux/SDL_sysjoystick.c
src/joystick/linux/SDL_sysjoystick_c.h
src/joystick/windows/SDL_dinputjoystick.c
src/joystick/windows/SDL_dinputjoystick_c.h
src/joystick/windows/SDL_windowsjoystick.c
src/joystick/windows/SDL_windowsjoystick_c.h
src/joystick/windows/SDL_xinputjoystick.c
src/joystick/windows/SDL_xinputjoystick_c.h
src/stdlib/SDL_string.c
test/testgamecontroller.c
test/testjoystick.c
     1.1 --- a/Android.mk	Wed Aug 08 15:39:20 2018 -0400
     1.2 +++ b/Android.mk	Thu Aug 09 16:00:17 2018 -0700
     1.3 @@ -31,7 +31,7 @@
     1.4  	$(wildcard $(LOCAL_PATH)/src/haptic/android/*.c) \
     1.5  	$(wildcard $(LOCAL_PATH)/src/joystick/*.c) \
     1.6  	$(wildcard $(LOCAL_PATH)/src/joystick/android/*.c) \
     1.7 -	$(LOCAL_PATH)/src/joystick/steam/SDL_steamcontroller.c \
     1.8 +	$(wildcard $(LOCAL_PATH)/src/joystick/hidapi/*.c) \
     1.9  	$(wildcard $(LOCAL_PATH)/src/loadso/dlopen/*.c) \
    1.10  	$(wildcard $(LOCAL_PATH)/src/power/*.c) \
    1.11  	$(wildcard $(LOCAL_PATH)/src/power/android/*.c) \
    1.12 @@ -48,6 +48,8 @@
    1.13  	$(wildcard $(LOCAL_PATH)/src/video/yuv2rgb/*.c) \
    1.14  	$(wildcard $(LOCAL_PATH)/src/test/*.c))
    1.15  
    1.16 +LOCAL_SHARED_LIBRARIES := hidapi
    1.17 +
    1.18  LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES
    1.19  LOCAL_LDLIBS := -ldl -lGLESv1_CM -lGLESv2 -llog -landroid
    1.20  
    1.21 @@ -88,4 +90,19 @@
    1.22  
    1.23  include $(BUILD_STATIC_LIBRARY)
    1.24  
    1.25 +###########################
    1.26 +#
    1.27 +# hidapi library
    1.28 +#
    1.29 +###########################
    1.30  
    1.31 +include $(CLEAR_VARS)
    1.32 +
    1.33 +LOCAL_CPPFLAGS += -std=c++11
    1.34 +
    1.35 +LOCAL_SRC_FILES := $(LOCAL_PATH)/src/hidapi/android/hid.cpp
    1.36 +
    1.37 +LOCAL_MODULE := libhidapi
    1.38 +LOCAL_LDLIBS := -llog
    1.39 +
    1.40 +include $(BUILD_SHARED_LIBRARY)
     2.1 --- a/VisualC/SDL/SDL.vcxproj	Wed Aug 08 15:39:20 2018 -0400
     2.2 +++ b/VisualC/SDL/SDL.vcxproj	Thu Aug 09 16:00:17 2018 -0700
     2.3 @@ -80,6 +80,18 @@
     2.4      <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
     2.5      <LibraryPath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Lib\x86;$(LibraryPath)</LibraryPath>
     2.6    </PropertyGroup>
     2.7 +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     2.8 +    <IncludePath>D:\dev\steam\rel\streaming_client\SDL\src\hidapi\hidapi;$(IncludePath)</IncludePath>
     2.9 +  </PropertyGroup>
    2.10 +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    2.11 +    <IncludePath>D:\dev\steam\rel\streaming_client\SDL\src\hidapi\hidapi;$(IncludePath)</IncludePath>
    2.12 +  </PropertyGroup>
    2.13 +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    2.14 +    <IncludePath>D:\dev\steam\rel\streaming_client\SDL\src\hidapi\hidapi;$(IncludePath)</IncludePath>
    2.15 +  </PropertyGroup>
    2.16 +  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    2.17 +    <IncludePath>D:\dev\steam\rel\streaming_client\SDL\src\hidapi\hidapi;$(IncludePath)</IncludePath>
    2.18 +  </PropertyGroup>
    2.19    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    2.20      <PreBuildEvent>
    2.21        <Command>
    2.22 @@ -109,7 +121,7 @@
    2.23        <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    2.24      </ResourceCompile>
    2.25      <Link>
    2.26 -      <AdditionalDependencies>winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
    2.27 +      <AdditionalDependencies>setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
    2.28        <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
    2.29        <GenerateDebugInformation>true</GenerateDebugInformation>
    2.30        <SubSystem>Windows</SubSystem>
    2.31 @@ -140,7 +152,7 @@
    2.32        <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    2.33      </ResourceCompile>
    2.34      <Link>
    2.35 -      <AdditionalDependencies>winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
    2.36 +      <AdditionalDependencies>setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
    2.37        <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
    2.38        <GenerateDebugInformation>true</GenerateDebugInformation>
    2.39        <SubSystem>Windows</SubSystem>
    2.40 @@ -174,7 +186,7 @@
    2.41        <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    2.42      </ResourceCompile>
    2.43      <Link>
    2.44 -      <AdditionalDependencies>winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
    2.45 +      <AdditionalDependencies>setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
    2.46        <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
    2.47        <GenerateDebugInformation>true</GenerateDebugInformation>
    2.48        <SubSystem>Windows</SubSystem>
    2.49 @@ -206,7 +218,7 @@
    2.50        <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    2.51      </ResourceCompile>
    2.52      <Link>
    2.53 -      <AdditionalDependencies>winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
    2.54 +      <AdditionalDependencies>setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
    2.55        <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
    2.56        <GenerateDebugInformation>true</GenerateDebugInformation>
    2.57        <SubSystem>Windows</SubSystem>
    2.58 @@ -318,6 +330,8 @@
    2.59      <ClInclude Include="..\..\src\haptic\windows\SDL_dinputhaptic_c.h" />
    2.60      <ClInclude Include="..\..\src\haptic\windows\SDL_windowshaptic_c.h" />
    2.61      <ClInclude Include="..\..\src\haptic\windows\SDL_xinputhaptic_c.h" />
    2.62 +    <ClInclude Include="..\..\src\joystick\hidapi\controller_type.h" />
    2.63 +    <ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapijoystick_c.h" />
    2.64      <ClInclude Include="..\..\src\joystick\SDL_joystick_c.h" />
    2.65      <ClInclude Include="..\..\src\joystick\SDL_sysjoystick.h" />
    2.66      <ClInclude Include="..\..\src\joystick\windows\SDL_dinputjoystick_c.h" />
    2.67 @@ -411,6 +425,12 @@
    2.68      <ClCompile Include="..\..\src\haptic\windows\SDL_dinputhaptic.c" />
    2.69      <ClCompile Include="..\..\src\haptic\windows\SDL_windowshaptic.c" />
    2.70      <ClCompile Include="..\..\src\haptic\windows\SDL_xinputhaptic.c" />
    2.71 +    <ClCompile Include="..\..\src\hidapi\windows\hid.c" />
    2.72 +    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapijoystick.c" />
    2.73 +    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_ps4.c" />
    2.74 +    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_switch.c" />
    2.75 +    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xbox360.c" />
    2.76 +    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xboxone.c" />
    2.77      <ClCompile Include="..\..\src\joystick\SDL_gamecontroller.c" />
    2.78      <ClCompile Include="..\..\src\joystick\SDL_joystick.c" />
    2.79      <ClCompile Include="..\..\src\joystick\windows\SDL_dinputjoystick.c" />
    2.80 @@ -523,4 +543,4 @@
    2.81    <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
    2.82    <ImportGroup Label="ExtensionTargets">
    2.83    </ImportGroup>
    2.84 -</Project>
    2.85 +</Project>
    2.86 \ No newline at end of file
     3.1 --- a/VisualC/SDL/SDL.vcxproj.filters	Wed Aug 08 15:39:20 2018 -0400
     3.2 +++ b/VisualC/SDL/SDL.vcxproj.filters	Thu Aug 09 16:00:17 2018 -0700
     3.3 @@ -1,461 +1,469 @@
     3.4 -<?xml version="1.0" encoding="utf-8"?>
     3.5 -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
     3.6 -  <ItemGroup>
     3.7 -    <Filter Include="API Headers">
     3.8 -      <UniqueIdentifier>{395b3af0-33d0-411b-b153-de1676bf1ef8}</UniqueIdentifier>
     3.9 -    </Filter>
    3.10 -  </ItemGroup>
    3.11 -  <ItemGroup>
    3.12 -    <ClInclude Include="..\..\include\begin_code.h">
    3.13 -      <Filter>API Headers</Filter>
    3.14 -    </ClInclude>
    3.15 -    <ClInclude Include="..\..\include\close_code.h">
    3.16 -      <Filter>API Headers</Filter>
    3.17 -    </ClInclude>
    3.18 -    <ClInclude Include="..\..\include\SDL.h">
    3.19 -      <Filter>API Headers</Filter>
    3.20 -    </ClInclude>
    3.21 -    <ClInclude Include="..\..\include\SDL_assert.h">
    3.22 -      <Filter>API Headers</Filter>
    3.23 -    </ClInclude>
    3.24 -    <ClInclude Include="..\..\include\SDL_atomic.h">
    3.25 -      <Filter>API Headers</Filter>
    3.26 -    </ClInclude>
    3.27 -    <ClInclude Include="..\..\include\SDL_audio.h">
    3.28 -      <Filter>API Headers</Filter>
    3.29 -    </ClInclude>
    3.30 -    <ClInclude Include="..\..\include\SDL_bits.h">
    3.31 -      <Filter>API Headers</Filter>
    3.32 -    </ClInclude>
    3.33 -    <ClInclude Include="..\..\include\SDL_blendmode.h">
    3.34 -      <Filter>API Headers</Filter>
    3.35 -    </ClInclude>
    3.36 -    <ClInclude Include="..\..\include\SDL_clipboard.h">
    3.37 -      <Filter>API Headers</Filter>
    3.38 -    </ClInclude>
    3.39 -    <ClInclude Include="..\..\include\SDL_config.h">
    3.40 -      <Filter>API Headers</Filter>
    3.41 -    </ClInclude>
    3.42 -    <ClInclude Include="..\..\include\SDL_config_windows.h">
    3.43 -      <Filter>API Headers</Filter>
    3.44 -    </ClInclude>
    3.45 -    <ClInclude Include="..\..\include\SDL_copying.h">
    3.46 -      <Filter>API Headers</Filter>
    3.47 -    </ClInclude>
    3.48 -    <ClInclude Include="..\..\include\SDL_cpuinfo.h">
    3.49 -      <Filter>API Headers</Filter>
    3.50 -    </ClInclude>
    3.51 -    <ClInclude Include="..\..\include\SDL_egl.h">
    3.52 -      <Filter>API Headers</Filter>
    3.53 -    </ClInclude>
    3.54 -    <ClInclude Include="..\..\include\SDL_endian.h">
    3.55 -      <Filter>API Headers</Filter>
    3.56 -    </ClInclude>
    3.57 -    <ClInclude Include="..\..\include\SDL_error.h">
    3.58 -      <Filter>API Headers</Filter>
    3.59 -    </ClInclude>
    3.60 -    <ClInclude Include="..\..\include\SDL_events.h">
    3.61 -      <Filter>API Headers</Filter>
    3.62 -    </ClInclude>
    3.63 -    <ClInclude Include="..\..\include\SDL_filesystem.h">
    3.64 -      <Filter>API Headers</Filter>
    3.65 -    </ClInclude>
    3.66 -    <ClInclude Include="..\..\include\SDL_gamecontroller.h">
    3.67 -      <Filter>API Headers</Filter>
    3.68 -    </ClInclude>
    3.69 -    <ClInclude Include="..\..\include\SDL_gesture.h">
    3.70 -      <Filter>API Headers</Filter>
    3.71 -    </ClInclude>
    3.72 -    <ClInclude Include="..\..\include\SDL_haptic.h">
    3.73 -      <Filter>API Headers</Filter>
    3.74 -    </ClInclude>
    3.75 -    <ClInclude Include="..\..\include\SDL_hints.h">
    3.76 -      <Filter>API Headers</Filter>
    3.77 -    </ClInclude>
    3.78 -    <ClInclude Include="..\..\include\SDL_joystick.h">
    3.79 -      <Filter>API Headers</Filter>
    3.80 -    </ClInclude>
    3.81 -    <ClInclude Include="..\..\include\SDL_keyboard.h">
    3.82 -      <Filter>API Headers</Filter>
    3.83 -    </ClInclude>
    3.84 -    <ClInclude Include="..\..\include\SDL_keycode.h">
    3.85 -      <Filter>API Headers</Filter>
    3.86 -    </ClInclude>
    3.87 -    <ClInclude Include="..\..\include\SDL_loadso.h">
    3.88 -      <Filter>API Headers</Filter>
    3.89 -    </ClInclude>
    3.90 -    <ClInclude Include="..\..\include\SDL_log.h">
    3.91 -      <Filter>API Headers</Filter>
    3.92 -    </ClInclude>
    3.93 -    <ClInclude Include="..\..\include\SDL_main.h">
    3.94 -      <Filter>API Headers</Filter>
    3.95 -    </ClInclude>
    3.96 -    <ClInclude Include="..\..\include\SDL_messagebox.h">
    3.97 -      <Filter>API Headers</Filter>
    3.98 -    </ClInclude>
    3.99 -    <ClInclude Include="..\..\include\SDL_mouse.h">
   3.100 -      <Filter>API Headers</Filter>
   3.101 -    </ClInclude>
   3.102 -    <ClInclude Include="..\..\include\SDL_mutex.h">
   3.103 -      <Filter>API Headers</Filter>
   3.104 -    </ClInclude>
   3.105 -    <ClInclude Include="..\..\include\SDL_name.h">
   3.106 -      <Filter>API Headers</Filter>
   3.107 -    </ClInclude>
   3.108 -    <ClInclude Include="..\..\include\SDL_opengl.h">
   3.109 -      <Filter>API Headers</Filter>
   3.110 -    </ClInclude>
   3.111 -    <ClInclude Include="..\..\include\SDL_opengl_glext.h">
   3.112 -      <Filter>API Headers</Filter>
   3.113 -    </ClInclude>
   3.114 -    <ClInclude Include="..\..\include\SDL_opengles.h">
   3.115 -      <Filter>API Headers</Filter>
   3.116 -    </ClInclude>
   3.117 -    <ClInclude Include="..\..\include\SDL_opengles2.h">
   3.118 -      <Filter>API Headers</Filter>
   3.119 -    </ClInclude>
   3.120 -    <ClInclude Include="..\..\include\SDL_opengles2_gl2.h">
   3.121 -      <Filter>API Headers</Filter>
   3.122 -    </ClInclude>
   3.123 -    <ClInclude Include="..\..\include\SDL_opengles2_gl2ext.h">
   3.124 -      <Filter>API Headers</Filter>
   3.125 -    </ClInclude>
   3.126 -    <ClInclude Include="..\..\include\SDL_opengles2_gl2platform.h">
   3.127 -      <Filter>API Headers</Filter>
   3.128 -    </ClInclude>
   3.129 -    <ClInclude Include="..\..\include\SDL_opengles2_khrplatform.h">
   3.130 -      <Filter>API Headers</Filter>
   3.131 -    </ClInclude>
   3.132 -    <ClInclude Include="..\..\include\SDL_pixels.h">
   3.133 -      <Filter>API Headers</Filter>
   3.134 -    </ClInclude>
   3.135 -    <ClInclude Include="..\..\include\SDL_platform.h">
   3.136 -      <Filter>API Headers</Filter>
   3.137 -    </ClInclude>
   3.138 -    <ClInclude Include="..\..\include\SDL_power.h">
   3.139 -      <Filter>API Headers</Filter>
   3.140 -    </ClInclude>
   3.141 -    <ClInclude Include="..\..\include\SDL_quit.h">
   3.142 -      <Filter>API Headers</Filter>
   3.143 -    </ClInclude>
   3.144 -    <ClInclude Include="..\..\include\SDL_rect.h">
   3.145 -      <Filter>API Headers</Filter>
   3.146 -    </ClInclude>
   3.147 -    <ClInclude Include="..\..\include\SDL_render.h">
   3.148 -      <Filter>API Headers</Filter>
   3.149 -    </ClInclude>
   3.150 -    <ClInclude Include="..\..\include\SDL_revision.h">
   3.151 -      <Filter>API Headers</Filter>
   3.152 -    </ClInclude>
   3.153 -    <ClInclude Include="..\..\include\SDL_rwops.h">
   3.154 -      <Filter>API Headers</Filter>
   3.155 -    </ClInclude>
   3.156 -    <ClInclude Include="..\..\include\SDL_scancode.h">
   3.157 -      <Filter>API Headers</Filter>
   3.158 -    </ClInclude>
   3.159 -    <ClInclude Include="..\..\include\SDL_shape.h">
   3.160 -      <Filter>API Headers</Filter>
   3.161 -    </ClInclude>
   3.162 -    <ClInclude Include="..\..\include\SDL_stdinc.h">
   3.163 -      <Filter>API Headers</Filter>
   3.164 -    </ClInclude>
   3.165 -    <ClInclude Include="..\..\include\SDL_surface.h">
   3.166 -      <Filter>API Headers</Filter>
   3.167 -    </ClInclude>
   3.168 -    <ClInclude Include="..\..\include\SDL_system.h">
   3.169 -      <Filter>API Headers</Filter>
   3.170 -    </ClInclude>
   3.171 -    <ClInclude Include="..\..\include\SDL_syswm.h">
   3.172 -      <Filter>API Headers</Filter>
   3.173 -    </ClInclude>
   3.174 -    <ClInclude Include="..\..\include\SDL_test.h">
   3.175 -      <Filter>API Headers</Filter>
   3.176 -    </ClInclude>
   3.177 -    <ClInclude Include="..\..\include\SDL_test_assert.h">
   3.178 -      <Filter>API Headers</Filter>
   3.179 -    </ClInclude>
   3.180 -    <ClInclude Include="..\..\include\SDL_test_common.h">
   3.181 -      <Filter>API Headers</Filter>
   3.182 -    </ClInclude>
   3.183 -    <ClInclude Include="..\..\include\SDL_test_compare.h">
   3.184 -      <Filter>API Headers</Filter>
   3.185 -    </ClInclude>
   3.186 -    <ClInclude Include="..\..\include\SDL_test_crc32.h">
   3.187 -      <Filter>API Headers</Filter>
   3.188 -    </ClInclude>
   3.189 -    <ClInclude Include="..\..\include\SDL_test_font.h">
   3.190 -      <Filter>API Headers</Filter>
   3.191 -    </ClInclude>
   3.192 -    <ClInclude Include="..\..\include\SDL_test_fuzzer.h">
   3.193 -      <Filter>API Headers</Filter>
   3.194 -    </ClInclude>
   3.195 -    <ClInclude Include="..\..\include\SDL_test_harness.h">
   3.196 -      <Filter>API Headers</Filter>
   3.197 -    </ClInclude>
   3.198 -    <ClInclude Include="..\..\include\SDL_test_images.h">
   3.199 -      <Filter>API Headers</Filter>
   3.200 -    </ClInclude>
   3.201 -    <ClInclude Include="..\..\include\SDL_test_log.h">
   3.202 -      <Filter>API Headers</Filter>
   3.203 -    </ClInclude>
   3.204 -    <ClInclude Include="..\..\include\SDL_test_md5.h">
   3.205 -      <Filter>API Headers</Filter>
   3.206 -    </ClInclude>
   3.207 -    <ClInclude Include="..\..\include\SDL_test_random.h">
   3.208 -      <Filter>API Headers</Filter>
   3.209 -    </ClInclude>
   3.210 -    <ClInclude Include="..\..\include\SDL_thread.h">
   3.211 -      <Filter>API Headers</Filter>
   3.212 -    </ClInclude>
   3.213 -    <ClInclude Include="..\..\include\SDL_timer.h">
   3.214 -      <Filter>API Headers</Filter>
   3.215 -    </ClInclude>
   3.216 -    <ClInclude Include="..\..\include\SDL_touch.h">
   3.217 -      <Filter>API Headers</Filter>
   3.218 -    </ClInclude>
   3.219 -    <ClInclude Include="..\..\include\SDL_types.h">
   3.220 -      <Filter>API Headers</Filter>
   3.221 -    </ClInclude>
   3.222 -    <ClInclude Include="..\..\include\SDL_version.h">
   3.223 -      <Filter>API Headers</Filter>
   3.224 -    </ClInclude>
   3.225 -    <ClInclude Include="..\..\include\SDL_video.h">
   3.226 -      <Filter>API Headers</Filter>
   3.227 -    </ClInclude>
   3.228 -    <ClInclude Include="..\..\include\SDL_vulkan.h">
   3.229 -      <Filter>API Headers</Filter>
   3.230 -    </ClInclude>
   3.231 -    <ClInclude Include="..\..\src\audio\directsound\SDL_directsound.h" />
   3.232 -    <ClInclude Include="..\..\src\audio\disk\SDL_diskaudio.h" />
   3.233 -    <ClInclude Include="..\..\src\audio\dummy\SDL_dummyaudio.h" />
   3.234 -    <ClInclude Include="..\..\src\audio\SDL_audio_c.h" />
   3.235 -    <ClInclude Include="..\..\src\audio\SDL_audiodev_c.h" />
   3.236 -    <ClInclude Include="..\..\src\audio\SDL_sysaudio.h" />
   3.237 -    <ClInclude Include="..\..\src\audio\SDL_wave.h" />
   3.238 -    <ClInclude Include="..\..\src\audio\wasapi\SDL_wasapi.h" />
   3.239 -    <ClInclude Include="..\..\src\audio\winmm\SDL_winmm.h" />
   3.240 -    <ClInclude Include="..\..\src\core\windows\SDL_directx.h" />
   3.241 -    <ClInclude Include="..\..\src\core\windows\SDL_windows.h" />
   3.242 -    <ClInclude Include="..\..\src\core\windows\SDL_xinput.h" />
   3.243 -    <ClInclude Include="..\..\src\dynapi\SDL_dynapi.h" />
   3.244 -    <ClInclude Include="..\..\src\dynapi\SDL_dynapi_overrides.h" />
   3.245 -    <ClInclude Include="..\..\src\dynapi\SDL_dynapi_procs.h" />
   3.246 -    <ClInclude Include="..\..\src\events\blank_cursor.h" />
   3.247 -    <ClInclude Include="..\..\src\events\default_cursor.h" />
   3.248 -    <ClInclude Include="..\..\src\events\SDL_clipboardevents_c.h" />
   3.249 -    <ClInclude Include="..\..\src\events\SDL_dropevents_c.h" />
   3.250 -    <ClInclude Include="..\..\src\events\SDL_events_c.h" />
   3.251 -    <ClInclude Include="..\..\src\events\SDL_gesture_c.h" />
   3.252 -    <ClInclude Include="..\..\src\events\SDL_keyboard_c.h" />
   3.253 -    <ClInclude Include="..\..\src\events\SDL_mouse_c.h" />
   3.254 -    <ClInclude Include="..\..\src\events\SDL_sysevents.h" />
   3.255 -    <ClInclude Include="..\..\src\events\SDL_touch_c.h" />
   3.256 -    <ClInclude Include="..\..\src\events\SDL_windowevents_c.h" />
   3.257 -    <ClInclude Include="..\..\src\haptic\SDL_syshaptic.h" />
   3.258 -    <ClInclude Include="..\..\src\haptic\windows\SDL_dinputhaptic_c.h" />
   3.259 -    <ClInclude Include="..\..\src\haptic\windows\SDL_windowshaptic_c.h" />
   3.260 -    <ClInclude Include="..\..\src\haptic\windows\SDL_xinputhaptic_c.h" />
   3.261 -    <ClInclude Include="..\..\src\joystick\SDL_joystick_c.h" />
   3.262 -    <ClInclude Include="..\..\src\joystick\SDL_sysjoystick.h" />
   3.263 -    <ClInclude Include="..\..\src\joystick\windows\SDL_dinputjoystick_c.h" />
   3.264 -    <ClInclude Include="..\..\src\joystick\windows\SDL_windowsjoystick_c.h" />
   3.265 -    <ClInclude Include="..\..\src\joystick\windows\SDL_xinputjoystick_c.h" />
   3.266 -    <ClInclude Include="..\..\src\libm\math_libm.h" />
   3.267 -    <ClInclude Include="..\..\src\libm\math_private.h" />
   3.268 -    <ClInclude Include="..\..\src\render\opengl\SDL_glfuncs.h" />
   3.269 -    <ClInclude Include="..\..\src\render\opengl\SDL_shaders_gl.h" />
   3.270 -    <ClInclude Include="..\..\src\render\opengles\SDL_glesfuncs.h" />
   3.271 -    <ClInclude Include="..\..\src\render\SDL_d3dmath.h" />
   3.272 -    <ClInclude Include="..\..\src\render\SDL_sysrender.h" />
   3.273 -    <ClInclude Include="..\..\src\render\SDL_yuv_sw_c.h" />
   3.274 -    <ClInclude Include="..\..\src\render\software\SDL_blendfillrect.h" />
   3.275 -    <ClInclude Include="..\..\src\render\software\SDL_blendline.h" />
   3.276 -    <ClInclude Include="..\..\src\render\software\SDL_blendpoint.h" />
   3.277 -    <ClInclude Include="..\..\src\render\software\SDL_draw.h" />
   3.278 -    <ClInclude Include="..\..\src\render\software\SDL_drawline.h" />
   3.279 -    <ClInclude Include="..\..\src\render\software\SDL_drawpoint.h" />
   3.280 -    <ClInclude Include="..\..\src\render\software\SDL_render_sw_c.h" />
   3.281 -    <ClInclude Include="..\..\src\render\software\SDL_rotate.h" />
   3.282 -    <ClInclude Include="..\..\src\SDL_dataqueue.h" />
   3.283 -    <ClInclude Include="..\..\src\SDL_error_c.h" />
   3.284 -    <ClInclude Include="..\..\src\thread\SDL_systhread.h" />
   3.285 -    <ClInclude Include="..\..\src\thread\SDL_thread_c.h" />
   3.286 -    <ClInclude Include="..\..\src\thread\windows\SDL_systhread_c.h" />
   3.287 -    <ClInclude Include="..\..\src\timer\SDL_timer_c.h" />
   3.288 -    <ClInclude Include="..\..\src\video\dummy\SDL_nullevents_c.h" />
   3.289 -    <ClInclude Include="..\..\src\video\dummy\SDL_nullframebuffer_c.h" />
   3.290 -    <ClInclude Include="..\..\src\video\dummy\SDL_nullvideo.h" />
   3.291 -    <ClInclude Include="..\..\src\video\SDL_blit.h" />
   3.292 -    <ClInclude Include="..\..\src\video\SDL_blit_auto.h" />
   3.293 -    <ClInclude Include="..\..\src\video\SDL_blit_copy.h" />
   3.294 -    <ClInclude Include="..\..\src\video\SDL_blit_slow.h" />
   3.295 -    <ClInclude Include="..\..\src\video\SDL_pixels_c.h" />
   3.296 -    <ClInclude Include="..\..\src\video\SDL_rect_c.h" />
   3.297 -    <ClInclude Include="..\..\src\video\SDL_RLEaccel_c.h" />
   3.298 -    <ClInclude Include="..\..\src\video\SDL_shape_internals.h" />
   3.299 -    <ClInclude Include="..\..\src\video\SDL_sysvideo.h" />
   3.300 -    <ClInclude Include="..\..\src\video\SDL_vulkan_internal.h" />
   3.301 -    <ClInclude Include="..\..\src\video\windows\SDL_vkeys.h" />
   3.302 -    <ClInclude Include="..\..\src\video\windows\SDL_windowsclipboard.h" />
   3.303 -    <ClInclude Include="..\..\src\video\windows\SDL_windowsevents.h" />
   3.304 -    <ClInclude Include="..\..\src\video\windows\SDL_windowsframebuffer.h" />
   3.305 -    <ClInclude Include="..\..\src\video\windows\SDL_windowskeyboard.h" />
   3.306 -    <ClInclude Include="..\..\src\video\windows\SDL_windowsmessagebox.h" />
   3.307 -    <ClInclude Include="..\..\src\video\windows\SDL_windowsmodes.h" />
   3.308 -    <ClInclude Include="..\..\src\video\windows\SDL_windowsmouse.h" />
   3.309 -    <ClInclude Include="..\..\src\video\windows\SDL_windowsopengl.h" />
   3.310 -    <ClInclude Include="..\..\src\video\windows\SDL_windowsshape.h" />
   3.311 -    <ClInclude Include="..\..\src\video\windows\SDL_windowsvideo.h" />
   3.312 -    <ClInclude Include="..\..\src\video\windows\SDL_windowsvulkan.h" />
   3.313 -    <ClInclude Include="..\..\src\video\windows\SDL_windowswindow.h" />
   3.314 -    <ClInclude Include="..\..\src\video\windows\wmmsg.h" />
   3.315 -    <ClInclude Include="..\..\src\video\SDL_yuv_c.h" />
   3.316 -    <ClInclude Include="..\..\src\video\yuv2rgb\yuv_rgb.h" />
   3.317 -    <ClInclude Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.h" />
   3.318 -    <ClInclude Include="..\..\src\render\direct3d\SDL_shaders_d3d.h" />
   3.319 -  </ItemGroup>
   3.320 -  <ItemGroup>
   3.321 -    <ClCompile Include="..\..\src\libm\e_atan2.c" />
   3.322 -    <ClCompile Include="..\..\src\libm\e_exp.c" />
   3.323 -    <ClCompile Include="..\..\src\libm\e_log.c" />
   3.324 -    <ClCompile Include="..\..\src\libm\e_log10.c" />
   3.325 -    <ClCompile Include="..\..\src\libm\e_pow.c" />
   3.326 -    <ClCompile Include="..\..\src\libm\e_rem_pio2.c" />
   3.327 -    <ClCompile Include="..\..\src\libm\e_sqrt.c" />
   3.328 -    <ClCompile Include="..\..\src\libm\k_cos.c" />
   3.329 -    <ClCompile Include="..\..\src\libm\k_rem_pio2.c" />
   3.330 -    <ClCompile Include="..\..\src\libm\k_sin.c" />
   3.331 -    <ClCompile Include="..\..\src\libm\k_tan.c" />
   3.332 -    <ClCompile Include="..\..\src\libm\s_atan.c" />
   3.333 -    <ClCompile Include="..\..\src\libm\s_copysign.c" />
   3.334 -    <ClCompile Include="..\..\src\libm\s_cos.c" />
   3.335 -    <ClCompile Include="..\..\src\libm\s_fabs.c" />
   3.336 -    <ClCompile Include="..\..\src\libm\s_floor.c" />
   3.337 -    <ClCompile Include="..\..\src\libm\s_scalbn.c" />
   3.338 -    <ClCompile Include="..\..\src\libm\s_sin.c" />
   3.339 -    <ClCompile Include="..\..\src\libm\s_tan.c" />
   3.340 -    <ClCompile Include="..\..\src\SDL.c" />
   3.341 -    <ClCompile Include="..\..\src\SDL_assert.c" />
   3.342 -    <ClCompile Include="..\..\src\atomic\SDL_atomic.c" />
   3.343 -    <ClCompile Include="..\..\src\audio\SDL_audio.c" />
   3.344 -    <ClCompile Include="..\..\src\audio\SDL_audiocvt.c" />
   3.345 -    <ClCompile Include="..\..\src\audio\SDL_audiodev.c" />
   3.346 -    <ClCompile Include="..\..\src\audio\SDL_audiotypecvt.c" />
   3.347 -    <ClCompile Include="..\..\src\render\software\SDL_blendfillrect.c" />
   3.348 -    <ClCompile Include="..\..\src\render\software\SDL_blendline.c" />
   3.349 -    <ClCompile Include="..\..\src\render\software\SDL_blendpoint.c" />
   3.350 -    <ClCompile Include="..\..\src\video\SDL_blit.c" />
   3.351 -    <ClCompile Include="..\..\src\video\SDL_blit_0.c" />
   3.352 -    <ClCompile Include="..\..\src\video\SDL_blit_1.c" />
   3.353 -    <ClCompile Include="..\..\src\video\SDL_blit_A.c" />
   3.354 -    <ClCompile Include="..\..\src\video\SDL_blit_auto.c" />
   3.355 -    <ClCompile Include="..\..\src\video\SDL_blit_copy.c" />
   3.356 -    <ClCompile Include="..\..\src\video\SDL_blit_N.c" />
   3.357 -    <ClCompile Include="..\..\src\video\SDL_blit_slow.c" />
   3.358 -    <ClCompile Include="..\..\src\video\SDL_bmp.c" />
   3.359 -    <ClCompile Include="..\..\src\video\SDL_clipboard.c" />
   3.360 -    <ClCompile Include="..\..\src\events\SDL_clipboardevents.c" />
   3.361 -    <ClCompile Include="..\..\src\cpuinfo\SDL_cpuinfo.c" />
   3.362 -    <ClCompile Include="..\..\src\render\SDL_d3dmath.c" />
   3.363 -    <ClCompile Include="..\..\src\haptic\windows\SDL_dinputhaptic.c" />
   3.364 -    <ClCompile Include="..\..\src\joystick\windows\SDL_dinputjoystick.c" />
   3.365 -    <ClCompile Include="..\..\src\audio\directsound\SDL_directsound.c" />
   3.366 -    <ClCompile Include="..\..\src\audio\disk\SDL_diskaudio.c" />
   3.367 -    <ClCompile Include="..\..\src\render\software\SDL_drawline.c" />
   3.368 -    <ClCompile Include="..\..\src\render\software\SDL_drawpoint.c" />
   3.369 -    <ClCompile Include="..\..\src\events\SDL_dropevents.c" />
   3.370 -    <ClCompile Include="..\..\src\audio\dummy\SDL_dummyaudio.c" />
   3.371 -    <ClCompile Include="..\..\src\dynapi\SDL_dynapi.c" />
   3.372 -    <ClCompile Include="..\..\src\video\SDL_egl.c" />
   3.373 -    <ClCompile Include="..\..\src\SDL_dataqueue.c" />
   3.374 -    <ClCompile Include="..\..\src\SDL_error.c" />
   3.375 -    <ClCompile Include="..\..\src\events\SDL_events.c" />
   3.376 -    <ClCompile Include="..\..\src\video\SDL_fillrect.c" />
   3.377 -    <ClCompile Include="..\..\src\joystick\SDL_gamecontroller.c" />
   3.378 -    <ClCompile Include="..\..\src\events\SDL_gesture.c" />
   3.379 -    <ClCompile Include="..\..\src\stdlib\SDL_getenv.c" />
   3.380 -    <ClCompile Include="..\..\src\haptic\SDL_haptic.c" />
   3.381 -    <ClCompile Include="..\..\src\SDL_hints.c" />
   3.382 -    <ClCompile Include="..\..\src\stdlib\SDL_iconv.c" />
   3.383 -    <ClCompile Include="..\..\src\joystick\SDL_joystick.c" />
   3.384 -    <ClCompile Include="..\..\src\events\SDL_keyboard.c" />
   3.385 -    <ClCompile Include="..\..\src\SDL_log.c" />
   3.386 -    <ClCompile Include="..\..\src\stdlib\SDL_malloc.c" />
   3.387 -    <ClCompile Include="..\..\src\audio\SDL_mixer.c" />
   3.388 -    <ClCompile Include="..\..\src\joystick\windows\SDL_mmjoystick.c" />
   3.389 -    <ClCompile Include="..\..\src\events\SDL_mouse.c" />
   3.390 -    <ClCompile Include="..\..\src\video\dummy\SDL_nullevents.c" />
   3.391 -    <ClCompile Include="..\..\src\video\dummy\SDL_nullframebuffer.c" />
   3.392 -    <ClCompile Include="..\..\src\video\dummy\SDL_nullvideo.c" />
   3.393 -    <ClCompile Include="..\..\src\video\SDL_pixels.c" />
   3.394 -    <ClCompile Include="..\..\src\power\SDL_power.c" />
   3.395 -    <ClCompile Include="..\..\src\stdlib\SDL_qsort.c" />
   3.396 -    <ClCompile Include="..\..\src\events\SDL_quit.c" />
   3.397 -    <ClCompile Include="..\..\src\video\SDL_rect.c" />
   3.398 -    <ClCompile Include="..\..\src\render\SDL_render.c" />
   3.399 -    <ClCompile Include="..\..\src\render\direct3d\SDL_render_d3d.c" />
   3.400 -    <ClCompile Include="..\..\src\render\direct3d11\SDL_render_d3d11.c" />
   3.401 -    <ClCompile Include="..\..\src\render\opengl\SDL_render_gl.c" />
   3.402 -    <ClCompile Include="..\..\src\render\opengles2\SDL_render_gles2.c" />
   3.403 -    <ClCompile Include="..\..\src\render\software\SDL_render_sw.c" />
   3.404 -    <ClCompile Include="..\..\src\video\SDL_RLEaccel.c" />
   3.405 -    <ClCompile Include="..\..\src\render\software\SDL_rotate.c" />
   3.406 -    <ClCompile Include="..\..\src\file\SDL_rwops.c" />
   3.407 -    <ClCompile Include="..\..\src\render\opengl\SDL_shaders_gl.c" />
   3.408 -    <ClCompile Include="..\..\src\render\opengles2\SDL_shaders_gles2.c" />
   3.409 -    <ClCompile Include="..\..\src\video\SDL_shape.c" />
   3.410 -    <ClCompile Include="..\..\src\atomic\SDL_spinlock.c" />
   3.411 -    <ClCompile Include="..\..\src\stdlib\SDL_stdlib.c" />
   3.412 -    <ClCompile Include="..\..\src\video\SDL_stretch.c" />
   3.413 -    <ClCompile Include="..\..\src\stdlib\SDL_string.c" />
   3.414 -    <ClCompile Include="..\..\src\video\SDL_surface.c" />
   3.415 -    <ClCompile Include="..\..\src\thread\generic\SDL_syscond.c" />
   3.416 -    <ClCompile Include="..\..\src\filesystem\windows\SDL_sysfilesystem.c" />
   3.417 -    <ClCompile Include="..\..\src\loadso\windows\SDL_sysloadso.c" />
   3.418 -    <ClCompile Include="..\..\src\thread\windows\SDL_sysmutex.c" />
   3.419 -    <ClCompile Include="..\..\src\power\windows\SDL_syspower.c" />
   3.420 -    <ClCompile Include="..\..\src\thread\windows\SDL_syssem.c" />
   3.421 -    <ClCompile Include="..\..\src\thread\windows\SDL_systhread.c" />
   3.422 -    <ClCompile Include="..\..\src\timer\windows\SDL_systimer.c" />
   3.423 -    <ClCompile Include="..\..\src\thread\windows\SDL_systls.c" />
   3.424 -    <ClCompile Include="..\..\src\thread\SDL_thread.c" />
   3.425 -    <ClCompile Include="..\..\src\timer\SDL_timer.c" />
   3.426 -    <ClCompile Include="..\..\src\events\SDL_touch.c" />
   3.427 -    <ClCompile Include="..\..\src\video\SDL_video.c" />
   3.428 -    <ClCompile Include="..\..\src\audio\SDL_wave.c" />
   3.429 -    <ClCompile Include="..\..\src\events\SDL_windowevents.c" />
   3.430 -    <ClCompile Include="..\..\src\core\windows\SDL_windows.c" />
   3.431 -    <ClCompile Include="..\..\src\video\windows\SDL_windowsclipboard.c" />
   3.432 -    <ClCompile Include="..\..\src\video\windows\SDL_windowsevents.c" />
   3.433 -    <ClCompile Include="..\..\src\video\windows\SDL_windowsframebuffer.c" />
   3.434 -    <ClCompile Include="..\..\src\haptic\windows\SDL_windowshaptic.c" />
   3.435 -    <ClCompile Include="..\..\src\joystick\windows\SDL_windowsjoystick.c" />
   3.436 -    <ClCompile Include="..\..\src\video\windows\SDL_windowskeyboard.c" />
   3.437 -    <ClCompile Include="..\..\src\video\windows\SDL_windowsmessagebox.c" />
   3.438 -    <ClCompile Include="..\..\src\video\windows\SDL_windowsmodes.c" />
   3.439 -    <ClCompile Include="..\..\src\video\windows\SDL_windowsmouse.c" />
   3.440 -    <ClCompile Include="..\..\src\video\windows\SDL_windowsopengl.c" />
   3.441 -    <ClCompile Include="..\..\src\video\windows\SDL_windowsopengles.c" />
   3.442 -    <ClCompile Include="..\..\src\video\windows\SDL_windowsshape.c" />
   3.443 -    <ClCompile Include="..\..\src\video\windows\SDL_windowsvideo.c" />
   3.444 -    <ClCompile Include="..\..\src\video\windows\SDL_windowswindow.c" />
   3.445 -    <ClCompile Include="..\..\src\audio\winmm\SDL_winmm.c" />
   3.446 -    <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
   3.447 -    <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi_win32.c" />
   3.448 -    <ClCompile Include="..\..\src\core\windows\SDL_xinput.c" />
   3.449 -    <ClCompile Include="..\..\src\haptic\windows\SDL_xinputhaptic.c" />
   3.450 -    <ClCompile Include="..\..\src\joystick\windows\SDL_xinputjoystick.c" />
   3.451 -    <ClCompile Include="..\..\src\render\SDL_yuv_sw.c" />
   3.452 -    <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
   3.453 -    <ClCompile Include="..\..\src\video\SDL_vulkan_utils.c" />
   3.454 -    <ClCompile Include="..\..\src\video\windows\SDL_windowsvulkan.c" />
   3.455 -    <ClCompile Include="..\..\src\libm\e_fmod.c" />
   3.456 -    <ClCompile Include="..\..\src\video\SDL_yuv.c" />
   3.457 -    <ClCompile Include="..\..\src\video\yuv2rgb\yuv_rgb.c" />
   3.458 -    <ClCompile Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.c" />
   3.459 -    <ClCompile Include="..\..\src\render\direct3d\SDL_shaders_d3d.c" />
   3.460 -  </ItemGroup>
   3.461 -  <ItemGroup>
   3.462 -    <ResourceCompile Include="..\..\src\main\windows\version.rc" />
   3.463 -  </ItemGroup>
   3.464 +???<?xml version="1.0" encoding="utf-8"?>
   3.465 +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   3.466 +  <ItemGroup>
   3.467 +    <Filter Include="API Headers">
   3.468 +      <UniqueIdentifier>{395b3af0-33d0-411b-b153-de1676bf1ef8}</UniqueIdentifier>
   3.469 +    </Filter>
   3.470 +  </ItemGroup>
   3.471 +  <ItemGroup>
   3.472 +    <ClInclude Include="..\..\include\begin_code.h">
   3.473 +      <Filter>API Headers</Filter>
   3.474 +    </ClInclude>
   3.475 +    <ClInclude Include="..\..\include\close_code.h">
   3.476 +      <Filter>API Headers</Filter>
   3.477 +    </ClInclude>
   3.478 +    <ClInclude Include="..\..\include\SDL.h">
   3.479 +      <Filter>API Headers</Filter>
   3.480 +    </ClInclude>
   3.481 +    <ClInclude Include="..\..\include\SDL_assert.h">
   3.482 +      <Filter>API Headers</Filter>
   3.483 +    </ClInclude>
   3.484 +    <ClInclude Include="..\..\include\SDL_atomic.h">
   3.485 +      <Filter>API Headers</Filter>
   3.486 +    </ClInclude>
   3.487 +    <ClInclude Include="..\..\include\SDL_audio.h">
   3.488 +      <Filter>API Headers</Filter>
   3.489 +    </ClInclude>
   3.490 +    <ClInclude Include="..\..\include\SDL_bits.h">
   3.491 +      <Filter>API Headers</Filter>
   3.492 +    </ClInclude>
   3.493 +    <ClInclude Include="..\..\include\SDL_blendmode.h">
   3.494 +      <Filter>API Headers</Filter>
   3.495 +    </ClInclude>
   3.496 +    <ClInclude Include="..\..\include\SDL_clipboard.h">
   3.497 +      <Filter>API Headers</Filter>
   3.498 +    </ClInclude>
   3.499 +    <ClInclude Include="..\..\include\SDL_config.h">
   3.500 +      <Filter>API Headers</Filter>
   3.501 +    </ClInclude>
   3.502 +    <ClInclude Include="..\..\include\SDL_config_windows.h">
   3.503 +      <Filter>API Headers</Filter>
   3.504 +    </ClInclude>
   3.505 +    <ClInclude Include="..\..\include\SDL_copying.h">
   3.506 +      <Filter>API Headers</Filter>
   3.507 +    </ClInclude>
   3.508 +    <ClInclude Include="..\..\include\SDL_cpuinfo.h">
   3.509 +      <Filter>API Headers</Filter>
   3.510 +    </ClInclude>
   3.511 +    <ClInclude Include="..\..\include\SDL_egl.h">
   3.512 +      <Filter>API Headers</Filter>
   3.513 +    </ClInclude>
   3.514 +    <ClInclude Include="..\..\include\SDL_endian.h">
   3.515 +      <Filter>API Headers</Filter>
   3.516 +    </ClInclude>
   3.517 +    <ClInclude Include="..\..\include\SDL_error.h">
   3.518 +      <Filter>API Headers</Filter>
   3.519 +    </ClInclude>
   3.520 +    <ClInclude Include="..\..\include\SDL_events.h">
   3.521 +      <Filter>API Headers</Filter>
   3.522 +    </ClInclude>
   3.523 +    <ClInclude Include="..\..\include\SDL_filesystem.h">
   3.524 +      <Filter>API Headers</Filter>
   3.525 +    </ClInclude>
   3.526 +    <ClInclude Include="..\..\include\SDL_gamecontroller.h">
   3.527 +      <Filter>API Headers</Filter>
   3.528 +    </ClInclude>
   3.529 +    <ClInclude Include="..\..\include\SDL_gesture.h">
   3.530 +      <Filter>API Headers</Filter>
   3.531 +    </ClInclude>
   3.532 +    <ClInclude Include="..\..\include\SDL_haptic.h">
   3.533 +      <Filter>API Headers</Filter>
   3.534 +    </ClInclude>
   3.535 +    <ClInclude Include="..\..\include\SDL_hints.h">
   3.536 +      <Filter>API Headers</Filter>
   3.537 +    </ClInclude>
   3.538 +    <ClInclude Include="..\..\include\SDL_joystick.h">
   3.539 +      <Filter>API Headers</Filter>
   3.540 +    </ClInclude>
   3.541 +    <ClInclude Include="..\..\include\SDL_keyboard.h">
   3.542 +      <Filter>API Headers</Filter>
   3.543 +    </ClInclude>
   3.544 +    <ClInclude Include="..\..\include\SDL_keycode.h">
   3.545 +      <Filter>API Headers</Filter>
   3.546 +    </ClInclude>
   3.547 +    <ClInclude Include="..\..\include\SDL_loadso.h">
   3.548 +      <Filter>API Headers</Filter>
   3.549 +    </ClInclude>
   3.550 +    <ClInclude Include="..\..\include\SDL_log.h">
   3.551 +      <Filter>API Headers</Filter>
   3.552 +    </ClInclude>
   3.553 +    <ClInclude Include="..\..\include\SDL_main.h">
   3.554 +      <Filter>API Headers</Filter>
   3.555 +    </ClInclude>
   3.556 +    <ClInclude Include="..\..\include\SDL_messagebox.h">
   3.557 +      <Filter>API Headers</Filter>
   3.558 +    </ClInclude>
   3.559 +    <ClInclude Include="..\..\include\SDL_mouse.h">
   3.560 +      <Filter>API Headers</Filter>
   3.561 +    </ClInclude>
   3.562 +    <ClInclude Include="..\..\include\SDL_mutex.h">
   3.563 +      <Filter>API Headers</Filter>
   3.564 +    </ClInclude>
   3.565 +    <ClInclude Include="..\..\include\SDL_name.h">
   3.566 +      <Filter>API Headers</Filter>
   3.567 +    </ClInclude>
   3.568 +    <ClInclude Include="..\..\include\SDL_opengl.h">
   3.569 +      <Filter>API Headers</Filter>
   3.570 +    </ClInclude>
   3.571 +    <ClInclude Include="..\..\include\SDL_opengl_glext.h">
   3.572 +      <Filter>API Headers</Filter>
   3.573 +    </ClInclude>
   3.574 +    <ClInclude Include="..\..\include\SDL_opengles.h">
   3.575 +      <Filter>API Headers</Filter>
   3.576 +    </ClInclude>
   3.577 +    <ClInclude Include="..\..\include\SDL_opengles2.h">
   3.578 +      <Filter>API Headers</Filter>
   3.579 +    </ClInclude>
   3.580 +    <ClInclude Include="..\..\include\SDL_opengles2_gl2.h">
   3.581 +      <Filter>API Headers</Filter>
   3.582 +    </ClInclude>
   3.583 +    <ClInclude Include="..\..\include\SDL_opengles2_gl2ext.h">
   3.584 +      <Filter>API Headers</Filter>
   3.585 +    </ClInclude>
   3.586 +    <ClInclude Include="..\..\include\SDL_opengles2_gl2platform.h">
   3.587 +      <Filter>API Headers</Filter>
   3.588 +    </ClInclude>
   3.589 +    <ClInclude Include="..\..\include\SDL_opengles2_khrplatform.h">
   3.590 +      <Filter>API Headers</Filter>
   3.591 +    </ClInclude>
   3.592 +    <ClInclude Include="..\..\include\SDL_pixels.h">
   3.593 +      <Filter>API Headers</Filter>
   3.594 +    </ClInclude>
   3.595 +    <ClInclude Include="..\..\include\SDL_platform.h">
   3.596 +      <Filter>API Headers</Filter>
   3.597 +    </ClInclude>
   3.598 +    <ClInclude Include="..\..\include\SDL_power.h">
   3.599 +      <Filter>API Headers</Filter>
   3.600 +    </ClInclude>
   3.601 +    <ClInclude Include="..\..\include\SDL_quit.h">
   3.602 +      <Filter>API Headers</Filter>
   3.603 +    </ClInclude>
   3.604 +    <ClInclude Include="..\..\include\SDL_rect.h">
   3.605 +      <Filter>API Headers</Filter>
   3.606 +    </ClInclude>
   3.607 +    <ClInclude Include="..\..\include\SDL_render.h">
   3.608 +      <Filter>API Headers</Filter>
   3.609 +    </ClInclude>
   3.610 +    <ClInclude Include="..\..\include\SDL_revision.h">
   3.611 +      <Filter>API Headers</Filter>
   3.612 +    </ClInclude>
   3.613 +    <ClInclude Include="..\..\include\SDL_rwops.h">
   3.614 +      <Filter>API Headers</Filter>
   3.615 +    </ClInclude>
   3.616 +    <ClInclude Include="..\..\include\SDL_scancode.h">
   3.617 +      <Filter>API Headers</Filter>
   3.618 +    </ClInclude>
   3.619 +    <ClInclude Include="..\..\include\SDL_shape.h">
   3.620 +      <Filter>API Headers</Filter>
   3.621 +    </ClInclude>
   3.622 +    <ClInclude Include="..\..\include\SDL_stdinc.h">
   3.623 +      <Filter>API Headers</Filter>
   3.624 +    </ClInclude>
   3.625 +    <ClInclude Include="..\..\include\SDL_surface.h">
   3.626 +      <Filter>API Headers</Filter>
   3.627 +    </ClInclude>
   3.628 +    <ClInclude Include="..\..\include\SDL_system.h">
   3.629 +      <Filter>API Headers</Filter>
   3.630 +    </ClInclude>
   3.631 +    <ClInclude Include="..\..\include\SDL_syswm.h">
   3.632 +      <Filter>API Headers</Filter>
   3.633 +    </ClInclude>
   3.634 +    <ClInclude Include="..\..\include\SDL_test.h">
   3.635 +      <Filter>API Headers</Filter>
   3.636 +    </ClInclude>
   3.637 +    <ClInclude Include="..\..\include\SDL_test_assert.h">
   3.638 +      <Filter>API Headers</Filter>
   3.639 +    </ClInclude>
   3.640 +    <ClInclude Include="..\..\include\SDL_test_common.h">
   3.641 +      <Filter>API Headers</Filter>
   3.642 +    </ClInclude>
   3.643 +    <ClInclude Include="..\..\include\SDL_test_compare.h">
   3.644 +      <Filter>API Headers</Filter>
   3.645 +    </ClInclude>
   3.646 +    <ClInclude Include="..\..\include\SDL_test_crc32.h">
   3.647 +      <Filter>API Headers</Filter>
   3.648 +    </ClInclude>
   3.649 +    <ClInclude Include="..\..\include\SDL_test_font.h">
   3.650 +      <Filter>API Headers</Filter>
   3.651 +    </ClInclude>
   3.652 +    <ClInclude Include="..\..\include\SDL_test_fuzzer.h">
   3.653 +      <Filter>API Headers</Filter>
   3.654 +    </ClInclude>
   3.655 +    <ClInclude Include="..\..\include\SDL_test_harness.h">
   3.656 +      <Filter>API Headers</Filter>
   3.657 +    </ClInclude>
   3.658 +    <ClInclude Include="..\..\include\SDL_test_images.h">
   3.659 +      <Filter>API Headers</Filter>
   3.660 +    </ClInclude>
   3.661 +    <ClInclude Include="..\..\include\SDL_test_log.h">
   3.662 +      <Filter>API Headers</Filter>
   3.663 +    </ClInclude>
   3.664 +    <ClInclude Include="..\..\include\SDL_test_md5.h">
   3.665 +      <Filter>API Headers</Filter>
   3.666 +    </ClInclude>
   3.667 +    <ClInclude Include="..\..\include\SDL_test_random.h">
   3.668 +      <Filter>API Headers</Filter>
   3.669 +    </ClInclude>
   3.670 +    <ClInclude Include="..\..\include\SDL_thread.h">
   3.671 +      <Filter>API Headers</Filter>
   3.672 +    </ClInclude>
   3.673 +    <ClInclude Include="..\..\include\SDL_timer.h">
   3.674 +      <Filter>API Headers</Filter>
   3.675 +    </ClInclude>
   3.676 +    <ClInclude Include="..\..\include\SDL_touch.h">
   3.677 +      <Filter>API Headers</Filter>
   3.678 +    </ClInclude>
   3.679 +    <ClInclude Include="..\..\include\SDL_types.h">
   3.680 +      <Filter>API Headers</Filter>
   3.681 +    </ClInclude>
   3.682 +    <ClInclude Include="..\..\include\SDL_version.h">
   3.683 +      <Filter>API Headers</Filter>
   3.684 +    </ClInclude>
   3.685 +    <ClInclude Include="..\..\include\SDL_video.h">
   3.686 +      <Filter>API Headers</Filter>
   3.687 +    </ClInclude>
   3.688 +    <ClInclude Include="..\..\include\SDL_vulkan.h">
   3.689 +      <Filter>API Headers</Filter>
   3.690 +    </ClInclude>
   3.691 +    <ClInclude Include="..\..\src\audio\directsound\SDL_directsound.h" />
   3.692 +    <ClInclude Include="..\..\src\audio\disk\SDL_diskaudio.h" />
   3.693 +    <ClInclude Include="..\..\src\audio\dummy\SDL_dummyaudio.h" />
   3.694 +    <ClInclude Include="..\..\src\audio\SDL_audio_c.h" />
   3.695 +    <ClInclude Include="..\..\src\audio\SDL_audiodev_c.h" />
   3.696 +    <ClInclude Include="..\..\src\audio\SDL_sysaudio.h" />
   3.697 +    <ClInclude Include="..\..\src\audio\SDL_wave.h" />
   3.698 +    <ClInclude Include="..\..\src\audio\wasapi\SDL_wasapi.h" />
   3.699 +    <ClInclude Include="..\..\src\audio\winmm\SDL_winmm.h" />
   3.700 +    <ClInclude Include="..\..\src\core\windows\SDL_directx.h" />
   3.701 +    <ClInclude Include="..\..\src\core\windows\SDL_windows.h" />
   3.702 +    <ClInclude Include="..\..\src\core\windows\SDL_xinput.h" />
   3.703 +    <ClInclude Include="..\..\src\dynapi\SDL_dynapi.h" />
   3.704 +    <ClInclude Include="..\..\src\dynapi\SDL_dynapi_overrides.h" />
   3.705 +    <ClInclude Include="..\..\src\dynapi\SDL_dynapi_procs.h" />
   3.706 +    <ClInclude Include="..\..\src\events\blank_cursor.h" />
   3.707 +    <ClInclude Include="..\..\src\events\default_cursor.h" />
   3.708 +    <ClInclude Include="..\..\src\events\SDL_clipboardevents_c.h" />
   3.709 +    <ClInclude Include="..\..\src\events\SDL_dropevents_c.h" />
   3.710 +    <ClInclude Include="..\..\src\events\SDL_events_c.h" />
   3.711 +    <ClInclude Include="..\..\src\events\SDL_gesture_c.h" />
   3.712 +    <ClInclude Include="..\..\src\events\SDL_keyboard_c.h" />
   3.713 +    <ClInclude Include="..\..\src\events\SDL_mouse_c.h" />
   3.714 +    <ClInclude Include="..\..\src\events\SDL_sysevents.h" />
   3.715 +    <ClInclude Include="..\..\src\events\SDL_touch_c.h" />
   3.716 +    <ClInclude Include="..\..\src\events\SDL_windowevents_c.h" />
   3.717 +    <ClInclude Include="..\..\src\haptic\SDL_syshaptic.h" />
   3.718 +    <ClInclude Include="..\..\src\haptic\windows\SDL_dinputhaptic_c.h" />
   3.719 +    <ClInclude Include="..\..\src\haptic\windows\SDL_windowshaptic_c.h" />
   3.720 +    <ClInclude Include="..\..\src\haptic\windows\SDL_xinputhaptic_c.h" />
   3.721 +    <ClInclude Include="..\..\src\joystick\SDL_joystick_c.h" />
   3.722 +    <ClInclude Include="..\..\src\joystick\SDL_sysjoystick.h" />
   3.723 +    <ClInclude Include="..\..\src\joystick\windows\SDL_dinputjoystick_c.h" />
   3.724 +    <ClInclude Include="..\..\src\joystick\windows\SDL_windowsjoystick_c.h" />
   3.725 +    <ClInclude Include="..\..\src\joystick\windows\SDL_xinputjoystick_c.h" />
   3.726 +    <ClInclude Include="..\..\src\libm\math_libm.h" />
   3.727 +    <ClInclude Include="..\..\src\libm\math_private.h" />
   3.728 +    <ClInclude Include="..\..\src\render\opengl\SDL_glfuncs.h" />
   3.729 +    <ClInclude Include="..\..\src\render\opengl\SDL_shaders_gl.h" />
   3.730 +    <ClInclude Include="..\..\src\render\opengles\SDL_glesfuncs.h" />
   3.731 +    <ClInclude Include="..\..\src\render\SDL_d3dmath.h" />
   3.732 +    <ClInclude Include="..\..\src\render\SDL_sysrender.h" />
   3.733 +    <ClInclude Include="..\..\src\render\SDL_yuv_sw_c.h" />
   3.734 +    <ClInclude Include="..\..\src\render\software\SDL_blendfillrect.h" />
   3.735 +    <ClInclude Include="..\..\src\render\software\SDL_blendline.h" />
   3.736 +    <ClInclude Include="..\..\src\render\software\SDL_blendpoint.h" />
   3.737 +    <ClInclude Include="..\..\src\render\software\SDL_draw.h" />
   3.738 +    <ClInclude Include="..\..\src\render\software\SDL_drawline.h" />
   3.739 +    <ClInclude Include="..\..\src\render\software\SDL_drawpoint.h" />
   3.740 +    <ClInclude Include="..\..\src\render\software\SDL_render_sw_c.h" />
   3.741 +    <ClInclude Include="..\..\src\render\software\SDL_rotate.h" />
   3.742 +    <ClInclude Include="..\..\src\SDL_dataqueue.h" />
   3.743 +    <ClInclude Include="..\..\src\SDL_error_c.h" />
   3.744 +    <ClInclude Include="..\..\src\thread\SDL_systhread.h" />
   3.745 +    <ClInclude Include="..\..\src\thread\SDL_thread_c.h" />
   3.746 +    <ClInclude Include="..\..\src\thread\windows\SDL_systhread_c.h" />
   3.747 +    <ClInclude Include="..\..\src\timer\SDL_timer_c.h" />
   3.748 +    <ClInclude Include="..\..\src\video\dummy\SDL_nullevents_c.h" />
   3.749 +    <ClInclude Include="..\..\src\video\dummy\SDL_nullframebuffer_c.h" />
   3.750 +    <ClInclude Include="..\..\src\video\dummy\SDL_nullvideo.h" />
   3.751 +    <ClInclude Include="..\..\src\video\SDL_blit.h" />
   3.752 +    <ClInclude Include="..\..\src\video\SDL_blit_auto.h" />
   3.753 +    <ClInclude Include="..\..\src\video\SDL_blit_copy.h" />
   3.754 +    <ClInclude Include="..\..\src\video\SDL_blit_slow.h" />
   3.755 +    <ClInclude Include="..\..\src\video\SDL_pixels_c.h" />
   3.756 +    <ClInclude Include="..\..\src\video\SDL_rect_c.h" />
   3.757 +    <ClInclude Include="..\..\src\video\SDL_RLEaccel_c.h" />
   3.758 +    <ClInclude Include="..\..\src\video\SDL_shape_internals.h" />
   3.759 +    <ClInclude Include="..\..\src\video\SDL_sysvideo.h" />
   3.760 +    <ClInclude Include="..\..\src\video\SDL_vulkan_internal.h" />
   3.761 +    <ClInclude Include="..\..\src\video\windows\SDL_vkeys.h" />
   3.762 +    <ClInclude Include="..\..\src\video\windows\SDL_windowsclipboard.h" />
   3.763 +    <ClInclude Include="..\..\src\video\windows\SDL_windowsevents.h" />
   3.764 +    <ClInclude Include="..\..\src\video\windows\SDL_windowsframebuffer.h" />
   3.765 +    <ClInclude Include="..\..\src\video\windows\SDL_windowskeyboard.h" />
   3.766 +    <ClInclude Include="..\..\src\video\windows\SDL_windowsmessagebox.h" />
   3.767 +    <ClInclude Include="..\..\src\video\windows\SDL_windowsmodes.h" />
   3.768 +    <ClInclude Include="..\..\src\video\windows\SDL_windowsmouse.h" />
   3.769 +    <ClInclude Include="..\..\src\video\windows\SDL_windowsopengl.h" />
   3.770 +    <ClInclude Include="..\..\src\video\windows\SDL_windowsshape.h" />
   3.771 +    <ClInclude Include="..\..\src\video\windows\SDL_windowsvideo.h" />
   3.772 +    <ClInclude Include="..\..\src\video\windows\SDL_windowsvulkan.h" />
   3.773 +    <ClInclude Include="..\..\src\video\windows\SDL_windowswindow.h" />
   3.774 +    <ClInclude Include="..\..\src\video\windows\wmmsg.h" />
   3.775 +    <ClInclude Include="..\..\src\video\SDL_yuv_c.h" />
   3.776 +    <ClInclude Include="..\..\src\video\yuv2rgb\yuv_rgb.h" />
   3.777 +    <ClInclude Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.h" />
   3.778 +    <ClInclude Include="..\..\src\render\direct3d\SDL_shaders_d3d.h" />
   3.779 +    <ClInclude Include="..\..\src\joystick\hidapi\controller_type.h" />
   3.780 +    <ClInclude Include="..\..\src\joystick\hidapi\SDL_hidapijoystick_c.h" />
   3.781 +  </ItemGroup>
   3.782 +  <ItemGroup>
   3.783 +    <ClCompile Include="..\..\src\libm\e_atan2.c" />
   3.784 +    <ClCompile Include="..\..\src\libm\e_exp.c" />
   3.785 +    <ClCompile Include="..\..\src\libm\e_log.c" />
   3.786 +    <ClCompile Include="..\..\src\libm\e_log10.c" />
   3.787 +    <ClCompile Include="..\..\src\libm\e_pow.c" />
   3.788 +    <ClCompile Include="..\..\src\libm\e_rem_pio2.c" />
   3.789 +    <ClCompile Include="..\..\src\libm\e_sqrt.c" />
   3.790 +    <ClCompile Include="..\..\src\libm\k_cos.c" />
   3.791 +    <ClCompile Include="..\..\src\libm\k_rem_pio2.c" />
   3.792 +    <ClCompile Include="..\..\src\libm\k_sin.c" />
   3.793 +    <ClCompile Include="..\..\src\libm\k_tan.c" />
   3.794 +    <ClCompile Include="..\..\src\libm\s_atan.c" />
   3.795 +    <ClCompile Include="..\..\src\libm\s_copysign.c" />
   3.796 +    <ClCompile Include="..\..\src\libm\s_cos.c" />
   3.797 +    <ClCompile Include="..\..\src\libm\s_fabs.c" />
   3.798 +    <ClCompile Include="..\..\src\libm\s_floor.c" />
   3.799 +    <ClCompile Include="..\..\src\libm\s_scalbn.c" />
   3.800 +    <ClCompile Include="..\..\src\libm\s_sin.c" />
   3.801 +    <ClCompile Include="..\..\src\libm\s_tan.c" />
   3.802 +    <ClCompile Include="..\..\src\SDL.c" />
   3.803 +    <ClCompile Include="..\..\src\SDL_assert.c" />
   3.804 +    <ClCompile Include="..\..\src\atomic\SDL_atomic.c" />
   3.805 +    <ClCompile Include="..\..\src\audio\SDL_audio.c" />
   3.806 +    <ClCompile Include="..\..\src\audio\SDL_audiocvt.c" />
   3.807 +    <ClCompile Include="..\..\src\audio\SDL_audiodev.c" />
   3.808 +    <ClCompile Include="..\..\src\audio\SDL_audiotypecvt.c" />
   3.809 +    <ClCompile Include="..\..\src\render\software\SDL_blendfillrect.c" />
   3.810 +    <ClCompile Include="..\..\src\render\software\SDL_blendline.c" />
   3.811 +    <ClCompile Include="..\..\src\render\software\SDL_blendpoint.c" />
   3.812 +    <ClCompile Include="..\..\src\video\SDL_blit.c" />
   3.813 +    <ClCompile Include="..\..\src\video\SDL_blit_0.c" />
   3.814 +    <ClCompile Include="..\..\src\video\SDL_blit_1.c" />
   3.815 +    <ClCompile Include="..\..\src\video\SDL_blit_A.c" />
   3.816 +    <ClCompile Include="..\..\src\video\SDL_blit_auto.c" />
   3.817 +    <ClCompile Include="..\..\src\video\SDL_blit_copy.c" />
   3.818 +    <ClCompile Include="..\..\src\video\SDL_blit_N.c" />
   3.819 +    <ClCompile Include="..\..\src\video\SDL_blit_slow.c" />
   3.820 +    <ClCompile Include="..\..\src\video\SDL_bmp.c" />
   3.821 +    <ClCompile Include="..\..\src\video\SDL_clipboard.c" />
   3.822 +    <ClCompile Include="..\..\src\events\SDL_clipboardevents.c" />
   3.823 +    <ClCompile Include="..\..\src\cpuinfo\SDL_cpuinfo.c" />
   3.824 +    <ClCompile Include="..\..\src\render\SDL_d3dmath.c" />
   3.825 +    <ClCompile Include="..\..\src\haptic\windows\SDL_dinputhaptic.c" />
   3.826 +    <ClCompile Include="..\..\src\joystick\windows\SDL_dinputjoystick.c" />
   3.827 +    <ClCompile Include="..\..\src\audio\directsound\SDL_directsound.c" />
   3.828 +    <ClCompile Include="..\..\src\audio\disk\SDL_diskaudio.c" />
   3.829 +    <ClCompile Include="..\..\src\render\software\SDL_drawline.c" />
   3.830 +    <ClCompile Include="..\..\src\render\software\SDL_drawpoint.c" />
   3.831 +    <ClCompile Include="..\..\src\events\SDL_dropevents.c" />
   3.832 +    <ClCompile Include="..\..\src\audio\dummy\SDL_dummyaudio.c" />
   3.833 +    <ClCompile Include="..\..\src\dynapi\SDL_dynapi.c" />
   3.834 +    <ClCompile Include="..\..\src\video\SDL_egl.c" />
   3.835 +    <ClCompile Include="..\..\src\SDL_dataqueue.c" />
   3.836 +    <ClCompile Include="..\..\src\SDL_error.c" />
   3.837 +    <ClCompile Include="..\..\src\events\SDL_events.c" />
   3.838 +    <ClCompile Include="..\..\src\video\SDL_fillrect.c" />
   3.839 +    <ClCompile Include="..\..\src\joystick\SDL_gamecontroller.c" />
   3.840 +    <ClCompile Include="..\..\src\events\SDL_gesture.c" />
   3.841 +    <ClCompile Include="..\..\src\stdlib\SDL_getenv.c" />
   3.842 +    <ClCompile Include="..\..\src\haptic\SDL_haptic.c" />
   3.843 +    <ClCompile Include="..\..\src\SDL_hints.c" />
   3.844 +    <ClCompile Include="..\..\src\stdlib\SDL_iconv.c" />
   3.845 +    <ClCompile Include="..\..\src\joystick\SDL_joystick.c" />
   3.846 +    <ClCompile Include="..\..\src\events\SDL_keyboard.c" />
   3.847 +    <ClCompile Include="..\..\src\SDL_log.c" />
   3.848 +    <ClCompile Include="..\..\src\stdlib\SDL_malloc.c" />
   3.849 +    <ClCompile Include="..\..\src\audio\SDL_mixer.c" />
   3.850 +    <ClCompile Include="..\..\src\joystick\windows\SDL_mmjoystick.c" />
   3.851 +    <ClCompile Include="..\..\src\events\SDL_mouse.c" />
   3.852 +    <ClCompile Include="..\..\src\video\dummy\SDL_nullevents.c" />
   3.853 +    <ClCompile Include="..\..\src\video\dummy\SDL_nullframebuffer.c" />
   3.854 +    <ClCompile Include="..\..\src\video\dummy\SDL_nullvideo.c" />
   3.855 +    <ClCompile Include="..\..\src\video\SDL_pixels.c" />
   3.856 +    <ClCompile Include="..\..\src\power\SDL_power.c" />
   3.857 +    <ClCompile Include="..\..\src\stdlib\SDL_qsort.c" />
   3.858 +    <ClCompile Include="..\..\src\events\SDL_quit.c" />
   3.859 +    <ClCompile Include="..\..\src\video\SDL_rect.c" />
   3.860 +    <ClCompile Include="..\..\src\render\SDL_render.c" />
   3.861 +    <ClCompile Include="..\..\src\render\direct3d\SDL_render_d3d.c" />
   3.862 +    <ClCompile Include="..\..\src\render\direct3d11\SDL_render_d3d11.c" />
   3.863 +    <ClCompile Include="..\..\src\render\opengl\SDL_render_gl.c" />
   3.864 +    <ClCompile Include="..\..\src\render\opengles2\SDL_render_gles2.c" />
   3.865 +    <ClCompile Include="..\..\src\render\software\SDL_render_sw.c" />
   3.866 +    <ClCompile Include="..\..\src\video\SDL_RLEaccel.c" />
   3.867 +    <ClCompile Include="..\..\src\render\software\SDL_rotate.c" />
   3.868 +    <ClCompile Include="..\..\src\file\SDL_rwops.c" />
   3.869 +    <ClCompile Include="..\..\src\render\opengl\SDL_shaders_gl.c" />
   3.870 +    <ClCompile Include="..\..\src\render\opengles2\SDL_shaders_gles2.c" />
   3.871 +    <ClCompile Include="..\..\src\video\SDL_shape.c" />
   3.872 +    <ClCompile Include="..\..\src\atomic\SDL_spinlock.c" />
   3.873 +    <ClCompile Include="..\..\src\stdlib\SDL_stdlib.c" />
   3.874 +    <ClCompile Include="..\..\src\video\SDL_stretch.c" />
   3.875 +    <ClCompile Include="..\..\src\stdlib\SDL_string.c" />
   3.876 +    <ClCompile Include="..\..\src\video\SDL_surface.c" />
   3.877 +    <ClCompile Include="..\..\src\thread\generic\SDL_syscond.c" />
   3.878 +    <ClCompile Include="..\..\src\filesystem\windows\SDL_sysfilesystem.c" />
   3.879 +    <ClCompile Include="..\..\src\loadso\windows\SDL_sysloadso.c" />
   3.880 +    <ClCompile Include="..\..\src\thread\windows\SDL_sysmutex.c" />
   3.881 +    <ClCompile Include="..\..\src\power\windows\SDL_syspower.c" />
   3.882 +    <ClCompile Include="..\..\src\thread\windows\SDL_syssem.c" />
   3.883 +    <ClCompile Include="..\..\src\thread\windows\SDL_systhread.c" />
   3.884 +    <ClCompile Include="..\..\src\timer\windows\SDL_systimer.c" />
   3.885 +    <ClCompile Include="..\..\src\thread\windows\SDL_systls.c" />
   3.886 +    <ClCompile Include="..\..\src\thread\SDL_thread.c" />
   3.887 +    <ClCompile Include="..\..\src\timer\SDL_timer.c" />
   3.888 +    <ClCompile Include="..\..\src\events\SDL_touch.c" />
   3.889 +    <ClCompile Include="..\..\src\video\SDL_video.c" />
   3.890 +    <ClCompile Include="..\..\src\audio\SDL_wave.c" />
   3.891 +    <ClCompile Include="..\..\src\events\SDL_windowevents.c" />
   3.892 +    <ClCompile Include="..\..\src\core\windows\SDL_windows.c" />
   3.893 +    <ClCompile Include="..\..\src\video\windows\SDL_windowsclipboard.c" />
   3.894 +    <ClCompile Include="..\..\src\video\windows\SDL_windowsevents.c" />
   3.895 +    <ClCompile Include="..\..\src\video\windows\SDL_windowsframebuffer.c" />
   3.896 +    <ClCompile Include="..\..\src\haptic\windows\SDL_windowshaptic.c" />
   3.897 +    <ClCompile Include="..\..\src\joystick\windows\SDL_windowsjoystick.c" />
   3.898 +    <ClCompile Include="..\..\src\video\windows\SDL_windowskeyboard.c" />
   3.899 +    <ClCompile Include="..\..\src\video\windows\SDL_windowsmessagebox.c" />
   3.900 +    <ClCompile Include="..\..\src\video\windows\SDL_windowsmodes.c" />
   3.901 +    <ClCompile Include="..\..\src\video\windows\SDL_windowsmouse.c" />
   3.902 +    <ClCompile Include="..\..\src\video\windows\SDL_windowsopengl.c" />
   3.903 +    <ClCompile Include="..\..\src\video\windows\SDL_windowsopengles.c" />
   3.904 +    <ClCompile Include="..\..\src\video\windows\SDL_windowsshape.c" />
   3.905 +    <ClCompile Include="..\..\src\video\windows\SDL_windowsvideo.c" />
   3.906 +    <ClCompile Include="..\..\src\video\windows\SDL_windowswindow.c" />
   3.907 +    <ClCompile Include="..\..\src\audio\winmm\SDL_winmm.c" />
   3.908 +    <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
   3.909 +    <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi_win32.c" />
   3.910 +    <ClCompile Include="..\..\src\core\windows\SDL_xinput.c" />
   3.911 +    <ClCompile Include="..\..\src\haptic\windows\SDL_xinputhaptic.c" />
   3.912 +    <ClCompile Include="..\..\src\joystick\windows\SDL_xinputjoystick.c" />
   3.913 +    <ClCompile Include="..\..\src\render\SDL_yuv_sw.c" />
   3.914 +    <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
   3.915 +    <ClCompile Include="..\..\src\video\SDL_vulkan_utils.c" />
   3.916 +    <ClCompile Include="..\..\src\video\windows\SDL_windowsvulkan.c" />
   3.917 +    <ClCompile Include="..\..\src\libm\e_fmod.c" />
   3.918 +    <ClCompile Include="..\..\src\video\SDL_yuv.c" />
   3.919 +    <ClCompile Include="..\..\src\video\yuv2rgb\yuv_rgb.c" />
   3.920 +    <ClCompile Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.c" />
   3.921 +    <ClCompile Include="..\..\src\render\direct3d\SDL_shaders_d3d.c" />
   3.922 +    <ClCompile Include="..\..\src\hidapi\windows\hid.c" />
   3.923 +    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_ps4.c" />
   3.924 +    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_switch.c" />
   3.925 +    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xbox360.c" />
   3.926 +    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_xboxone.c" />
   3.927 +    <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapijoystick.c" />
   3.928 +  </ItemGroup>
   3.929 +  <ItemGroup>
   3.930 +    <ResourceCompile Include="..\..\src\main\windows\version.rc" />
   3.931 +  </ItemGroup>
   3.932  </Project>
   3.933 \ No newline at end of file
     4.1 --- a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj	Wed Aug 08 15:39:20 2018 -0400
     4.2 +++ b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj	Thu Aug 09 16:00:17 2018 -0700
     4.3 @@ -110,8 +110,8 @@
     4.4  		56F9D5601DF73BA400C15B5D /* SDL_dataqueue.c in Sources */ = {isa = PBXBuildFile; fileRef = 566726431DF72CF5001DD3DB /* SDL_dataqueue.c */; };
     4.5  		93CB792313FC5E5200BD3E05 /* SDL_uikitviewcontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = 93CB792213FC5E5200BD3E05 /* SDL_uikitviewcontroller.h */; };
     4.6  		93CB792613FC5F5300BD3E05 /* SDL_uikitviewcontroller.m in Sources */ = {isa = PBXBuildFile; fileRef = 93CB792513FC5F5300BD3E05 /* SDL_uikitviewcontroller.m */; };
     4.7 -		A7A9EEA91F702631002A5589 /* SDL_steamcontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = A7A9EEA71F702631002A5589 /* SDL_steamcontroller.c */; };
     4.8 -		A7A9EEAA1F702631002A5589 /* SDL_steamcontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = A7A9EEA81F702631002A5589 /* SDL_steamcontroller.h */; };
     4.9 +		A704172E20F7E74800A82227 /* controller_type.h in Headers */ = {isa = PBXBuildFile; fileRef = A704172D20F7E74800A82227 /* controller_type.h */; };
    4.10 +		A704172F20F7E76000A82227 /* SDL_gamecontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */; };
    4.11  		A7F629241FE06523002F9CC9 /* SDL_uikitmetalview.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D7516F81EE1C28A00820EEA /* SDL_uikitmetalview.m */; };
    4.12  		AA0AD06216647BBB00CE5896 /* SDL_gamecontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */; };
    4.13  		AA0AD06516647BD400CE5896 /* SDL_gamecontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = AA0AD06416647BD400CE5896 /* SDL_gamecontroller.h */; };
    4.14 @@ -194,7 +194,18 @@
    4.15  		AADC5A631FDA10C800960936 /* SDL_shaders_metal_ios.h in Headers */ = {isa = PBXBuildFile; fileRef = AADC5A611FDA10C800960936 /* SDL_shaders_metal_ios.h */; };
    4.16  		AADC5A641FDA10C800960936 /* SDL_render_metal.m in Sources */ = {isa = PBXBuildFile; fileRef = AADC5A621FDA10C800960936 /* SDL_render_metal.m */; };
    4.17  		AADC5A651FDA10CB00960936 /* SDL_render_metal.m in Sources */ = {isa = PBXBuildFile; fileRef = AADC5A621FDA10C800960936 /* SDL_render_metal.m */; };
    4.18 -		AAE7A4222041CCA90096E65A /* SDL_steamcontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = A7A9EEA71F702631002A5589 /* SDL_steamcontroller.c */; };
    4.19 +		F3BDD77620F51C3C004ECBF3 /* hid.mm in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD77520F51C3C004ECBF3 /* hid.mm */; };
    4.20 +		F3BDD79220F51CB8004ECBF3 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78B20F51CB8004ECBF3 /* SDL_hidapi_xbox360.c */; };
    4.21 +		F3BDD79320F51CB8004ECBF3 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78B20F51CB8004ECBF3 /* SDL_hidapi_xbox360.c */; };
    4.22 +		F3BDD79420F51CB8004ECBF3 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78C20F51CB8004ECBF3 /* SDL_hidapi_switch.c */; };
    4.23 +		F3BDD79520F51CB8004ECBF3 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78C20F51CB8004ECBF3 /* SDL_hidapi_switch.c */; };
    4.24 +		F3BDD79620F51CB8004ECBF3 /* SDL_hidapi_xboxone.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78D20F51CB8004ECBF3 /* SDL_hidapi_xboxone.c */; };
    4.25 +		F3BDD79720F51CB8004ECBF3 /* SDL_hidapi_xboxone.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78D20F51CB8004ECBF3 /* SDL_hidapi_xboxone.c */; };
    4.26 +		F3BDD79820F51CB8004ECBF3 /* SDL_hidapi_ps4.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78E20F51CB8004ECBF3 /* SDL_hidapi_ps4.c */; };
    4.27 +		F3BDD79920F51CB8004ECBF3 /* SDL_hidapi_ps4.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78E20F51CB8004ECBF3 /* SDL_hidapi_ps4.c */; };
    4.28 +		F3BDD79B20F51CB8004ECBF3 /* SDL_hidapijoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3BDD79020F51CB8004ECBF3 /* SDL_hidapijoystick_c.h */; };
    4.29 +		F3BDD79C20F51CB8004ECBF3 /* SDL_hidapijoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD79120F51CB8004ECBF3 /* SDL_hidapijoystick.c */; };
    4.30 +		F3BDD79D20F51CB8004ECBF3 /* SDL_hidapijoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD79120F51CB8004ECBF3 /* SDL_hidapijoystick.c */; };
    4.31  		FA1DC2721C62BE65008F99A0 /* SDL_uikitclipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = FA1DC2701C62BE65008F99A0 /* SDL_uikitclipboard.h */; };
    4.32  		FA1DC2731C62BE65008F99A0 /* SDL_uikitclipboard.m in Sources */ = {isa = PBXBuildFile; fileRef = FA1DC2711C62BE65008F99A0 /* SDL_uikitclipboard.m */; };
    4.33  		FAB5981D1BB5C31500BE72C5 /* SDL_atomic.c in Sources */ = {isa = PBXBuildFile; fileRef = 04FFAB8912E23B8D00BA343D /* SDL_atomic.c */; };
    4.34 @@ -223,7 +234,6 @@
    4.35  		FAB5984C1BB5C31600BE72C5 /* SDL_syshaptic.c in Sources */ = {isa = PBXBuildFile; fileRef = 047677B80EA76A31008ABAF1 /* SDL_syshaptic.c */; };
    4.36  		FAB5984D1BB5C31600BE72C5 /* SDL_haptic.c in Sources */ = {isa = PBXBuildFile; fileRef = 047677B90EA76A31008ABAF1 /* SDL_haptic.c */; };
    4.37  		FAB598501BB5C31600BE72C5 /* SDL_sysjoystick.m in Sources */ = {isa = PBXBuildFile; fileRef = FD689F000E26E5B600F90B21 /* SDL_sysjoystick.m */; };
    4.38 -		FAB598511BB5C31600BE72C5 /* SDL_gamecontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */; };
    4.39  		FAB598521BB5C31600BE72C5 /* SDL_joystick.c in Sources */ = {isa = PBXBuildFile; fileRef = FD5F9D1E0E0E08B3008E885B /* SDL_joystick.c */; };
    4.40  		FAB598551BB5C31600BE72C5 /* SDL_sysloadso.c in Sources */ = {isa = PBXBuildFile; fileRef = 047AF1B20EA98D6C00811173 /* SDL_sysloadso.c */; };
    4.41  		FAB598561BB5C31600BE72C5 /* SDL_sysloadso.c in Sources */ = {isa = PBXBuildFile; fileRef = FD8BD8190E27E25900B52CD5 /* SDL_sysloadso.c */; };
    4.42 @@ -432,8 +442,7 @@
    4.43  		56ED04E2118A8EFD00A56AA6 /* SDL_syspower.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDL_syspower.m; path = ../../src/power/uikit/SDL_syspower.m; sourceTree = SOURCE_ROOT; };
    4.44  		93CB792213FC5E5200BD3E05 /* SDL_uikitviewcontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_uikitviewcontroller.h; sourceTree = "<group>"; };
    4.45  		93CB792513FC5F5300BD3E05 /* SDL_uikitviewcontroller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_uikitviewcontroller.m; sourceTree = "<group>"; };
    4.46 -		A7A9EEA71F702631002A5589 /* SDL_steamcontroller.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_steamcontroller.c; sourceTree = "<group>"; };
    4.47 -		A7A9EEA81F702631002A5589 /* SDL_steamcontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_steamcontroller.h; sourceTree = "<group>"; };
    4.48 +		A704172D20F7E74800A82227 /* controller_type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = controller_type.h; sourceTree = "<group>"; };
    4.49  		AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_gamecontroller.c; sourceTree = "<group>"; };
    4.50  		AA0AD06416647BD400CE5896 /* SDL_gamecontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gamecontroller.h; sourceTree = "<group>"; };
    4.51  		AA0F8494178D5F1A00823F9D /* SDL_systls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_systls.c; sourceTree = "<group>"; };
    4.52 @@ -510,6 +519,13 @@
    4.53  		AADA5B8E16CCAB7C00107CF7 /* SDL_bits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_bits.h; sourceTree = "<group>"; };
    4.54  		AADC5A611FDA10C800960936 /* SDL_shaders_metal_ios.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_shaders_metal_ios.h; sourceTree = "<group>"; };
    4.55  		AADC5A621FDA10C800960936 /* SDL_render_metal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_render_metal.m; sourceTree = "<group>"; };
    4.56 +		F3BDD77520F51C3C004ECBF3 /* hid.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = hid.mm; sourceTree = "<group>"; };
    4.57 +		F3BDD78B20F51CB8004ECBF3 /* SDL_hidapi_xbox360.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_xbox360.c; sourceTree = "<group>"; };
    4.58 +		F3BDD78C20F51CB8004ECBF3 /* SDL_hidapi_switch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_switch.c; sourceTree = "<group>"; };
    4.59 +		F3BDD78D20F51CB8004ECBF3 /* SDL_hidapi_xboxone.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_xboxone.c; sourceTree = "<group>"; };
    4.60 +		F3BDD78E20F51CB8004ECBF3 /* SDL_hidapi_ps4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_ps4.c; sourceTree = "<group>"; };
    4.61 +		F3BDD79020F51CB8004ECBF3 /* SDL_hidapijoystick_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_hidapijoystick_c.h; sourceTree = "<group>"; };
    4.62 +		F3BDD79120F51CB8004ECBF3 /* SDL_hidapijoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapijoystick.c; sourceTree = "<group>"; };
    4.63  		FA1DC2701C62BE65008F99A0 /* SDL_uikitclipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_uikitclipboard.h; sourceTree = "<group>"; };
    4.64  		FA1DC2711C62BE65008F99A0 /* SDL_uikitclipboard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_uikitclipboard.m; sourceTree = "<group>"; };
    4.65  		FAB598141BB5C1B100BE72C5 /* libSDL2.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSDL2.a; sourceTree = BUILT_PRODUCTS_DIR; };
    4.66 @@ -778,15 +794,6 @@
    4.67  			name = uikit;
    4.68  			sourceTree = "<group>";
    4.69  		};
    4.70 -		A7A9EEA61F702607002A5589 /* steam */ = {
    4.71 -			isa = PBXGroup;
    4.72 -			children = (
    4.73 -				A7A9EEA71F702631002A5589 /* SDL_steamcontroller.c */,
    4.74 -				A7A9EEA81F702631002A5589 /* SDL_steamcontroller.h */,
    4.75 -			);
    4.76 -			path = steam;
    4.77 -			sourceTree = "<group>";
    4.78 -		};
    4.79  		AA13B3521FB8B41700D9FEE6 /* yuv2rgb */ = {
    4.80  			isa = PBXGroup;
    4.81  			children = (
    4.82 @@ -807,6 +814,36 @@
    4.83  			path = metal;
    4.84  			sourceTree = "<group>";
    4.85  		};
    4.86 +		F35CEA6E20F51B7F003ECE98 /* hidapi */ = {
    4.87 +			isa = PBXGroup;
    4.88 +			children = (
    4.89 +				F3BDD77420F51C18004ECBF3 /* ios */,
    4.90 +			);
    4.91 +			name = hidapi;
    4.92 +			path = ../../src/hidapi;
    4.93 +			sourceTree = SOURCE_ROOT;
    4.94 +		};
    4.95 +		F3BDD77420F51C18004ECBF3 /* ios */ = {
    4.96 +			isa = PBXGroup;
    4.97 +			children = (
    4.98 +				F3BDD77520F51C3C004ECBF3 /* hid.mm */,
    4.99 +			);
   4.100 +			path = ios;
   4.101 +			sourceTree = "<group>";
   4.102 +		};
   4.103 +		F3BDD78A20F51C8D004ECBF3 /* hidapi */ = {
   4.104 +			isa = PBXGroup;
   4.105 +			children = (
   4.106 +				F3BDD78E20F51CB8004ECBF3 /* SDL_hidapi_ps4.c */,
   4.107 +				F3BDD78C20F51CB8004ECBF3 /* SDL_hidapi_switch.c */,
   4.108 +				F3BDD78B20F51CB8004ECBF3 /* SDL_hidapi_xbox360.c */,
   4.109 +				F3BDD78D20F51CB8004ECBF3 /* SDL_hidapi_xboxone.c */,
   4.110 +				F3BDD79020F51CB8004ECBF3 /* SDL_hidapijoystick_c.h */,
   4.111 +				F3BDD79120F51CB8004ECBF3 /* SDL_hidapijoystick.c */,
   4.112 +			);
   4.113 +			path = hidapi;
   4.114 +			sourceTree = "<group>";
   4.115 +		};
   4.116  		FD3F4A6F0DEA620800C5B771 /* stdlib */ = {
   4.117  			isa = PBXGroup;
   4.118  			children = (
   4.119 @@ -824,8 +861,9 @@
   4.120  		FD5F9D080E0E08B3008E885B /* joystick */ = {
   4.121  			isa = PBXGroup;
   4.122  			children = (
   4.123 -				A7A9EEA61F702607002A5589 /* steam */,
   4.124 +				F3BDD78A20F51C8D004ECBF3 /* hidapi */,
   4.125  				FD689EFF0E26E5B600F90B21 /* iphoneos */,
   4.126 +				A704172D20F7E74800A82227 /* controller_type.h */,
   4.127  				AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */,
   4.128  				FD5F9D1E0E0E08B3008E885B /* SDL_joystick.c */,
   4.129  				FD5F9D1F0E0E08B3008E885B /* SDL_joystick_c.h */,
   4.130 @@ -970,6 +1008,7 @@
   4.131  				FD99B99D0DD52EDC00FB1D6B /* file */,
   4.132  				56C181E017C44D6900406AE3 /* filesystem */,
   4.133  				047677B60EA769DF008ABAF1 /* haptic */,
   4.134 +				F35CEA6E20F51B7F003ECE98 /* hidapi */,
   4.135  				FD5F9D080E0E08B3008E885B /* joystick */,
   4.136  				FD8BD8150E27E25900B52CD5 /* loadso */,
   4.137  				56ED04DE118A8E9A00A56AA6 /* power */,
   4.138 @@ -1213,13 +1252,13 @@
   4.139  				AA13B3591FB8B46400D9FEE6 /* yuv_rgb.h in Headers */,
   4.140  				04F7807712FB751400FC43C0 /* SDL_blendfillrect.h in Headers */,
   4.141  				04F7807912FB751400FC43C0 /* SDL_blendline.h in Headers */,
   4.142 +				F3BDD79B20F51CB8004ECBF3 /* SDL_hidapijoystick_c.h in Headers */,
   4.143  				04F7807B12FB751400FC43C0 /* SDL_blendpoint.h in Headers */,
   4.144  				04F7807C12FB751400FC43C0 /* SDL_draw.h in Headers */,
   4.145  				04F7807E12FB751400FC43C0 /* SDL_drawline.h in Headers */,
   4.146  				AA13B34E1FB8B27800D9FEE6 /* SDL_yuv_c.h in Headers */,
   4.147  				04F7808012FB751400FC43C0 /* SDL_drawpoint.h in Headers */,
   4.148  				04F7808412FB753F00FC43C0 /* SDL_nullframebuffer_c.h in Headers */,
   4.149 -				A7A9EEAA1F702631002A5589 /* SDL_steamcontroller.h in Headers */,
   4.150  				0442EC5012FE1C1E004C9285 /* SDL_render_sw_c.h in Headers */,
   4.151  				FA1DC2721C62BE65008F99A0 /* SDL_uikitclipboard.h in Headers */,
   4.152  				0402A85A12FE70C600CECEE3 /* SDL_shaders_gles2.h in Headers */,
   4.153 @@ -1250,6 +1289,7 @@
   4.154  				AA7558AA1595D55500BBD41B /* SDL_joystick.h in Headers */,
   4.155  				AA13B34B1FB8B27800D9FEE6 /* SDL_shape_internals.h in Headers */,
   4.156  				AA7558AB1595D55500BBD41B /* SDL_keyboard.h in Headers */,
   4.157 +				A704172E20F7E74800A82227 /* controller_type.h in Headers */,
   4.158  				AA7558AC1595D55500BBD41B /* SDL_keycode.h in Headers */,
   4.159  				AA7558AD1595D55500BBD41B /* SDL_loadso.h in Headers */,
   4.160  				AA7558AE1595D55500BBD41B /* SDL_log.h in Headers */,
   4.161 @@ -1432,6 +1472,7 @@
   4.162  				FAB598251BB5C31500BE72C5 /* SDL_audiocvt.c in Sources */,
   4.163  				FAB598271BB5C31500BE72C5 /* SDL_audiotypecvt.c in Sources */,
   4.164  				FAB598281BB5C31500BE72C5 /* SDL_mixer.c in Sources */,
   4.165 +				F3BDD79720F51CB8004ECBF3 /* SDL_hidapi_xboxone.c in Sources */,
   4.166  				FAB5982A1BB5C31500BE72C5 /* SDL_wave.c in Sources */,
   4.167  				FAFDF8C61D88D4530083E6F2 /* SDL_uikitclipboard.m in Sources */,
   4.168  				FAB5982C1BB5C31500BE72C5 /* SDL_cpuinfo.c in Sources */,
   4.169 @@ -1442,7 +1483,9 @@
   4.170  				A7F629241FE06523002F9CC9 /* SDL_uikitmetalview.m in Sources */,
   4.171  				FAB5983C1BB5C31500BE72C5 /* SDL_gesture.c in Sources */,
   4.172  				FAB5983E1BB5C31500BE72C5 /* SDL_keyboard.c in Sources */,
   4.173 +				F3BDD79520F51CB8004ECBF3 /* SDL_hidapi_switch.c in Sources */,
   4.174  				FAB598401BB5C31500BE72C5 /* SDL_mouse.c in Sources */,
   4.175 +				A704172F20F7E76000A82227 /* SDL_gamecontroller.c in Sources */,
   4.176  				FAB598421BB5C31500BE72C5 /* SDL_quit.c in Sources */,
   4.177  				FAB598441BB5C31500BE72C5 /* SDL_touch.c in Sources */,
   4.178  				FAB598461BB5C31500BE72C5 /* SDL_windowevents.c in Sources */,
   4.179 @@ -1454,8 +1497,8 @@
   4.180  				AADC5A5F1FDA105600960936 /* SDL_vulkan_utils.c in Sources */,
   4.181  				AADC5A5E1FDA105300960936 /* SDL_yuv.c in Sources */,
   4.182  				FAB5984D1BB5C31600BE72C5 /* SDL_haptic.c in Sources */,
   4.183 +				F3BDD79320F51CB8004ECBF3 /* SDL_hidapi_xbox360.c in Sources */,
   4.184  				FAB598501BB5C31600BE72C5 /* SDL_sysjoystick.m in Sources */,
   4.185 -				FAB598511BB5C31600BE72C5 /* SDL_gamecontroller.c in Sources */,
   4.186  				FAB598521BB5C31600BE72C5 /* SDL_joystick.c in Sources */,
   4.187  				FAB598551BB5C31600BE72C5 /* SDL_sysloadso.c in Sources */,
   4.188  				AADC5A651FDA10CB00960936 /* SDL_render_metal.m in Sources */,
   4.189 @@ -1482,6 +1525,7 @@
   4.190  				FAB598761BB5C31600BE72C5 /* SDL_stdlib.c in Sources */,
   4.191  				FAB598771BB5C31600BE72C5 /* SDL_string.c in Sources */,
   4.192  				FAB598781BB5C31600BE72C5 /* SDL_syscond.c in Sources */,
   4.193 +				F3BDD79D20F51CB8004ECBF3 /* SDL_hidapijoystick.c in Sources */,
   4.194  				AADC5A601FDA10A400960936 /* SDL_uikitvulkan.m in Sources */,
   4.195  				FAB598791BB5C31600BE72C5 /* SDL_sysmutex.c in Sources */,
   4.196  				FAB5987B1BB5C31600BE72C5 /* SDL_syssem.c in Sources */,
   4.197 @@ -1492,6 +1536,7 @@
   4.198  				FAB598821BB5C31600BE72C5 /* SDL_systimer.c in Sources */,
   4.199  				FAB598831BB5C31600BE72C5 /* SDL_timer.c in Sources */,
   4.200  				FAB598871BB5C31600BE72C5 /* SDL_uikitappdelegate.m in Sources */,
   4.201 +				F3BDD79920F51CB8004ECBF3 /* SDL_hidapi_ps4.c in Sources */,
   4.202  				FAB598891BB5C31600BE72C5 /* SDL_uikitevents.m in Sources */,
   4.203  				FAB5988B1BB5C31600BE72C5 /* SDL_uikitmessagebox.m in Sources */,
   4.204  				FAB5988D1BB5C31600BE72C5 /* SDL_uikitmodes.m in Sources */,
   4.205 @@ -1535,7 +1580,6 @@
   4.206  			files = (
   4.207  				FD6526810DE8FCDD002AD96B /* SDL_systimer.c in Sources */,
   4.208  				FD6526800DE8FCDD002AD96B /* SDL_timer.c in Sources */,
   4.209 -				A7A9EEA91F702631002A5589 /* SDL_steamcontroller.c in Sources */,
   4.210  				FD3F4A7B0DEA620800C5B771 /* SDL_string.c in Sources */,
   4.211  				FD6526660DE8FCDD002AD96B /* SDL_dummyaudio.c in Sources */,
   4.212  				FD6526670DE8FCDD002AD96B /* SDL_audio.c in Sources */,
   4.213 @@ -1566,7 +1610,9 @@
   4.214  				FD3F4A760DEA620800C5B771 /* SDL_getenv.c in Sources */,
   4.215  				FD3F4A770DEA620800C5B771 /* SDL_iconv.c in Sources */,
   4.216  				FD3F4A780DEA620800C5B771 /* SDL_malloc.c in Sources */,
   4.217 +				F3BDD79220F51CB8004ECBF3 /* SDL_hidapi_xbox360.c in Sources */,
   4.218  				FD3F4A790DEA620800C5B771 /* SDL_qsort.c in Sources */,
   4.219 +				F3BDD79820F51CB8004ECBF3 /* SDL_hidapi_ps4.c in Sources */,
   4.220  				FD3F4A7A0DEA620800C5B771 /* SDL_stdlib.c in Sources */,
   4.221  				FDA6844D0DF2374E00F98A1A /* SDL_blit.c in Sources */,
   4.222  				FDA6844F0DF2374E00F98A1A /* SDL_blit_0.c in Sources */,
   4.223 @@ -1599,11 +1645,13 @@
   4.224  				FD689F270E26E5D900F90B21 /* SDL_uikitopenglview.m in Sources */,
   4.225  				FD689FCE0E26E9D400F90B21 /* SDL_uikitappdelegate.m in Sources */,
   4.226  				FD8BD8250E27E25900B52CD5 /* SDL_sysloadso.c in Sources */,
   4.227 +				F3BDD79C20F51CB8004ECBF3 /* SDL_hidapijoystick.c in Sources */,
   4.228  				047677BB0EA76A31008ABAF1 /* SDL_syshaptic.c in Sources */,
   4.229  				047677BC0EA76A31008ABAF1 /* SDL_haptic.c in Sources */,
   4.230  				047AF1B30EA98D6C00811173 /* SDL_sysloadso.c in Sources */,
   4.231  				046387460F0B5B7D0041FD65 /* SDL_fillrect.c in Sources */,
   4.232  				04F2AF561104ABD200D6DDF7 /* SDL_assert.c in Sources */,
   4.233 +				F3BDD79620F51CB8004ECBF3 /* SDL_hidapi_xboxone.c in Sources */,
   4.234  				56ED04E1118A8EE200A56AA6 /* SDL_power.c in Sources */,
   4.235  				56ED04E3118A8EFD00A56AA6 /* SDL_syspower.m in Sources */,
   4.236  				006E9889119552DD001DE610 /* SDL_rwopsbundlesupport.m in Sources */,
   4.237 @@ -1629,10 +1677,12 @@
   4.238  				0402A85912FE70C600CECEE3 /* SDL_shaders_gles2.c in Sources */,
   4.239  				04BAC09D1300C1290055DE28 /* SDL_log.c in Sources */,
   4.240  				56EA86FB13E9EC2B002E47EB /* SDL_coreaudio.m in Sources */,
   4.241 +				F3BDD79420F51CB8004ECBF3 /* SDL_hidapi_switch.c in Sources */,
   4.242  				93CB792613FC5F5300BD3E05 /* SDL_uikitviewcontroller.m in Sources */,
   4.243  				AA628ADB159369E3005138DD /* SDL_rotate.c in Sources */,
   4.244  				AA126AD51617C5E7005ABC8F /* SDL_uikitmodes.m in Sources */,
   4.245  				AA704DD7162AA90A0076D1C1 /* SDL_dropevents.c in Sources */,
   4.246 +				F3BDD77620F51C3C004ECBF3 /* hid.mm in Sources */,
   4.247  				AABCC3951640643D00AB8930 /* SDL_uikitmessagebox.m in Sources */,
   4.248  				AA0AD06216647BBB00CE5896 /* SDL_gamecontroller.c in Sources */,
   4.249  				AA0F8495178D5F1A00823F9D /* SDL_systls.c in Sources */,
     5.1 --- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj	Wed Aug 08 15:39:20 2018 -0400
     5.2 +++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj	Thu Aug 09 16:00:17 2018 -0700
     5.3 @@ -462,6 +462,30 @@
     5.4  		5C2EF6FE1FC9EE65003F5197 /* SDL_egl.c in Sources */ = {isa = PBXBuildFile; fileRef = 5C2EF6F51FC9EE35003F5197 /* SDL_egl.c */; };
     5.5  		5C2EF6FF1FC9EE65003F5197 /* SDL_rect_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C2EF6F41FC9EE34003F5197 /* SDL_rect_c.h */; };
     5.6  		5C2EF7011FC9EF10003F5197 /* SDL_egl.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C2EF7001FC9EF0F003F5197 /* SDL_egl.h */; };
     5.7 +		A704170920F09A9800A82227 /* hid.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170820F09A9800A82227 /* hid.c */; };
     5.8 +		A704170A20F09A9800A82227 /* hid.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170820F09A9800A82227 /* hid.c */; };
     5.9 +		A704170B20F09A9800A82227 /* hid.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170820F09A9800A82227 /* hid.c */; };
    5.10 +		A704171420F09AC900A82227 /* SDL_hidapijoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170D20F09AC800A82227 /* SDL_hidapijoystick.c */; };
    5.11 +		A704171520F09AC900A82227 /* SDL_hidapijoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170D20F09AC800A82227 /* SDL_hidapijoystick.c */; };
    5.12 +		A704171620F09AC900A82227 /* SDL_hidapijoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170D20F09AC800A82227 /* SDL_hidapijoystick.c */; };
    5.13 +		A704171720F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A704170E20F09AC800A82227 /* SDL_hidapijoystick_c.h */; };
    5.14 +		A704171820F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A704170E20F09AC800A82227 /* SDL_hidapijoystick_c.h */; };
    5.15 +		A704171920F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A704170E20F09AC800A82227 /* SDL_hidapijoystick_c.h */; };
    5.16 +		A704171A20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170F20F09AC800A82227 /* SDL_hidapi_switch.c */; };
    5.17 +		A704171B20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170F20F09AC800A82227 /* SDL_hidapi_switch.c */; };
    5.18 +		A704171C20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170F20F09AC800A82227 /* SDL_hidapi_switch.c */; };
    5.19 +		A704171D20F09AC900A82227 /* controller_type.h in Headers */ = {isa = PBXBuildFile; fileRef = A704171020F09AC900A82227 /* controller_type.h */; };
    5.20 +		A704171E20F09AC900A82227 /* controller_type.h in Headers */ = {isa = PBXBuildFile; fileRef = A704171020F09AC900A82227 /* controller_type.h */; };
    5.21 +		A704171F20F09AC900A82227 /* controller_type.h in Headers */ = {isa = PBXBuildFile; fileRef = A704171020F09AC900A82227 /* controller_type.h */; };
    5.22 +		A704172020F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171120F09AC900A82227 /* SDL_hidapi_ps4.c */; };
    5.23 +		A704172120F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171120F09AC900A82227 /* SDL_hidapi_ps4.c */; };
    5.24 +		A704172220F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171120F09AC900A82227 /* SDL_hidapi_ps4.c */; };
    5.25 +		A704172320F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171220F09AC900A82227 /* SDL_hidapi_xboxone.c */; };
    5.26 +		A704172420F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171220F09AC900A82227 /* SDL_hidapi_xboxone.c */; };
    5.27 +		A704172520F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171220F09AC900A82227 /* SDL_hidapi_xboxone.c */; };
    5.28 +		A704172620F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171320F09AC900A82227 /* SDL_hidapi_xbox360.c */; };
    5.29 +		A704172720F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171320F09AC900A82227 /* SDL_hidapi_xbox360.c */; };
    5.30 +		A704172820F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171320F09AC900A82227 /* SDL_hidapi_xbox360.c */; };
    5.31  		A7381E961D8B69D600B177DD /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7381E951D8B69D600B177DD /* CoreAudio.framework */; };
    5.32  		A7381E971D8B6A0300B177DD /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7381E931D8B69C300B177DD /* AudioToolbox.framework */; };
    5.33  		A77E6EB4167AB0A90010E40B /* SDL_gamecontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = A77E6EB3167AB0A90010E40B /* SDL_gamecontroller.h */; settings = {ATTRIBUTES = (Public, ); }; };
    5.34 @@ -1114,6 +1138,14 @@
    5.35  		5C2EF6F51FC9EE35003F5197 /* SDL_egl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_egl.c; sourceTree = "<group>"; };
    5.36  		5C2EF6F61FC9EE35003F5197 /* SDL_egl_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_egl_c.h; sourceTree = "<group>"; };
    5.37  		5C2EF7001FC9EF0F003F5197 /* SDL_egl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_egl.h; sourceTree = "<group>"; };
    5.38 +		A704170820F09A9800A82227 /* hid.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hid.c; sourceTree = "<group>"; };
    5.39 +		A704170D20F09AC800A82227 /* SDL_hidapijoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapijoystick.c; sourceTree = "<group>"; };
    5.40 +		A704170E20F09AC800A82227 /* SDL_hidapijoystick_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_hidapijoystick_c.h; sourceTree = "<group>"; };
    5.41 +		A704170F20F09AC800A82227 /* SDL_hidapi_switch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_switch.c; sourceTree = "<group>"; };
    5.42 +		A704171020F09AC900A82227 /* controller_type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = controller_type.h; sourceTree = "<group>"; };
    5.43 +		A704171120F09AC900A82227 /* SDL_hidapi_ps4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_ps4.c; sourceTree = "<group>"; };
    5.44 +		A704171220F09AC900A82227 /* SDL_hidapi_xboxone.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_xboxone.c; sourceTree = "<group>"; };
    5.45 +		A704171320F09AC900A82227 /* SDL_hidapi_xbox360.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_xbox360.c; sourceTree = "<group>"; };
    5.46  		A7381E931D8B69C300B177DD /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
    5.47  		A7381E951D8B69D600B177DD /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
    5.48  		A77E6EB3167AB0A90010E40B /* SDL_gamecontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gamecontroller.h; sourceTree = "<group>"; };
    5.49 @@ -1536,6 +1568,7 @@
    5.50  		04BDFDFF12E6671700899322 /* joystick */ = {
    5.51  			isa = PBXGroup;
    5.52  			children = (
    5.53 +				A704170C20F09AA600A82227 /* hidapi */,
    5.54  				04BDFE0612E6671700899322 /* darwin */,
    5.55  				04BDFE1612E6671700899322 /* SDL_joystick.c */,
    5.56  				04BDFE1712E6671700899322 /* SDL_joystick_c.h */,
    5.57 @@ -1818,6 +1851,7 @@
    5.58  				567E2F1F17C44BBB005F1892 /* filesystem */,
    5.59  				04BDFDEC12E6671700899322 /* file */,
    5.60  				04BDFDF112E6671700899322 /* haptic */,
    5.61 +				A73EBCD520F099C10043B449 /* hidapi */,
    5.62  				04BDFDFF12E6671700899322 /* joystick */,
    5.63  				04BDFE2F12E6671700899322 /* loadso */,
    5.64  				04BDFE4512E6671700899322 /* power */,
    5.65 @@ -1879,6 +1913,37 @@
    5.66  			path = opengles2;
    5.67  			sourceTree = "<group>";
    5.68  		};
    5.69 +		A704170720F09A6700A82227 /* mac */ = {
    5.70 +			isa = PBXGroup;
    5.71 +			children = (
    5.72 +				A704170820F09A9800A82227 /* hid.c */,
    5.73 +			);
    5.74 +			path = mac;
    5.75 +			sourceTree = "<group>";
    5.76 +		};
    5.77 +		A704170C20F09AA600A82227 /* hidapi */ = {
    5.78 +			isa = PBXGroup;
    5.79 +			children = (
    5.80 +				A704171020F09AC900A82227 /* controller_type.h */,
    5.81 +				A704171120F09AC900A82227 /* SDL_hidapi_ps4.c */,
    5.82 +				A704170F20F09AC800A82227 /* SDL_hidapi_switch.c */,
    5.83 +				A704171320F09AC900A82227 /* SDL_hidapi_xbox360.c */,
    5.84 +				A704171220F09AC900A82227 /* SDL_hidapi_xboxone.c */,
    5.85 +				A704170E20F09AC800A82227 /* SDL_hidapijoystick_c.h */,
    5.86 +				A704170D20F09AC800A82227 /* SDL_hidapijoystick.c */,
    5.87 +			);
    5.88 +			path = hidapi;
    5.89 +			sourceTree = "<group>";
    5.90 +		};
    5.91 +		A73EBCD520F099C10043B449 /* hidapi */ = {
    5.92 +			isa = PBXGroup;
    5.93 +			children = (
    5.94 +				A704170720F09A6700A82227 /* mac */,
    5.95 +			);
    5.96 +			name = hidapi;
    5.97 +			path = ../../src/hidapi;
    5.98 +			sourceTree = SOURCE_ROOT;
    5.99 +		};
   5.100  		AA9A7F0E1FB0200B00FED37F /* yuv2rgb */ = {
   5.101  			isa = PBXGroup;
   5.102  			children = (
   5.103 @@ -1993,6 +2058,7 @@
   5.104  				AA7558421595D4D800BBD41B /* SDL_revision.h in Headers */,
   5.105  				AA7558441595D4D800BBD41B /* SDL_rwops.h in Headers */,
   5.106  				AA7558461595D4D800BBD41B /* SDL_scancode.h in Headers */,
   5.107 +				A704171720F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */,
   5.108  				AA7558481595D4D800BBD41B /* SDL_shape.h in Headers */,
   5.109  				AA75584A1595D4D800BBD41B /* SDL_stdinc.h in Headers */,
   5.110  				AA75584C1595D4D800BBD41B /* SDL_surface.h in Headers */,
   5.111 @@ -2083,6 +2149,7 @@
   5.112  				04BD01F912E6671800899322 /* SDL_x11window.h in Headers */,
   5.113  				041B2CA612FA0D680087D585 /* SDL_sysrender.h in Headers */,
   5.114  				AA9A7F161FB0209D00FED37F /* SDL_yuv_c.h in Headers */,
   5.115 +				A704171D20F09AC900A82227 /* controller_type.h in Headers */,
   5.116  				04409B9312FA97ED00FB9AA8 /* SDL_yuv_sw_c.h in Headers */,
   5.117  				04F7803912FB748500FC43C0 /* SDL_nullframebuffer_c.h in Headers */,
   5.118  				04F7804A12FB74A200FC43C0 /* SDL_blendfillrect.h in Headers */,
   5.119 @@ -2132,6 +2199,7 @@
   5.120  				AA75581F1595D4D800BBD41B /* SDL_joystick.h in Headers */,
   5.121  				AA7558211595D4D800BBD41B /* SDL_keyboard.h in Headers */,
   5.122  				AA7558231595D4D800BBD41B /* SDL_keycode.h in Headers */,
   5.123 +				A704171E20F09AC900A82227 /* controller_type.h in Headers */,
   5.124  				AA7558251595D4D800BBD41B /* SDL_loadso.h in Headers */,
   5.125  				AA7558271595D4D800BBD41B /* SDL_log.h in Headers */,
   5.126  				AA7558291595D4D800BBD41B /* SDL_main.h in Headers */,
   5.127 @@ -2207,6 +2275,7 @@
   5.128  				04BD02DC12E6671800899322 /* SDL_systhread_c.h in Headers */,
   5.129  				04BD02E312E6671800899322 /* SDL_systhread.h in Headers */,
   5.130  				04BD02E512E6671800899322 /* SDL_thread_c.h in Headers */,
   5.131 +				A704171820F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */,
   5.132  				04BD02F212E6671800899322 /* SDL_timer_c.h in Headers */,
   5.133  				04BD030D12E6671800899322 /* SDL_cocoaclipboard.h in Headers */,
   5.134  				04BD030F12E6671800899322 /* SDL_cocoaevents.h in Headers */,
   5.135 @@ -2296,6 +2365,7 @@
   5.136  				DB313FD917554B71006C0E22 /* SDL_joystick.h in Headers */,
   5.137  				DB313FDA17554B71006C0E22 /* SDL_keyboard.h in Headers */,
   5.138  				DB313FDB17554B71006C0E22 /* SDL_keycode.h in Headers */,
   5.139 +				A704171F20F09AC900A82227 /* controller_type.h in Headers */,
   5.140  				DB313FDC17554B71006C0E22 /* SDL_loadso.h in Headers */,
   5.141  				DB313FDD17554B71006C0E22 /* SDL_log.h in Headers */,
   5.142  				DB313FDE17554B71006C0E22 /* SDL_main.h in Headers */,
   5.143 @@ -2371,6 +2441,7 @@
   5.144  				DB313F9317554B71006C0E22 /* SDL_systhread_c.h in Headers */,
   5.145  				DB313F9417554B71006C0E22 /* SDL_systhread.h in Headers */,
   5.146  				DB313F9517554B71006C0E22 /* SDL_thread_c.h in Headers */,
   5.147 +				A704171920F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */,
   5.148  				DB313F9617554B71006C0E22 /* SDL_timer_c.h in Headers */,
   5.149  				DB313F9717554B71006C0E22 /* SDL_cocoaclipboard.h in Headers */,
   5.150  				DB313F9817554B71006C0E22 /* SDL_cocoaevents.h in Headers */,
   5.151 @@ -2602,6 +2673,7 @@
   5.152  				04BD004112E6671800899322 /* SDL_cpuinfo.c in Sources */,
   5.153  				04BD004812E6671800899322 /* SDL_clipboardevents.c in Sources */,
   5.154  				04BD004A12E6671800899322 /* SDL_events.c in Sources */,
   5.155 +				A704172620F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */,
   5.156  				04BD004C12E6671800899322 /* SDL_gesture.c in Sources */,
   5.157  				04BD004E12E6671800899322 /* SDL_keyboard.c in Sources */,
   5.158  				04BD005012E6671800899322 /* SDL_mouse.c in Sources */,
   5.159 @@ -2640,6 +2712,7 @@
   5.160  				04BD00F612E6671800899322 /* SDL_cocoaevents.m in Sources */,
   5.161  				04BD00F812E6671800899322 /* SDL_cocoakeyboard.m in Sources */,
   5.162  				AA9A7F151FB0209D00FED37F /* SDL_yuv.c in Sources */,
   5.163 +				A704171A20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */,
   5.164  				04BD00FA12E6671800899322 /* SDL_cocoamodes.m in Sources */,
   5.165  				4D16644F1EDD6023003DE88E /* SDL_vulkan_utils.c in Sources */,
   5.166  				04BD00FC12E6671800899322 /* SDL_cocoamouse.m in Sources */,
   5.167 @@ -2648,6 +2721,7 @@
   5.168  				04BD010212E6671800899322 /* SDL_cocoavideo.m in Sources */,
   5.169  				04BD010412E6671800899322 /* SDL_cocoawindow.m in Sources */,
   5.170  				04BD011712E6671800899322 /* SDL_nullevents.c in Sources */,
   5.171 +				A704172320F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */,
   5.172  				04BD011B12E6671800899322 /* SDL_nullvideo.c in Sources */,
   5.173  				04BD017512E6671800899322 /* SDL_blit.c in Sources */,
   5.174  				04BD017712E6671800899322 /* SDL_blit_0.c in Sources */,
   5.175 @@ -2656,6 +2730,8 @@
   5.176  				04BD017912E6671800899322 /* SDL_blit_A.c in Sources */,
   5.177  				04BD017A12E6671800899322 /* SDL_blit_auto.c in Sources */,
   5.178  				04BD017C12E6671800899322 /* SDL_blit_copy.c in Sources */,
   5.179 +				A704172020F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */,
   5.180 +				A704170920F09A9800A82227 /* hid.c in Sources */,
   5.181  				04BD017E12E6671800899322 /* SDL_blit_N.c in Sources */,
   5.182  				04BD017F12E6671800899322 /* SDL_blit_slow.c in Sources */,
   5.183  				04BD018112E6671800899322 /* SDL_bmp.c in Sources */,
   5.184 @@ -2664,6 +2740,7 @@
   5.185  				04BD018C12E6671800899322 /* SDL_pixels.c in Sources */,
   5.186  				04BD018E12E6671800899322 /* SDL_rect.c in Sources */,
   5.187  				04BD019612E6671800899322 /* SDL_RLEaccel.c in Sources */,
   5.188 +				A704171420F09AC900A82227 /* SDL_hidapijoystick.c in Sources */,
   5.189  				04BD019812E6671800899322 /* SDL_shape.c in Sources */,
   5.190  				04BD019A12E6671800899322 /* SDL_stretch.c in Sources */,
   5.191  				04BD019B12E6671800899322 /* SDL_surface.c in Sources */,
   5.192 @@ -2731,6 +2808,7 @@
   5.193  				04BD024812E6671800899322 /* SDL_audiotypecvt.c in Sources */,
   5.194  				04BD024912E6671800899322 /* SDL_mixer.c in Sources */,
   5.195  				04BD025112E6671800899322 /* SDL_wave.c in Sources */,
   5.196 +				A704172720F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */,
   5.197  				04BD025C12E6671800899322 /* SDL_cpuinfo.c in Sources */,
   5.198  				04BD026312E6671800899322 /* SDL_clipboardevents.c in Sources */,
   5.199  				04BD026512E6671800899322 /* SDL_events.c in Sources */,
   5.200 @@ -2769,6 +2847,7 @@
   5.201  				04BD02E412E6671800899322 /* SDL_thread.c in Sources */,
   5.202  				04BD02F112E6671800899322 /* SDL_timer.c in Sources */,
   5.203  				04BD02F312E6671800899322 /* SDL_systimer.c in Sources */,
   5.204 +				A704171B20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */,
   5.205  				04BD030E12E6671800899322 /* SDL_cocoaclipboard.m in Sources */,
   5.206  				04BD031012E6671800899322 /* SDL_cocoaevents.m in Sources */,
   5.207  				04BD031212E6671800899322 /* SDL_cocoakeyboard.m in Sources */,
   5.208 @@ -2777,6 +2856,7 @@
   5.209  				5C2EF6A31FC98B38003F5197 /* SDL_yuv.c in Sources */,
   5.210  				5C2EF6F11FC9D181003F5197 /* SDL_cocoaopengles.m in Sources */,
   5.211  				04BD031812E6671800899322 /* SDL_cocoaopengl.m in Sources */,
   5.212 +				A704172420F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */,
   5.213  				04BD031A12E6671800899322 /* SDL_cocoashape.m in Sources */,
   5.214  				04BD031C12E6671800899322 /* SDL_cocoavideo.m in Sources */,
   5.215  				04BD031E12E6671800899322 /* SDL_cocoawindow.m in Sources */,
   5.216 @@ -2785,6 +2865,8 @@
   5.217  				5C2EF6A51FC98B6B003F5197 /* yuv_rgb.c in Sources */,
   5.218  				04BD038F12E6671800899322 /* SDL_blit.c in Sources */,
   5.219  				04BD039112E6671800899322 /* SDL_blit_0.c in Sources */,
   5.220 +				A704172120F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */,
   5.221 +				A704170A20F09A9800A82227 /* hid.c in Sources */,
   5.222  				04BD039212E6671800899322 /* SDL_blit_1.c in Sources */,
   5.223  				04BD039312E6671800899322 /* SDL_blit_A.c in Sources */,
   5.224  				04BD039412E6671800899322 /* SDL_blit_auto.c in Sources */,
   5.225 @@ -2793,6 +2875,7 @@
   5.226  				04BD039912E6671800899322 /* SDL_blit_slow.c in Sources */,
   5.227  				04BD039B12E6671800899322 /* SDL_bmp.c in Sources */,
   5.228  				04BD039C12E6671800899322 /* SDL_clipboard.c in Sources */,
   5.229 +				A704171520F09AC900A82227 /* SDL_hidapijoystick.c in Sources */,
   5.230  				04BD03A112E6671800899322 /* SDL_fillrect.c in Sources */,
   5.231  				04BD03A612E6671800899322 /* SDL_pixels.c in Sources */,
   5.232  				04BD03A812E6671800899322 /* SDL_rect.c in Sources */,
   5.233 @@ -2860,6 +2943,7 @@
   5.234  				DB31400617554B71006C0E22 /* SDL_audiotypecvt.c in Sources */,
   5.235  				DB31400717554B71006C0E22 /* SDL_mixer.c in Sources */,
   5.236  				DB31400817554B71006C0E22 /* SDL_wave.c in Sources */,
   5.237 +				A704172820F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */,
   5.238  				DB31400917554B71006C0E22 /* SDL_cpuinfo.c in Sources */,
   5.239  				DB31400A17554B71006C0E22 /* SDL_clipboardevents.c in Sources */,
   5.240  				DB31400B17554B71006C0E22 /* SDL_events.c in Sources */,
   5.241 @@ -2898,6 +2982,7 @@
   5.242  				DB31402B17554B71006C0E22 /* SDL_thread.c in Sources */,
   5.243  				DB31402C17554B71006C0E22 /* SDL_timer.c in Sources */,
   5.244  				DB31402D17554B71006C0E22 /* SDL_systimer.c in Sources */,
   5.245 +				A704171C20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */,
   5.246  				DB31402E17554B71006C0E22 /* SDL_cocoaclipboard.m in Sources */,
   5.247  				DB31402F17554B71006C0E22 /* SDL_cocoaevents.m in Sources */,
   5.248  				DB31403017554B71006C0E22 /* SDL_cocoakeyboard.m in Sources */,
   5.249 @@ -2906,6 +2991,7 @@
   5.250  				5C2EF6A41FC98B39003F5197 /* SDL_yuv.c in Sources */,
   5.251  				5C2EF6F31FC9D182003F5197 /* SDL_cocoaopengles.m in Sources */,
   5.252  				DB31403317554B71006C0E22 /* SDL_cocoaopengl.m in Sources */,
   5.253 +				A704172520F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */,
   5.254  				DB31403417554B71006C0E22 /* SDL_cocoashape.m in Sources */,
   5.255  				DB31403517554B71006C0E22 /* SDL_cocoavideo.m in Sources */,
   5.256  				DB31403617554B71006C0E22 /* SDL_cocoawindow.m in Sources */,
   5.257 @@ -2914,6 +3000,8 @@
   5.258  				5C2EF6A61FC98B6C003F5197 /* yuv_rgb.c in Sources */,
   5.259  				DB31403917554B71006C0E22 /* SDL_blit.c in Sources */,
   5.260  				DB31403A17554B71006C0E22 /* SDL_blit_0.c in Sources */,
   5.261 +				A704172220F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */,
   5.262 +				A704170B20F09A9800A82227 /* hid.c in Sources */,
   5.263  				DB31403B17554B71006C0E22 /* SDL_blit_1.c in Sources */,
   5.264  				DB31403C17554B71006C0E22 /* SDL_blit_A.c in Sources */,
   5.265  				DB31403D17554B71006C0E22 /* SDL_blit_auto.c in Sources */,
   5.266 @@ -2922,6 +3010,7 @@
   5.267  				DB31404017554B71006C0E22 /* SDL_blit_slow.c in Sources */,
   5.268  				DB31404117554B71006C0E22 /* SDL_bmp.c in Sources */,
   5.269  				DB31404217554B71006C0E22 /* SDL_clipboard.c in Sources */,
   5.270 +				A704171620F09AC900A82227 /* SDL_hidapijoystick.c in Sources */,
   5.271  				DB31404317554B71006C0E22 /* SDL_fillrect.c in Sources */,
   5.272  				DB31404417554B71006C0E22 /* SDL_pixels.c in Sources */,
   5.273  				DB31404517554B71006C0E22 /* SDL_rect.c in Sources */,
   5.274 @@ -3019,6 +3108,7 @@
   5.275  					/usr/X11R6/include,
   5.276  					"$(VULKAN_SDK)/include",
   5.277  					../../src/video/khronos,
   5.278 +					../../src/hidapi/hidapi,
   5.279  				);
   5.280  				MACOSX_DEPLOYMENT_TARGET = 10.6;
   5.281  				SDKROOT = macosx;
   5.282 @@ -3114,6 +3204,7 @@
   5.283  					/usr/X11R6/include,
   5.284  					"$(VULKAN_SDK)/include",
   5.285  					../../src/video/khronos,
   5.286 +					../../src/hidapi/hidapi,
   5.287  				);
   5.288  				MACOSX_DEPLOYMENT_TARGET = 10.6;
   5.289  				ONLY_ACTIVE_ARCH = YES;
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/android-project/app/src/main/java/org/libsdl/app/HIDDevice.java	Thu Aug 09 16:00:17 2018 -0700
     6.3 @@ -0,0 +1,19 @@
     6.4 +package org.libsdl.app;
     6.5 +
     6.6 +interface HIDDevice
     6.7 +{
     6.8 +    public int getId();
     6.9 +    public int getVendorId();
    6.10 +    public int getProductId();
    6.11 +    public String getSerialNumber();
    6.12 +    public int getVersion();
    6.13 +    public String getManufacturerName();
    6.14 +    public String getProductName();
    6.15 +    public boolean open();
    6.16 +    public int sendFeatureReport(byte[] report);
    6.17 +    public int sendOutputReport(byte[] report);
    6.18 +    public boolean getFeatureReport(byte[] report);
    6.19 +    public void setFrozen(boolean frozen);
    6.20 +    public void close();
    6.21 +    public void shutdown();
    6.22 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java	Thu Aug 09 16:00:17 2018 -0700
     7.3 @@ -0,0 +1,640 @@
     7.4 +package org.libsdl.app;
     7.5 +
     7.6 +import android.content.Context;
     7.7 +import android.bluetooth.BluetoothDevice;
     7.8 +import android.bluetooth.BluetoothGatt;
     7.9 +import android.bluetooth.BluetoothGattCallback;
    7.10 +import android.bluetooth.BluetoothGattCharacteristic;
    7.11 +import android.bluetooth.BluetoothGattDescriptor;
    7.12 +import android.bluetooth.BluetoothManager;
    7.13 +import android.bluetooth.BluetoothProfile;
    7.14 +import android.bluetooth.BluetoothGattService;
    7.15 +import android.os.Handler;
    7.16 +import android.os.Looper;
    7.17 +import android.util.Log;
    7.18 +
    7.19 +import com.android.internal.util.HexDump;
    7.20 +
    7.21 +import java.lang.Runnable;
    7.22 +import java.lang.reflect.InvocationTargetException;
    7.23 +import java.lang.reflect.Method;
    7.24 +import java.util.Arrays;
    7.25 +import java.util.LinkedList;
    7.26 +import java.util.UUID;
    7.27 +
    7.28 +class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
    7.29 +
    7.30 +    private static final String TAG = "hidapi";
    7.31 +    private HIDDeviceManager mManager;
    7.32 +    private BluetoothDevice mDevice;
    7.33 +    private int mDeviceId;
    7.34 +    private BluetoothGatt mGatt;
    7.35 +    private boolean mIsRegistered = false;
    7.36 +    private boolean mIsConnected = false;
    7.37 +    private boolean mIsChromebook = false;
    7.38 +    private boolean mIsReconnecting = false;
    7.39 +    private boolean mFrozen = false;
    7.40 +    private LinkedList<GattOperation> mOperations;
    7.41 +    GattOperation mCurrentOperation = null;
    7.42 +    private Handler mHandler;
    7.43 +
    7.44 +    private static final int TRANSPORT_AUTO = 0;
    7.45 +    private static final int TRANSPORT_BREDR = 1;
    7.46 +    private static final int TRANSPORT_LE = 2;
    7.47 +
    7.48 +    private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
    7.49 +
    7.50 +    static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
    7.51 +    static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
    7.52 +    static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
    7.53 +    static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
    7.54 +
    7.55 +    static class GattOperation {
    7.56 +        private enum Operation {
    7.57 +            CHR_READ,
    7.58 +            CHR_WRITE,
    7.59 +            ENABLE_NOTIFICATION
    7.60 +        }
    7.61 +
    7.62 +        Operation mOp;
    7.63 +        UUID mUuid;
    7.64 +        byte[] mValue;
    7.65 +        BluetoothGatt mGatt;
    7.66 +        boolean mResult = true;
    7.67 +
    7.68 +        private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
    7.69 +            mGatt = gatt;
    7.70 +            mOp = operation;
    7.71 +            mUuid = uuid;
    7.72 +        }
    7.73 +
    7.74 +        private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
    7.75 +            mGatt = gatt;
    7.76 +            mOp = operation;
    7.77 +            mUuid = uuid;
    7.78 +            mValue = value;
    7.79 +        }
    7.80 +
    7.81 +        public void run() {
    7.82 +            // This is executed in main thread
    7.83 +            BluetoothGattCharacteristic chr;
    7.84 +
    7.85 +            switch (mOp) {
    7.86 +                case CHR_READ:
    7.87 +                    chr = getCharacteristic(mUuid);
    7.88 +                    //Log.v(TAG, "Reading characteristic " + chr.getUuid());
    7.89 +                    if (!mGatt.readCharacteristic(chr)) {
    7.90 +                        Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
    7.91 +                        mResult = false;
    7.92 +                        break;
    7.93 +                    }
    7.94 +                    mResult = true;
    7.95 +                    break;
    7.96 +                case CHR_WRITE:
    7.97 +                    chr = getCharacteristic(mUuid);
    7.98 +                    //Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
    7.99 +                    chr.setValue(mValue);
   7.100 +                    if (!mGatt.writeCharacteristic(chr)) {
   7.101 +                        Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
   7.102 +                        mResult = false;
   7.103 +                        break;
   7.104 +                    }
   7.105 +                    mResult = true;
   7.106 +                    break;
   7.107 +                case ENABLE_NOTIFICATION:
   7.108 +                    chr = getCharacteristic(mUuid);
   7.109 +                    //Log.v(TAG, "Writing descriptor of " + chr.getUuid());
   7.110 +                    if (chr != null) {
   7.111 +                        BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
   7.112 +                        if (cccd != null) {
   7.113 +                            int properties = chr.getProperties();
   7.114 +                            byte[] value;
   7.115 +                            if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
   7.116 +                                value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
   7.117 +                            } else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
   7.118 +                                value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
   7.119 +                            } else {
   7.120 +                                Log.e(TAG, "Unable to start notifications on input characteristic");
   7.121 +                                mResult = false;
   7.122 +                                return;
   7.123 +                            }
   7.124 +
   7.125 +                            mGatt.setCharacteristicNotification(chr, true);
   7.126 +                            cccd.setValue(value);
   7.127 +                            if (!mGatt.writeDescriptor(cccd)) {
   7.128 +                                Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
   7.129 +                                mResult = false;
   7.130 +                                return;
   7.131 +                            }
   7.132 +                            mResult = true;
   7.133 +                        }
   7.134 +                    }
   7.135 +            }
   7.136 +        }
   7.137 +
   7.138 +        public boolean finish() {
   7.139 +            return mResult;
   7.140 +        }
   7.141 +
   7.142 +        private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
   7.143 +            BluetoothGattService valveService = mGatt.getService(steamControllerService);
   7.144 +            if (valveService == null)
   7.145 +                return null;
   7.146 +            return valveService.getCharacteristic(uuid);
   7.147 +        }
   7.148 +
   7.149 +        static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
   7.150 +            return new GattOperation(gatt, Operation.CHR_READ, uuid);
   7.151 +        }
   7.152 +
   7.153 +        static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
   7.154 +            return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
   7.155 +        }
   7.156 +
   7.157 +        static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
   7.158 +            return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
   7.159 +        }
   7.160 +    }
   7.161 +
   7.162 +    public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
   7.163 +        mManager = manager;
   7.164 +        mDevice = device;
   7.165 +        mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
   7.166 +        mIsRegistered = false;
   7.167 +        mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
   7.168 +        mOperations = new LinkedList<GattOperation>();
   7.169 +        mHandler = new Handler(Looper.getMainLooper());
   7.170 +
   7.171 +        mGatt = connectGatt();
   7.172 +        final HIDDeviceBLESteamController finalThis = this;
   7.173 +        mHandler.postDelayed(new Runnable() {
   7.174 +            @Override
   7.175 +            public void run() {
   7.176 +                finalThis.checkConnectionForChromebookIssue();
   7.177 +            }
   7.178 +        }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
   7.179 +    }
   7.180 +
   7.181 +    public String getIdentifier() {
   7.182 +        return String.format("SteamController.%s", mDevice.getAddress());
   7.183 +    }
   7.184 +
   7.185 +    public BluetoothGatt getGatt() {
   7.186 +        return mGatt;
   7.187 +    }
   7.188 +
   7.189 +    // Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
   7.190 +    // of TRANSPORT_LE.  Let's force ourselves to connect low energy.
   7.191 +    private BluetoothGatt connectGatt(boolean managed) {
   7.192 +        try {
   7.193 +            Method m = mDevice.getClass().getDeclaredMethod("connectGatt", Context.class, boolean.class, BluetoothGattCallback.class, int.class);
   7.194 +            return (BluetoothGatt) m.invoke(mDevice, mManager.getContext(), managed, this, TRANSPORT_LE);
   7.195 +        } catch (Exception e) {
   7.196 +            return mDevice.connectGatt(mManager.getContext(), managed, this);
   7.197 +        }
   7.198 +    }
   7.199 +
   7.200 +    private BluetoothGatt connectGatt() {
   7.201 +        return connectGatt(false);
   7.202 +    }
   7.203 +
   7.204 +    protected int getConnectionState() {
   7.205 +
   7.206 +        Context context = mManager.getContext();
   7.207 +        if (context == null) {
   7.208 +            // We are lacking any context to get our Bluetooth information.  We'll just assume disconnected.
   7.209 +            return BluetoothProfile.STATE_DISCONNECTED;
   7.210 +        }
   7.211 +
   7.212 +        BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
   7.213 +        if (btManager == null) {
   7.214 +            // This device doesn't support Bluetooth.  We should never be here, because how did
   7.215 +            // we instantiate a device to start with?
   7.216 +            return BluetoothProfile.STATE_DISCONNECTED;
   7.217 +        }
   7.218 +
   7.219 +        return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
   7.220 +    }
   7.221 +
   7.222 +    public void reconnect() {
   7.223 +
   7.224 +        if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
   7.225 +            mGatt.disconnect();
   7.226 +            mGatt = connectGatt();
   7.227 +        }
   7.228 +
   7.229 +    }
   7.230 +
   7.231 +    protected void checkConnectionForChromebookIssue() {
   7.232 +        if (!mIsChromebook) {
   7.233 +            // We only do this on Chromebooks, because otherwise it's really annoying to just attempt
   7.234 +            // over and over.
   7.235 +            return;
   7.236 +        }
   7.237 +
   7.238 +        int connectionState = getConnectionState();
   7.239 +
   7.240 +        switch (connectionState) {
   7.241 +            case BluetoothProfile.STATE_CONNECTED:
   7.242 +                if (!mIsConnected) {
   7.243 +                    // We are in the Bad Chromebook Place.  We can force a disconnect
   7.244 +                    // to try to recover.
   7.245 +                    Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback.  Forcing a reconnect.");
   7.246 +                    mIsReconnecting = true;
   7.247 +                    mGatt.disconnect();
   7.248 +                    mGatt = connectGatt(false);
   7.249 +                    break;
   7.250 +                }
   7.251 +                else if (!isRegistered()) {
   7.252 +                    if (mGatt.getServices().size() > 0) {
   7.253 +                        Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration.  Trying to recover.");
   7.254 +                        probeService(this);
   7.255 +                    }
   7.256 +                    else {
   7.257 +                        Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services.  Trying to recover.");
   7.258 +                        mIsReconnecting = true;
   7.259 +                        mGatt.disconnect();
   7.260 +                        mGatt = connectGatt(false);
   7.261 +                        break;
   7.262 +                    }
   7.263 +                }
   7.264 +                else {
   7.265 +                    Log.v(TAG, "Chromebook: We are connected, and registered.  Everything's good!");
   7.266 +                    return;
   7.267 +                }
   7.268 +                break;
   7.269 +
   7.270 +            case BluetoothProfile.STATE_DISCONNECTED:
   7.271 +                Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us.  Attempting a disconnect/reconnect, but we may not be able to recover.");
   7.272 +
   7.273 +                mIsReconnecting = true;
   7.274 +                mGatt.disconnect();
   7.275 +                mGatt = connectGatt(false);
   7.276 +                break;
   7.277 +
   7.278 +            case BluetoothProfile.STATE_CONNECTING:
   7.279 +                Log.v(TAG, "Chromebook: We're still trying to connect.  Waiting a bit longer.");
   7.280 +                break;
   7.281 +        }
   7.282 +
   7.283 +        final HIDDeviceBLESteamController finalThis = this;
   7.284 +        mHandler.postDelayed(new Runnable() {
   7.285 +            @Override
   7.286 +            public void run() {
   7.287 +                finalThis.checkConnectionForChromebookIssue();
   7.288 +            }
   7.289 +        }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
   7.290 +    }
   7.291 +
   7.292 +    private boolean isRegistered() {
   7.293 +        return mIsRegistered;
   7.294 +    }
   7.295 +
   7.296 +    private void setRegistered() {
   7.297 +        mIsRegistered = true;
   7.298 +    }
   7.299 +
   7.300 +    private boolean probeService(HIDDeviceBLESteamController controller) {
   7.301 +
   7.302 +        if (isRegistered()) {
   7.303 +            return true;
   7.304 +        }
   7.305 +
   7.306 +        if (!mIsConnected) {
   7.307 +            return false;
   7.308 +        }
   7.309 +
   7.310 +        Log.v(TAG, "probeService controller=" + controller);
   7.311 +
   7.312 +        for (BluetoothGattService service : mGatt.getServices()) {
   7.313 +            if (service.getUuid().equals(steamControllerService)) {
   7.314 +                Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
   7.315 +
   7.316 +                for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
   7.317 +                    if (chr.getUuid().equals(inputCharacteristic)) {
   7.318 +                        Log.v(TAG, "Found input characteristic");
   7.319 +                        // Start notifications
   7.320 +                        BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
   7.321 +                        if (cccd != null) {
   7.322 +                            enableNotification(chr.getUuid());
   7.323 +                        }
   7.324 +                    }
   7.325 +                }
   7.326 +                return true;
   7.327 +            }
   7.328 +        }
   7.329 +
   7.330 +        if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
   7.331 +            Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
   7.332 +            mIsConnected = false;
   7.333 +            mIsReconnecting = true;
   7.334 +            mGatt.disconnect();
   7.335 +            mGatt = connectGatt(false);
   7.336 +        }
   7.337 +
   7.338 +        return false;
   7.339 +    }
   7.340 +
   7.341 +    //////////////////////////////////////////////////////////////////////////////////////////////////////
   7.342 +    //////////////////////////////////////////////////////////////////////////////////////////////////////
   7.343 +    //////////////////////////////////////////////////////////////////////////////////////////////////////
   7.344 +
   7.345 +    private void finishCurrentGattOperation() {
   7.346 +        GattOperation op = null;
   7.347 +        synchronized (mOperations) {
   7.348 +            if (mCurrentOperation != null) {
   7.349 +                op = mCurrentOperation;
   7.350 +                mCurrentOperation = null;
   7.351 +            }
   7.352 +        }
   7.353 +        if (op != null) {
   7.354 +            boolean result = op.finish(); // TODO: Maybe in main thread as well?
   7.355 +
   7.356 +            // Our operation failed, let's add it back to the beginning of our queue.
   7.357 +            if (!result) {
   7.358 +                mOperations.addFirst(op);
   7.359 +            }
   7.360 +        }
   7.361 +        executeNextGattOperation();
   7.362 +    }
   7.363 +
   7.364 +    private void executeNextGattOperation() {
   7.365 +        synchronized (mOperations) {
   7.366 +            if (mCurrentOperation != null)
   7.367 +                return;
   7.368 +
   7.369 +            if (mOperations.isEmpty())
   7.370 +                return;
   7.371 +
   7.372 +            mCurrentOperation = mOperations.removeFirst();
   7.373 +        }
   7.374 +
   7.375 +        // Run in main thread
   7.376 +        mHandler.post(new Runnable() {
   7.377 +            @Override
   7.378 +            public void run() {
   7.379 +                synchronized (mOperations) {
   7.380 +                    if (mCurrentOperation == null) {
   7.381 +                        Log.e(TAG, "Current operation null in executor?");
   7.382 +                        return;
   7.383 +                    }
   7.384 +
   7.385 +                    mCurrentOperation.run();
   7.386 +                    // now wait for the GATT callback and when it comes, finish this operation
   7.387 +                }
   7.388 +            }
   7.389 +        });
   7.390 +    }
   7.391 +
   7.392 +    private void queueGattOperation(GattOperation op) {
   7.393 +        synchronized (mOperations) {
   7.394 +            mOperations.add(op);
   7.395 +        }
   7.396 +        executeNextGattOperation();
   7.397 +    }
   7.398 +
   7.399 +    private void enableNotification(UUID chrUuid) {
   7.400 +        GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
   7.401 +        queueGattOperation(op);
   7.402 +    }
   7.403 +
   7.404 +    public void writeCharacteristic(UUID uuid, byte[] value) {
   7.405 +        GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
   7.406 +        queueGattOperation(op);
   7.407 +    }
   7.408 +
   7.409 +    public void readCharacteristic(UUID uuid) {
   7.410 +        GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
   7.411 +        queueGattOperation(op);
   7.412 +    }
   7.413 +
   7.414 +    //////////////////////////////////////////////////////////////////////////////////////////////////////
   7.415 +    //////////////  BluetoothGattCallback overridden methods
   7.416 +    //////////////////////////////////////////////////////////////////////////////////////////////////////
   7.417 +
   7.418 +    public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
   7.419 +        //Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
   7.420 +        mIsReconnecting = false;
   7.421 +        if (newState == 2) {
   7.422 +            mIsConnected = true;
   7.423 +            // Run directly, without GattOperation
   7.424 +            if (!isRegistered()) {
   7.425 +                mHandler.post(new Runnable() {
   7.426 +                    @Override
   7.427 +                    public void run() {
   7.428 +                        mGatt.discoverServices();
   7.429 +                    }
   7.430 +                });
   7.431 +            }
   7.432 +        } 
   7.433 +        else if (newState == 0) {
   7.434 +            mIsConnected = false;
   7.435 +        }
   7.436 +
   7.437 +        // Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
   7.438 +    }
   7.439 +
   7.440 +    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
   7.441 +        //Log.v(TAG, "onServicesDiscovered status=" + status);
   7.442 +        if (status == 0) {
   7.443 +            if (gatt.getServices().size() == 0) {
   7.444 +                Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
   7.445 +                mIsReconnecting = true;
   7.446 +                mIsConnected = false;
   7.447 +                gatt.disconnect();
   7.448 +                mGatt = connectGatt(false);
   7.449 +            }
   7.450 +            else {
   7.451 +                probeService(this);
   7.452 +            }
   7.453 +        }
   7.454 +    }
   7.455 +
   7.456 +    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
   7.457 +        //Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
   7.458 +
   7.459 +        if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
   7.460 +            mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue());
   7.461 +        }
   7.462 +
   7.463 +        finishCurrentGattOperation();
   7.464 +    }
   7.465 +
   7.466 +    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
   7.467 +        //Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
   7.468 +
   7.469 +        if (characteristic.getUuid().equals(reportCharacteristic)) {
   7.470 +            // Only register controller with the native side once it has been fully configured
   7.471 +            if (!isRegistered()) {
   7.472 +                Log.v(TAG, "Registering Steam Controller with ID: " + getId());
   7.473 +                mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0);
   7.474 +                setRegistered();
   7.475 +            }
   7.476 +        }
   7.477 +
   7.478 +        finishCurrentGattOperation();
   7.479 +    }
   7.480 +
   7.481 +    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
   7.482 +    // Enable this for verbose logging of controller input reports
   7.483 +        //Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
   7.484 +
   7.485 +        if (characteristic.getUuid().equals(inputCharacteristic)) {
   7.486 +            mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
   7.487 +        }
   7.488 +    }
   7.489 +
   7.490 +    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
   7.491 +        //Log.v(TAG, "onDescriptorRead status=" + status);
   7.492 +    }
   7.493 +
   7.494 +    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
   7.495 +        BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
   7.496 +        //Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
   7.497 +
   7.498 +        if (chr.getUuid().equals(inputCharacteristic)) {
   7.499 +            boolean hasWrittenInputDescriptor = true;
   7.500 +            BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
   7.501 +            if (reportChr != null) {
   7.502 +                Log.v(TAG, "Writing report characteristic to enter valve mode");
   7.503 +                reportChr.setValue(enterValveMode);
   7.504 +                gatt.writeCharacteristic(reportChr);
   7.505 +            }
   7.506 +        }
   7.507 +
   7.508 +        finishCurrentGattOperation();
   7.509 +    }
   7.510 +
   7.511 +    public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
   7.512 +        //Log.v(TAG, "onReliableWriteCompleted status=" + status);
   7.513 +    }
   7.514 +
   7.515 +    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
   7.516 +        //Log.v(TAG, "onReadRemoteRssi status=" + status);
   7.517 +    }
   7.518 +
   7.519 +    public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
   7.520 +        //Log.v(TAG, "onMtuChanged status=" + status);
   7.521 +    }
   7.522 +
   7.523 +    //////////////////////////////////////////////////////////////////////////////////////////////////////
   7.524 +    //////// Public API
   7.525 +    //////////////////////////////////////////////////////////////////////////////////////////////////////
   7.526 +
   7.527 +    @Override
   7.528 +    public int getId() {
   7.529 +        return mDeviceId;
   7.530 +    }
   7.531 +
   7.532 +    @Override
   7.533 +    public int getVendorId() {
   7.534 +        // Valve Corporation
   7.535 +        final int VALVE_USB_VID = 0x28DE;
   7.536 +        return VALVE_USB_VID;
   7.537 +    }
   7.538 +
   7.539 +    @Override
   7.540 +    public int getProductId() {
   7.541 +        // We don't have an easy way to query from the Bluetooth device, but we know what it is
   7.542 +        final int D0G_BLE2_PID = 0x1106;
   7.543 +        return D0G_BLE2_PID;
   7.544 +    }
   7.545 +
   7.546 +    @Override
   7.547 +    public String getSerialNumber() {
   7.548 +        // This will be read later via feature report by Steam
   7.549 +        return "12345";
   7.550 +    }
   7.551 +
   7.552 +    @Override
   7.553 +    public int getVersion() {
   7.554 +        return 0;
   7.555 +    }
   7.556 +
   7.557 +    @Override
   7.558 +    public String getManufacturerName() {
   7.559 +        return "Valve Corporation";
   7.560 +    }
   7.561 +
   7.562 +    @Override
   7.563 +    public String getProductName() {
   7.564 +        return "Steam Controller";
   7.565 +    }
   7.566 +
   7.567 +    @Override
   7.568 +    public boolean open() {
   7.569 +        return true;
   7.570 +    }
   7.571 +
   7.572 +    @Override
   7.573 +    public int sendFeatureReport(byte[] report) {
   7.574 +        if (!isRegistered()) {
   7.575 +            Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!");
   7.576 +            if (mIsConnected) {
   7.577 +                probeService(this);
   7.578 +            }
   7.579 +            return -1;
   7.580 +        }
   7.581 +
   7.582 +        // We need to skip the first byte, as that doesn't go over the air
   7.583 +	byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
   7.584 +        //Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report));
   7.585 +        writeCharacteristic(reportCharacteristic, actual_report);
   7.586 +        return report.length;
   7.587 +    }
   7.588 +
   7.589 +    @Override
   7.590 +    public int sendOutputReport(byte[] report) {
   7.591 +        if (!isRegistered()) {
   7.592 +            Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!");
   7.593 +            if (mIsConnected) {
   7.594 +                probeService(this);
   7.595 +            }
   7.596 +            return -1;
   7.597 +        }
   7.598 +
   7.599 +        //Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report));
   7.600 +        writeCharacteristic(reportCharacteristic, report);
   7.601 +        return report.length;
   7.602 +    }
   7.603 +
   7.604 +    @Override
   7.605 +    public boolean getFeatureReport(byte[] report) {
   7.606 +        if (!isRegistered()) {
   7.607 +            Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!");
   7.608 +            if (mIsConnected) {
   7.609 +                probeService(this);
   7.610 +            }
   7.611 +            return false;
   7.612 +        }
   7.613 +
   7.614 +        //Log.v(TAG, "getFeatureReport");
   7.615 +        readCharacteristic(reportCharacteristic);
   7.616 +        return true;
   7.617 +    }
   7.618 +
   7.619 +    @Override
   7.620 +    public void close() {
   7.621 +    }
   7.622 +
   7.623 +    @Override
   7.624 +    public void setFrozen(boolean frozen) {
   7.625 +        mFrozen = frozen;
   7.626 +    }
   7.627 +
   7.628 +    @Override
   7.629 +    public void shutdown() {
   7.630 +        BluetoothGatt g = mGatt;
   7.631 +        if (g != null) {
   7.632 +            g.disconnect();
   7.633 +            g.close();
   7.634 +            mGatt = null;
   7.635 +        }
   7.636 +        mManager = null;
   7.637 +        mIsRegistered = false;
   7.638 +        mIsConnected = false;
   7.639 +        mOperations.clear();
   7.640 +    }
   7.641 +
   7.642 +}
   7.643 +
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java	Thu Aug 09 16:00:17 2018 -0700
     8.3 @@ -0,0 +1,614 @@
     8.4 +package org.libsdl.app;
     8.5 +
     8.6 +import android.app.PendingIntent;
     8.7 +import android.bluetooth.BluetoothAdapter;
     8.8 +import android.bluetooth.BluetoothDevice;
     8.9 +import android.bluetooth.BluetoothManager;
    8.10 +import android.bluetooth.BluetoothProfile;
    8.11 +import android.util.Log;
    8.12 +import android.content.BroadcastReceiver;
    8.13 +import android.content.Context;
    8.14 +import android.content.Intent;
    8.15 +import android.content.IntentFilter;
    8.16 +import android.content.SharedPreferences;
    8.17 +import android.hardware.usb.*;
    8.18 +import android.os.Handler;
    8.19 +import android.os.Looper;
    8.20 +
    8.21 +import java.util.HashMap;
    8.22 +import java.util.ArrayList;
    8.23 +import java.util.List;
    8.24 +
    8.25 +public class HIDDeviceManager {
    8.26 +    private static final String TAG = "hidapi";
    8.27 +    private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
    8.28 +
    8.29 +    protected Context mContext;
    8.30 +    private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
    8.31 +    private HashMap<UsbDevice, HIDDeviceUSB> mUSBDevices = new HashMap<UsbDevice, HIDDeviceUSB>();
    8.32 +    private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
    8.33 +    private int mNextDeviceId = 0;
    8.34 +    private SharedPreferences mSharedPreferences = null;
    8.35 +    private boolean mIsChromebook = false;
    8.36 +    private UsbManager mUsbManager;
    8.37 +    private Handler mHandler;
    8.38 +    private BluetoothManager mBluetoothManager;
    8.39 +    private List<BluetoothDevice> mLastBluetoothDevices;
    8.40 +
    8.41 +    private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
    8.42 +        @Override
    8.43 +        public void onReceive(Context context, Intent intent) {
    8.44 +            String action = intent.getAction();
    8.45 +            if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
    8.46 +                UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
    8.47 +                handleUsbDeviceAttached(usbDevice);
    8.48 +            } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
    8.49 +                UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
    8.50 +                handleUsbDeviceDetached(usbDevice);
    8.51 +            } else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
    8.52 +                UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
    8.53 +                handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
    8.54 +            }
    8.55 +        }
    8.56 +    };
    8.57 +
    8.58 +    private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
    8.59 +        @Override
    8.60 +        public void onReceive(Context context, Intent intent) {
    8.61 +            String action = intent.getAction();
    8.62 +            // Bluetooth device was connected. If it was a Steam Controller, handle it
    8.63 +            if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
    8.64 +                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    8.65 +                Log.d(TAG, "Bluetooth device connected: " + device);
    8.66 +
    8.67 +                if (isSteamController(device)) {
    8.68 +                    connectBluetoothDevice(device);
    8.69 +                }
    8.70 +            }
    8.71 +
    8.72 +            // Bluetooth device was disconnected, remove from controller manager (if any)
    8.73 +            if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
    8.74 +                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    8.75 +                Log.d(TAG, "Bluetooth device disconnected: " + device);
    8.76 +
    8.77 +                disconnectBluetoothDevice(device);
    8.78 +            }
    8.79 +        }
    8.80 +    };
    8.81 +
    8.82 +    public HIDDeviceManager(Context context) {
    8.83 +        mContext = context;
    8.84 +
    8.85 +        HIDDeviceRegisterCallback(this);
    8.86 +
    8.87 +        mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
    8.88 +        mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
    8.89 +
    8.90 +//        if (shouldClear) {
    8.91 +//            SharedPreferences.Editor spedit = mSharedPreferences.edit();
    8.92 +//            spedit.clear();
    8.93 +//            spedit.commit();
    8.94 +//        }
    8.95 +//        else
    8.96 +        {
    8.97 +            mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
    8.98 +        }
    8.99 +
   8.100 +        initializeUSB();
   8.101 +        initializeBluetooth();
   8.102 +    }
   8.103 +
   8.104 +    public Context getContext() {
   8.105 +        return mContext;
   8.106 +    }
   8.107 +
   8.108 +    public int getDeviceIDForIdentifier(String identifier) {
   8.109 +        SharedPreferences.Editor spedit = mSharedPreferences.edit();
   8.110 +
   8.111 +        int result = mSharedPreferences.getInt(identifier, 0);
   8.112 +        if (result == 0) {
   8.113 +            result = mNextDeviceId++;
   8.114 +            spedit.putInt("next_device_id", mNextDeviceId);
   8.115 +        }
   8.116 +
   8.117 +        spedit.putInt(identifier, result);
   8.118 +        spedit.commit();
   8.119 +        return result;
   8.120 +    }
   8.121 +
   8.122 +    protected void initializeUSB() {
   8.123 +        mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
   8.124 +
   8.125 +        /*
   8.126 +        // Logging
   8.127 +        for (UsbDevice device : mUsbManager.getDeviceList().values()) {
   8.128 +            Log.i(TAG,"Path: " + device.getDeviceName());
   8.129 +            Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
   8.130 +            Log.i(TAG,"Product: " + device.getProductName());
   8.131 +            Log.i(TAG,"ID: " + device.getDeviceId());
   8.132 +            Log.i(TAG,"Class: " + device.getDeviceClass());
   8.133 +            Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
   8.134 +            Log.i(TAG,"Vendor ID " + device.getVendorId());
   8.135 +            Log.i(TAG,"Product ID: " + device.getProductId());
   8.136 +            Log.i(TAG,"Interface count: " + device.getInterfaceCount());
   8.137 +            Log.i(TAG,"---------------------------------------");
   8.138 +
   8.139 +            // Get interface details
   8.140 +            for (int index = 0; index < device.getInterfaceCount(); index++) {
   8.141 +                UsbInterface mUsbInterface = device.getInterface(index);
   8.142 +                Log.i(TAG,"  *****     *****");
   8.143 +                Log.i(TAG,"  Interface index: " + index);
   8.144 +                Log.i(TAG,"  Interface ID: " + mUsbInterface.getId());
   8.145 +                Log.i(TAG,"  Interface class: " + mUsbInterface.getInterfaceClass());
   8.146 +                Log.i(TAG,"  Interface subclass: " + mUsbInterface.getInterfaceSubclass());
   8.147 +                Log.i(TAG,"  Interface protocol: " + mUsbInterface.getInterfaceProtocol());
   8.148 +                Log.i(TAG,"  Endpoint count: " + mUsbInterface.getEndpointCount());
   8.149 +
   8.150 +                // Get endpoint details 
   8.151 +                for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
   8.152 +                {
   8.153 +                    UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
   8.154 +                    Log.i(TAG,"    ++++   ++++   ++++");
   8.155 +                    Log.i(TAG,"    Endpoint index: " + epi);
   8.156 +                    Log.i(TAG,"    Attributes: " + mEndpoint.getAttributes());
   8.157 +                    Log.i(TAG,"    Direction: " + mEndpoint.getDirection());
   8.158 +                    Log.i(TAG,"    Number: " + mEndpoint.getEndpointNumber());
   8.159 +                    Log.i(TAG,"    Interval: " + mEndpoint.getInterval());
   8.160 +                    Log.i(TAG,"    Packet size: " + mEndpoint.getMaxPacketSize());
   8.161 +                    Log.i(TAG,"    Type: " + mEndpoint.getType());
   8.162 +                }
   8.163 +            }
   8.164 +        }
   8.165 +        Log.i(TAG," No more devices connected.");
   8.166 +        */
   8.167 +
   8.168 +        // Register for USB broadcasts and permission completions
   8.169 +        IntentFilter filter = new IntentFilter();
   8.170 +        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
   8.171 +        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
   8.172 +        filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
   8.173 +        mContext.registerReceiver(mUsbBroadcast, filter);
   8.174 +
   8.175 +        for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
   8.176 +            handleUsbDeviceAttached(usbDevice);
   8.177 +        }
   8.178 +    }
   8.179 +
   8.180 +    UsbManager getUSBManager() {
   8.181 +        return mUsbManager;
   8.182 +    }
   8.183 +
   8.184 +    protected void shutdownUSB() {
   8.185 +        mContext.unregisterReceiver(mUsbBroadcast);
   8.186 +    }
   8.187 +
   8.188 +    protected boolean isHIDDeviceUSB(UsbDevice usbDevice) {
   8.189 +        for (int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); ++interface_number) {
   8.190 +            if (isHIDDeviceInterface(usbDevice, interface_number)) {
   8.191 +                return true;
   8.192 +            }
   8.193 +        }
   8.194 +        return false;
   8.195 +    }
   8.196 +
   8.197 +    protected boolean isHIDDeviceInterface(UsbDevice usbDevice, int interface_number) {
   8.198 +        UsbInterface usbInterface = usbDevice.getInterface(interface_number);
   8.199 +        if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
   8.200 +            return true;
   8.201 +        }
   8.202 +        if (interface_number == 0) {
   8.203 +            if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
   8.204 +                return true;
   8.205 +            }
   8.206 +        }
   8.207 +        return false;
   8.208 +    }
   8.209 +
   8.210 +    protected boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
   8.211 +        final int XB360_IFACE_SUBCLASS = 93;
   8.212 +        final int XB360_IFACE_PROTOCOL = 1; // Wired only
   8.213 +        final int[] SUPPORTED_VENDORS = {
   8.214 +            0x0079, // GPD Win 2
   8.215 +            0x044f, // Thrustmaster
   8.216 +            0x045e, // Microsoft
   8.217 +            0x046d, // Logitech
   8.218 +            0x056e, // Elecom
   8.219 +            0x06a3, // Saitek
   8.220 +            0x0738, // Mad Catz
   8.221 +            0x07ff, // Mad Catz
   8.222 +            0x0e6f, // Unknown
   8.223 +            0x0f0d, // Hori
   8.224 +            0x11c9, // Nacon
   8.225 +            0x12ab, // Unknown
   8.226 +            0x1430, // RedOctane
   8.227 +            0x146b, // BigBen
   8.228 +            0x1532, // Razer Sabertooth
   8.229 +            0x15e4, // Numark
   8.230 +            0x162e, // Joytech
   8.231 +            0x1689, // Razer Onza
   8.232 +            0x1bad, // Harmonix
   8.233 +            0x24c6, // PowerA
   8.234 +        };
   8.235 +
   8.236 +        if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
   8.237 +            usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
   8.238 +            usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL) {
   8.239 +            int vendor_id = usbDevice.getVendorId();
   8.240 +            for (int supportedVid : SUPPORTED_VENDORS) {
   8.241 +                if (vendor_id == supportedVid) {
   8.242 +                    return true;
   8.243 +                }
   8.244 +            }
   8.245 +        }
   8.246 +        return false;
   8.247 +    }
   8.248 +
   8.249 +    protected boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
   8.250 +        final int XB1_IFACE_SUBCLASS = 71;
   8.251 +        final int XB1_IFACE_PROTOCOL = 208;
   8.252 +        final int[] SUPPORTED_VENDORS = {
   8.253 +            0x045e, // Microsoft
   8.254 +            0x0738, // Mad Catz
   8.255 +            0x0e6f, // Unknown
   8.256 +            0x0f0d, // Hori
   8.257 +            0x1532, // Razer Wildcat
   8.258 +            0x24c6, // PowerA
   8.259 +        };
   8.260 +
   8.261 +        if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
   8.262 +            usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
   8.263 +            usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
   8.264 +            int vendor_id = usbDevice.getVendorId();
   8.265 +            for (int supportedVid : SUPPORTED_VENDORS) {
   8.266 +                if (vendor_id == supportedVid) {
   8.267 +                    return true;
   8.268 +                }
   8.269 +            }
   8.270 +        }
   8.271 +        return false;
   8.272 +    }
   8.273 +
   8.274 +    protected void handleUsbDeviceAttached(UsbDevice usbDevice) {
   8.275 +        if (isHIDDeviceUSB(usbDevice)) {
   8.276 +            connectHIDDeviceUSB(usbDevice);
   8.277 +        }
   8.278 +    }
   8.279 +
   8.280 +    protected void handleUsbDeviceDetached(UsbDevice usbDevice) {
   8.281 +        HIDDeviceUSB device = mUSBDevices.get(usbDevice);
   8.282 +        if (device == null)
   8.283 +            return;
   8.284 +
   8.285 +        int id = device.getId();
   8.286 +        mUSBDevices.remove(usbDevice);
   8.287 +        mDevicesById.remove(id);
   8.288 +        device.shutdown();
   8.289 +        HIDDeviceDisconnected(id);
   8.290 +    }
   8.291 +
   8.292 +    protected void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
   8.293 +        HIDDeviceUSB device = mUSBDevices.get(usbDevice);
   8.294 +        if (device == null)
   8.295 +            return;
   8.296 +
   8.297 +        boolean opened = false;
   8.298 +        if (permission_granted) {
   8.299 +            opened = device.open();
   8.300 +        }
   8.301 +        HIDDeviceOpenResult(device.getId(), opened);
   8.302 +    }
   8.303 +
   8.304 +    protected void connectHIDDeviceUSB(UsbDevice usbDevice) {
   8.305 +        synchronized (this) {
   8.306 +            for (int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); interface_number++) {
   8.307 +                if (isHIDDeviceInterface(usbDevice, interface_number)) {
   8.308 +                    HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_number);
   8.309 +                    int id = device.getId();
   8.310 +                    mUSBDevices.put(usbDevice, device);
   8.311 +                    mDevicesById.put(id, device);
   8.312 +                    HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), interface_number);
   8.313 +                    break;
   8.314 +                }
   8.315 +            }
   8.316 +        }
   8.317 +    }
   8.318 +
   8.319 +    protected void initializeBluetooth() {
   8.320 +        Log.d(TAG, "Initializing Bluetooth");
   8.321 +
   8.322 +        // Find bonded bluetooth controllers and create SteamControllers for them
   8.323 +        mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
   8.324 +        if (mBluetoothManager == null) {
   8.325 +            // This device doesn't support Bluetooth.
   8.326 +            return;
   8.327 +        }
   8.328 +
   8.329 +        BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
   8.330 +        if (btAdapter == null) {
   8.331 +            // This device has Bluetooth support in the codebase, but has no available adapters.
   8.332 +            return;
   8.333 +        }
   8.334 +
   8.335 +        // Get our bonded devices.
   8.336 +        for (BluetoothDevice device : btAdapter.getBondedDevices()) {
   8.337 +
   8.338 +            Log.d(TAG, "Bluetooth device available: " + device);
   8.339 +            if (isSteamController(device)) {
   8.340 +                connectBluetoothDevice(device);
   8.341 +            }
   8.342 +
   8.343 +        }
   8.344 +
   8.345 +        // NOTE: These don't work on Chromebooks, to my undying dismay.
   8.346 +        IntentFilter filter = new IntentFilter();
   8.347 +        filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
   8.348 +        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
   8.349 +        mContext.registerReceiver(mBluetoothBroadcast, filter);
   8.350 +
   8.351 +        if (mIsChromebook) {
   8.352 +            mHandler = new Handler(Looper.getMainLooper());
   8.353 +            mLastBluetoothDevices = new ArrayList<>();
   8.354 +
   8.355 +            // final HIDDeviceManager finalThis = this;
   8.356 +            // mHandler.postDelayed(new Runnable() {
   8.357 +            //     @Override
   8.358 +            //     public void run() {
   8.359 +            //         finalThis.chromebookConnectionHandler();
   8.360 +            //     }
   8.361 +            // }, 5000);
   8.362 +        }
   8.363 +    }
   8.364 +
   8.365 +    protected void shutdownBluetooth() {
   8.366 +        mContext.unregisterReceiver(mBluetoothBroadcast);
   8.367 +    }
   8.368 +
   8.369 +    // Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
   8.370 +    // This function provides a sort of dummy version of that, watching for changes in the
   8.371 +    // connected devices and attempting to add controllers as things change.
   8.372 +    public void chromebookConnectionHandler() {
   8.373 +        if (!mIsChromebook) {
   8.374 +            return;
   8.375 +        }
   8.376 +
   8.377 +        ArrayList<BluetoothDevice> disconnected = new ArrayList<>();
   8.378 +        ArrayList<BluetoothDevice> connected = new ArrayList<>();
   8.379 +
   8.380 +        List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
   8.381 +
   8.382 +        for (BluetoothDevice bluetoothDevice : currentConnected) {
   8.383 +            if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
   8.384 +                connected.add(bluetoothDevice);
   8.385 +            }
   8.386 +        }
   8.387 +        for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
   8.388 +            if (!currentConnected.contains(bluetoothDevice)) {
   8.389 +                disconnected.add(bluetoothDevice);
   8.390 +            }
   8.391 +        }
   8.392 +
   8.393 +        mLastBluetoothDevices = currentConnected;
   8.394 +
   8.395 +        for (BluetoothDevice bluetoothDevice : disconnected) {
   8.396 +            disconnectBluetoothDevice(bluetoothDevice);
   8.397 +        }
   8.398 +        for (BluetoothDevice bluetoothDevice : connected) {
   8.399 +            connectBluetoothDevice(bluetoothDevice);
   8.400 +        }
   8.401 +
   8.402 +        final HIDDeviceManager finalThis = this;
   8.403 +        mHandler.postDelayed(new Runnable() {
   8.404 +            @Override
   8.405 +            public void run() {
   8.406 +                finalThis.chromebookConnectionHandler();
   8.407 +            }
   8.408 +        }, 10000);
   8.409 +    }
   8.410 +
   8.411 +    public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
   8.412 +        Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
   8.413 +        synchronized (this) {
   8.414 +            if (mBluetoothDevices.containsKey(bluetoothDevice)) {
   8.415 +                Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
   8.416 +
   8.417 +                HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
   8.418 +                device.reconnect();
   8.419 +
   8.420 +                return false;
   8.421 +            }
   8.422 +            HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
   8.423 +            int id = device.getId();
   8.424 +            mBluetoothDevices.put(bluetoothDevice, device);
   8.425 +            mDevicesById.put(id, device);
   8.426 +
   8.427 +            // The Steam Controller will mark itself connected once initialization is complete
   8.428 +        }
   8.429 +        return true;
   8.430 +    }
   8.431 +
   8.432 +    public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
   8.433 +        synchronized (this) {
   8.434 +            HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
   8.435 +            if (device == null)
   8.436 +                return;
   8.437 +
   8.438 +            int id = device.getId();
   8.439 +            mBluetoothDevices.remove(bluetoothDevice);
   8.440 +            mDevicesById.remove(id);
   8.441 +            device.shutdown();
   8.442 +            HIDDeviceDisconnected(id);
   8.443 +        }
   8.444 +    }
   8.445 +
   8.446 +    public boolean isSteamController(BluetoothDevice bluetoothDevice) {
   8.447 +        // Sanity check.  If you pass in a null device, by definition it is never a Steam Controller.
   8.448 +        if (bluetoothDevice == null) {
   8.449 +            return false;
   8.450 +        }
   8.451 +
   8.452 +        // If the device has no local name, we really don't want to try an equality check against it.
   8.453 +        if (bluetoothDevice.getName() == null) {
   8.454 +            return false;
   8.455 +        }
   8.456 +
   8.457 +        return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
   8.458 +    }
   8.459 +
   8.460 +    public void close() {
   8.461 +        shutdownUSB();
   8.462 +        shutdownBluetooth();
   8.463 +        synchronized (this) {
   8.464 +            for (HIDDevice device : mDevicesById.values()) {
   8.465 +                device.shutdown();
   8.466 +            }
   8.467 +            mDevicesById.clear();
   8.468 +            mBluetoothDevices.clear();
   8.469 +            HIDDeviceReleaseCallback();
   8.470 +        }
   8.471 +    }
   8.472 +
   8.473 +    public void setFrozen(boolean frozen) {
   8.474 +        synchronized (this) {
   8.475 +            for (HIDDevice device : mDevicesById.values()) {
   8.476 +                device.setFrozen(frozen);
   8.477 +            }
   8.478 +        }        
   8.479 +    }
   8.480 +
   8.481 +    //////////////////////////////////////////////////////////////////////////////////////////////////////
   8.482 +    //////////////////////////////////////////////////////////////////////////////////////////////////////
   8.483 +    //////////////////////////////////////////////////////////////////////////////////////////////////////
   8.484 +
   8.485 +    private HIDDevice getDevice(int id) {
   8.486 +        synchronized (this) {
   8.487 +            HIDDevice result = mDevicesById.get(id);
   8.488 +            if (result == null) {
   8.489 +                Log.v(TAG, "No device for id: " + id);
   8.490 +                Log.v(TAG, "Available devices: " + mDevicesById.keySet());
   8.491 +            }
   8.492 +            return result;
   8.493 +        }
   8.494 +    }
   8.495 +
   8.496 +    //////////////////////////////////////////////////////////////////////////////////////////////////////
   8.497 +    ////////// JNI interface functions
   8.498 +    //////////////////////////////////////////////////////////////////////////////////////////////////////
   8.499 +
   8.500 +    boolean openDevice(int deviceID) {
   8.501 +        // Look to see if this is a USB device and we have permission to access it
   8.502 +        for (HIDDeviceUSB device : mUSBDevices.values()) {
   8.503 +            if (deviceID == device.getId()) {
   8.504 +                UsbDevice usbDevice = device.getDevice();
   8.505 +                if (!mUsbManager.hasPermission(usbDevice)) {
   8.506 +                    HIDDeviceOpenPending(deviceID);
   8.507 +                    try {
   8.508 +                        mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), 0));
   8.509 +                    } catch (Exception e) {
   8.510 +                        Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
   8.511 +                        HIDDeviceOpenResult(deviceID, false);
   8.512 +                    }
   8.513 +                    return false;
   8.514 +                }
   8.515 +                break;
   8.516 +            }
   8.517 +        }
   8.518 +
   8.519 +        try {
   8.520 +            Log.v(TAG, "openDevice deviceID=" + deviceID);
   8.521 +            HIDDevice device;
   8.522 +            device = getDevice(deviceID);
   8.523 +            if (device == null) {
   8.524 +                HIDDeviceDisconnected(deviceID);
   8.525 +                return false;
   8.526 +            }
   8.527 +
   8.528 +            return device.open();
   8.529 +        } catch (Exception e) {
   8.530 +            Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
   8.531 +        }
   8.532 +        return false;
   8.533 +    }
   8.534 +
   8.535 +    int sendOutputReport(int deviceID, byte[] report) {
   8.536 +        try {
   8.537 +            Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
   8.538 +            HIDDevice device;
   8.539 +            device = getDevice(deviceID);
   8.540 +            if (device == null) {
   8.541 +                HIDDeviceDisconnected(deviceID);
   8.542 +                return -1;
   8.543 +            }
   8.544 +
   8.545 +            return device.sendOutputReport(report);
   8.546 +        } catch (Exception e) {
   8.547 +            Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
   8.548 +        }
   8.549 +        return -1;
   8.550 +    }
   8.551 +
   8.552 +    int sendFeatureReport(int deviceID, byte[] report) {
   8.553 +        try {
   8.554 +            Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
   8.555 +            HIDDevice device;
   8.556 +            device = getDevice(deviceID);
   8.557 +            if (device == null) {
   8.558 +                HIDDeviceDisconnected(deviceID);
   8.559 +                return -1;
   8.560 +            }
   8.561 +
   8.562 +            return device.sendFeatureReport(report);
   8.563 +        } catch (Exception e) {
   8.564 +            Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
   8.565 +        }
   8.566 +        return -1;
   8.567 +    }
   8.568 +
   8.569 +    boolean getFeatureReport(int deviceID, byte[] report) {
   8.570 +        try {
   8.571 +            Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
   8.572 +            HIDDevice device;
   8.573 +            device = getDevice(deviceID);
   8.574 +            if (device == null) {
   8.575 +                HIDDeviceDisconnected(deviceID);
   8.576 +                return false;
   8.577 +            }
   8.578 +
   8.579 +            return device.getFeatureReport(report);
   8.580 +        } catch (Exception e) {
   8.581 +            Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
   8.582 +        }
   8.583 +        return false;
   8.584 +    }
   8.585 +
   8.586 +    void closeDevice(int deviceID) {
   8.587 +        try {
   8.588 +            Log.v(TAG, "closeDevice deviceID=" + deviceID);
   8.589 +            HIDDevice device;
   8.590 +            device = getDevice(deviceID);
   8.591 +            if (device == null) {
   8.592 +                HIDDeviceDisconnected(deviceID);
   8.593 +                return;
   8.594 +            }
   8.595 +
   8.596 +            device.close();
   8.597 +        } catch (Exception e) {
   8.598 +            Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
   8.599 +        }
   8.600 +    }
   8.601 +
   8.602 +
   8.603 +    //////////////////////////////////////////////////////////////////////////////////////////////////////
   8.604 +    /////////////// Native methods
   8.605 +    //////////////////////////////////////////////////////////////////////////////////////////////////////
   8.606 +
   8.607 +    private native void HIDDeviceRegisterCallback(Object callbackHandler);
   8.608 +    private native void HIDDeviceReleaseCallback();
   8.609 +
   8.610 +    native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number);
   8.611 +    native void HIDDeviceOpenPending(int deviceID);
   8.612 +    native void HIDDeviceOpenResult(int deviceID, boolean opened);
   8.613 +    native void HIDDeviceDisconnected(int deviceID);
   8.614 +
   8.615 +    native void HIDDeviceInputReport(int deviceID, byte[] report);
   8.616 +    native void HIDDeviceFeatureReport(int deviceID, byte[] report);
   8.617 +}
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java	Thu Aug 09 16:00:17 2018 -0700
     9.3 @@ -0,0 +1,298 @@
     9.4 +package org.libsdl.app;
     9.5 +
     9.6 +import android.hardware.usb.*;
     9.7 +import android.os.Build;
     9.8 +import android.util.Log;
     9.9 +import java.util.Arrays;
    9.10 +
    9.11 +class HIDDeviceUSB implements HIDDevice {
    9.12 +
    9.13 +    private static final String TAG = "hidapi";
    9.14 +
    9.15 +    protected HIDDeviceManager mManager;
    9.16 +    protected UsbDevice mDevice;
    9.17 +    protected int mInterface;
    9.18 +    protected int mDeviceId;
    9.19 +    protected UsbDeviceConnection mConnection;
    9.20 +    protected UsbEndpoint mInputEndpoint;
    9.21 +    protected UsbEndpoint mOutputEndpoint;
    9.22 +    protected InputThread mInputThread;
    9.23 +    protected boolean mRunning;
    9.24 +    protected boolean mFrozen;
    9.25 +
    9.26 +    public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_number) {
    9.27 +        mManager = manager;
    9.28 +        mDevice = usbDevice;
    9.29 +        mInterface = interface_number;
    9.30 +        mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
    9.31 +        mRunning = false;
    9.32 +    }
    9.33 +
    9.34 +    public String getIdentifier() {
    9.35 +        return String.format("%s/%x/%x", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId());
    9.36 +    }
    9.37 +
    9.38 +    @Override
    9.39 +    public int getId() {
    9.40 +        return mDeviceId;
    9.41 +    }
    9.42 +
    9.43 +    @Override
    9.44 +    public int getVendorId() {
    9.45 +        return mDevice.getVendorId();
    9.46 +    }
    9.47 +
    9.48 +    @Override
    9.49 +    public int getProductId() {
    9.50 +        return mDevice.getProductId();
    9.51 +    }
    9.52 +
    9.53 +    @Override
    9.54 +    public String getSerialNumber() {
    9.55 +        String result = null;
    9.56 +        if (Build.VERSION.SDK_INT >= 21) {
    9.57 +            result = mDevice.getSerialNumber();
    9.58 +        }
    9.59 +        if (result == null) {
    9.60 +            result = "";
    9.61 +        }
    9.62 +        return result;
    9.63 +    }
    9.64 +
    9.65 +    @Override
    9.66 +    public int getVersion() {
    9.67 +        return 0;
    9.68 +    }
    9.69 +
    9.70 +    @Override
    9.71 +    public String getManufacturerName() {
    9.72 +        String result = null;
    9.73 +        if (Build.VERSION.SDK_INT >= 21) {
    9.74 +            result = mDevice.getManufacturerName();
    9.75 +        }
    9.76 +        if (result == null) {
    9.77 +            result = String.format("%x", getVendorId());
    9.78 +        }
    9.79 +        return result;
    9.80 +    }
    9.81 +
    9.82 +    @Override
    9.83 +    public String getProductName() {
    9.84 +        String result = null;
    9.85 +        if (Build.VERSION.SDK_INT >= 21) {
    9.86 +            result = mDevice.getProductName();
    9.87 +        }
    9.88 +        if (result == null) {
    9.89 +            result = String.format("%x", getProductId());
    9.90 +        }
    9.91 +        return result;
    9.92 +    }
    9.93 +
    9.94 +    public UsbDevice getDevice() {
    9.95 +        return mDevice;
    9.96 +    }
    9.97 +
    9.98 +    public String getDeviceName() {
    9.99 +        return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
   9.100 +    }
   9.101 +
   9.102 +    @Override
   9.103 +    public boolean open() {
   9.104 +        mConnection = mManager.getUSBManager().openDevice(mDevice);
   9.105 +        if (mConnection == null) {
   9.106 +            Log.w(TAG, "Unable to open USB device " + getDeviceName());
   9.107 +            return false;
   9.108 +        }
   9.109 +
   9.110 +        // Force claim all interfaces
   9.111 +        for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
   9.112 +            UsbInterface iface = mDevice.getInterface(i);
   9.113 +
   9.114 +            if (!mConnection.claimInterface(iface, true)) {
   9.115 +                Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
   9.116 +                close();
   9.117 +                return false;
   9.118 +            }
   9.119 +        }
   9.120 +
   9.121 +        // Find the endpoints
   9.122 +        UsbInterface iface = mDevice.getInterface(mInterface);
   9.123 +        for (int j = 0; j < iface.getEndpointCount(); j++) {
   9.124 +            UsbEndpoint endpt = iface.getEndpoint(j);
   9.125 +            switch (endpt.getDirection()) {
   9.126 +            case UsbConstants.USB_DIR_IN:
   9.127 +                if (mInputEndpoint == null) {
   9.128 +                    mInputEndpoint = endpt;
   9.129 +                }
   9.130 +                break;
   9.131 +            case UsbConstants.USB_DIR_OUT:
   9.132 +                if (mOutputEndpoint == null) {
   9.133 +                    mOutputEndpoint = endpt;
   9.134 +                }
   9.135 +                break;
   9.136 +            }
   9.137 +        }
   9.138 +
   9.139 +        // Make sure the required endpoints were present
   9.140 +        if (mInputEndpoint == null || mOutputEndpoint == null) {
   9.141 +            Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
   9.142 +            close();
   9.143 +            return false;
   9.144 +        }
   9.145 +
   9.146 +        // Start listening for input
   9.147 +        mRunning = true;
   9.148 +        mInputThread = new InputThread();
   9.149 +        mInputThread.start();
   9.150 +
   9.151 +        return true;
   9.152 +    }
   9.153 +
   9.154 +    @Override
   9.155 +    public int sendFeatureReport(byte[] report) {
   9.156 +        int res = -1;
   9.157 +        int offset = 0;
   9.158 +        int length = report.length;
   9.159 +        boolean skipped_report_id = false;
   9.160 +        byte report_number = report[0];
   9.161 +
   9.162 +        if (report_number == 0x0) {
   9.163 +            ++offset;
   9.164 +            --length;
   9.165 +            skipped_report_id = true;
   9.166 +        }
   9.167 +
   9.168 +        res = mConnection.controlTransfer(
   9.169 +            UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
   9.170 +            0x09/*HID set_report*/,
   9.171 +            (3/*HID feature*/ << 8) | report_number,
   9.172 +            0,
   9.173 +            report, offset, length,
   9.174 +            1000/*timeout millis*/);
   9.175 +
   9.176 +        if (res < 0) {
   9.177 +            Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
   9.178 +            return -1;
   9.179 +        }
   9.180 +
   9.181 +        if (skipped_report_id) {
   9.182 +            ++length;
   9.183 +        }
   9.184 +        return length;
   9.185 +    }
   9.186 +
   9.187 +    @Override
   9.188 +    public int sendOutputReport(byte[] report) {
   9.189 +        int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
   9.190 +        if (r != report.length) {
   9.191 +            Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
   9.192 +        }
   9.193 +        return r;
   9.194 +    }
   9.195 +
   9.196 +    @Override
   9.197 +    public boolean getFeatureReport(byte[] report) {
   9.198 +        int res = -1;
   9.199 +        int offset = 0;
   9.200 +        int length = report.length;
   9.201 +        boolean skipped_report_id = false;
   9.202 +        byte report_number = report[0];
   9.203 +
   9.204 +        if (report_number == 0x0) {
   9.205 +            /* Offset the return buffer by 1, so that the report ID
   9.206 +               will remain in byte 0. */
   9.207 +            ++offset;
   9.208 +            --length;
   9.209 +            skipped_report_id = true;
   9.210 +        }
   9.211 +
   9.212 +        res = mConnection.controlTransfer(
   9.213 +            UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
   9.214 +            0x01/*HID get_report*/,
   9.215 +            (3/*HID feature*/ << 8) | report_number,
   9.216 +            0,
   9.217 +            report, offset, length,
   9.218 +            1000/*timeout millis*/);
   9.219 +
   9.220 +        if (res < 0) {
   9.221 +            Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
   9.222 +            return false;
   9.223 +        }
   9.224 +
   9.225 +        if (skipped_report_id) {
   9.226 +            ++res;
   9.227 +            ++length;
   9.228 +        }
   9.229 +
   9.230 +        byte[] data;
   9.231 +        if (res == length) {
   9.232 +            data = report;
   9.233 +        } else {
   9.234 +            data = Arrays.copyOfRange(report, 0, res);
   9.235 +        }
   9.236 +        mManager.HIDDeviceFeatureReport(mDeviceId, data);
   9.237 +
   9.238 +        return true;
   9.239 +    }
   9.240 +
   9.241 +    @Override
   9.242 +    public void close() {
   9.243 +        mRunning = false;
   9.244 +        if (mInputThread != null) {
   9.245 +            while (mInputThread.isAlive()) {
   9.246 +                mInputThread.interrupt();
   9.247 +                try {
   9.248 +                    mInputThread.join();
   9.249 +                } catch (InterruptedException e) {
   9.250 +                    // Keep trying until we're done
   9.251 +                }
   9.252 +            }
   9.253 +            mInputThread = null;
   9.254 +        }
   9.255 +        if (mConnection != null) {
   9.256 +            for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
   9.257 +                UsbInterface iface = mDevice.getInterface(i);
   9.258 +                mConnection.releaseInterface(iface);
   9.259 +            }
   9.260 +            mConnection.close();
   9.261 +            mConnection = null;
   9.262 +        }
   9.263 +    }
   9.264 +
   9.265 +    @Override
   9.266 +    public void shutdown() {
   9.267 +        close();
   9.268 +        mManager = null;
   9.269 +    }
   9.270 +
   9.271 +    @Override
   9.272 +    public void setFrozen(boolean frozen) {
   9.273 +        mFrozen = frozen;
   9.274 +    }
   9.275 +
   9.276 +    protected class InputThread extends Thread {
   9.277 +        @Override
   9.278 +        public void run() {
   9.279 +            int packetSize = mInputEndpoint.getMaxPacketSize();
   9.280 +            byte[] packet = new byte[packetSize];
   9.281 +            while (mRunning) {
   9.282 +                int r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
   9.283 +                if (r < 0) {
   9.284 +                    // Could be a timeout or an I/O error
   9.285 +                }
   9.286 +                if (r > 0) {
   9.287 +                    byte[] data;
   9.288 +                    if (r == packetSize) {
   9.289 +                        data = packet;
   9.290 +                    } else {
   9.291 +                        data = Arrays.copyOfRange(packet, 0, r);
   9.292 +                    }
   9.293 +
   9.294 +                    if (!mFrozen) {
   9.295 +                        mManager.HIDDeviceInputReport(mDeviceId, data);
   9.296 +                    }
   9.297 +                }
   9.298 +            }
   9.299 +        }
   9.300 +    }
   9.301 +}
    10.1 --- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java	Wed Aug 08 15:39:20 2018 -0400
    10.2 +++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java	Thu Aug 09 16:00:17 2018 -0700
    10.3 @@ -80,7 +80,7 @@
    10.4      protected static Hashtable<Integer, Object> mCursors;
    10.5      protected static int mLastCursorID;
    10.6      protected static SDLGenericMotionListener_API12 mMotionListener;
    10.7 -
    10.8 +    protected static HIDDeviceManager mHIDDeviceManager;
    10.9  
   10.10      // This is what SDL runs in. It invokes SDL_main(), eventually
   10.11      protected static Thread mSDLThread;
   10.12 @@ -241,6 +241,8 @@
   10.13              mClipboardHandler = new SDLClipboardHandler_Old();
   10.14          }
   10.15  
   10.16 +        mHIDDeviceManager = new HIDDeviceManager(this);
   10.17 +
   10.18          // Set up the surface
   10.19          mSurface = new SDLSurface(getApplication());
   10.20  
   10.21 @@ -276,6 +278,10 @@
   10.22             return;
   10.23          }
   10.24  
   10.25 +        if (mHIDDeviceManager != null) {
   10.26 +            mHIDDeviceManager.setFrozen(true);
   10.27 +        }
   10.28 +
   10.29          SDLActivity.handleNativeState();
   10.30      }
   10.31  
   10.32 @@ -290,6 +296,10 @@
   10.33             return;
   10.34          }
   10.35  
   10.36 +        if (mHIDDeviceManager != null) {
   10.37 +            mHIDDeviceManager.setFrozen(false);
   10.38 +        }
   10.39 +
   10.40          SDLActivity.handleNativeState();
   10.41      }
   10.42  
   10.43 @@ -330,6 +340,11 @@
   10.44      protected void onDestroy() {
   10.45          Log.v(TAG, "onDestroy()");
   10.46  
   10.47 +        if (mHIDDeviceManager != null) {
   10.48 +            mHIDDeviceManager.close();
   10.49 +            mHIDDeviceManager = null;
   10.50 +        }
   10.51 +
   10.52          if (SDLActivity.mBrokenLibraries) {
   10.53             super.onDestroy();
   10.54             // Reset everything in case the user re opens the app
   10.55 @@ -466,10 +481,7 @@
   10.56      /* The native thread has finished */
   10.57      public static void handleNativeExit() {
   10.58          SDLActivity.mSDLThread = null;
   10.59 -
   10.60 -        // Make sure we currently have a singleton before we try to call it.
   10.61 -        if (mSingleton != null)
   10.62 -            mSingleton.finish();
   10.63 +        mSingleton.finish();
   10.64      }
   10.65  
   10.66  
    11.1 --- a/configure	Wed Aug 08 15:39:20 2018 -0400
    11.2 +++ b/configure	Thu Aug 09 16:00:17 2018 -0700
    11.3 @@ -868,6 +868,7 @@
    11.4  enable_directx
    11.5  enable_wasapi
    11.6  enable_sdl_dlopen
    11.7 +enable_hidapi
    11.8  enable_clock_gettime
    11.9  enable_rpath
   11.10  enable_render_d3d
   11.11 @@ -1623,6 +1624,8 @@
   11.12    --enable-directx        use DirectX for Windows audio/video [[default=yes]]
   11.13    --enable-wasapi         use the Windows WASAPI audio driver [[default=yes]]
   11.14    --enable-sdl-dlopen     use dlopen for shared object loading [[default=yes]]
   11.15 +  --enable-hidapi         use HIDAPI for low level joystick drivers
   11.16 +                          [[default=no]]
   11.17    --enable-clock_gettime  use clock_gettime() instead of gettimeofday() on
   11.18                            UNIX [[default=yes]]
   11.19    --enable-rpath          use an rpath when linking SDL [[default=yes]]
   11.20 @@ -16773,7 +16776,7 @@
   11.21  fi
   11.22  
   11.23  
   11.24 -	    for ac_header in libunwind.h
   11.25 +        for ac_header in libunwind.h
   11.26  do :
   11.27    ac_fn_c_check_header_mongrel "$LINENO" "libunwind.h" "ac_cv_header_libunwind_h" "$ac_includes_default"
   11.28  if test "x$ac_cv_header_libunwind_h" = xyes; then :
   11.29 @@ -18377,7 +18380,7 @@
   11.30          { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PulseAudio $PULSEAUDIO_REQUIRED_VERSION support" >&5
   11.31  $as_echo_n "checking for PulseAudio $PULSEAUDIO_REQUIRED_VERSION support... " >&6; }
   11.32          if test x$PKG_CONFIG != xno; then
   11.33 -        if $PKG_CONFIG --atleast-pkgconfig-version 0.7 && $PKG_CONFIG --atleast-version $PULSEAUDIO_REQUIRED_VERSION libpulse-simple; then
   11.34 +            if $PKG_CONFIG --atleast-pkgconfig-version 0.7 && $PKG_CONFIG --atleast-version $PULSEAUDIO_REQUIRED_VERSION libpulse-simple; then
   11.35                  PULSEAUDIO_CFLAGS=`$PKG_CONFIG --cflags libpulse-simple`
   11.36                  PULSEAUDIO_LIBS=`$PKG_CONFIG --libs libpulse-simple`
   11.37                  audio_pulseaudio=yes
   11.38 @@ -20463,7 +20466,7 @@
   11.39              cat confdefs.h - <<_ACEOF >conftest.$ac_ext
   11.40  /* end confdefs.h.  */
   11.41  
   11.42 -              	#include <X11/Xlib.h>
   11.43 +                #include <X11/Xlib.h>
   11.44  
   11.45  int
   11.46  main ()
   11.47 @@ -20807,13 +20810,13 @@
   11.48  
   11.49                  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for xinput2 multitouch" >&5
   11.50  $as_echo_n "checking for xinput2 multitouch... " >&6; }
   11.51 -            	have_xinput2_multitouch=no
   11.52 -            	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
   11.53 -/* end confdefs.h.  */
   11.54 -
   11.55 -              		#include <X11/Xlib.h>
   11.56 -             		#include <X11/Xproto.h>
   11.57 -			#include <X11/extensions/XInput2.h>
   11.58 +                have_xinput2_multitouch=no
   11.59 +                cat confdefs.h - <<_ACEOF >conftest.$ac_ext
   11.60 +/* end confdefs.h.  */
   11.61 +
   11.62 +                    #include <X11/Xlib.h>
   11.63 +                    #include <X11/Xproto.h>
   11.64 +                    #include <X11/extensions/XInput2.h>
   11.65  
   11.66  int
   11.67  main ()
   11.68 @@ -20828,14 +20831,14 @@
   11.69  _ACEOF
   11.70  if ac_fn_c_try_compile "$LINENO"; then :
   11.71  
   11.72 -            	have_xinput2_multitouch=yes
   11.73 -            	$as_echo "#define SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH 1" >>confdefs.h
   11.74 -
   11.75 -                SUMMARY_video_x11="${SUMMARY_video_x11} xinput2_multitouch"
   11.76 -
   11.77 -fi
   11.78 -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   11.79 -            	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_xinput2_multitouch" >&5
   11.80 +                    have_xinput2_multitouch=yes
   11.81 +                    $as_echo "#define SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH 1" >>confdefs.h
   11.82 +
   11.83 +                    SUMMARY_video_x11="${SUMMARY_video_x11} xinput2_multitouch"
   11.84 +
   11.85 +fi
   11.86 +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   11.87 +                { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_xinput2_multitouch" >&5
   11.88  $as_echo "$have_xinput2_multitouch" >&6; }
   11.89              fi
   11.90              # Check whether --enable-video-x11-xrandr was given.
   11.91 @@ -23728,6 +23731,93 @@
   11.92      esac
   11.93  }
   11.94  
   11.95 +CheckHIDAPI()
   11.96 +{
   11.97 +    # The hidraw support doesn't catch Xbox, PS4 and Nintendo controllers,
   11.98 +    # so we'll just use libusb when it's available.
   11.99 +    #
  11.100 +    # Except that libusb requires root permissions to open devices, so that's not generally useful, and we'll disable this by default.
  11.101 +    # Check whether --enable-hidapi was given.
  11.102 +if test "${enable_hidapi+set}" = set; then :
  11.103 +  enableval=$enable_hidapi;
  11.104 +else
  11.105 +  enable_hidapi=no
  11.106 +fi
  11.107 +
  11.108 +    if test x$enable_joystick = xyes -a x$enable_hidapi = xyes; then
  11.109 +        hidapi_support=no
  11.110 +        # Extract the first word of "pkg-config", so it can be a program name with args.
  11.111 +set dummy pkg-config; ac_word=$2
  11.112 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
  11.113 +$as_echo_n "checking for $ac_word... " >&6; }
  11.114 +if ${ac_cv_path_PKG_CONFIG+:} false; then :
  11.115 +  $as_echo_n "(cached) " >&6
  11.116 +else
  11.117 +  case $PKG_CONFIG in
  11.118 +  [\\/]* | ?:[\\/]*)
  11.119 +  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
  11.120 +  ;;
  11.121 +  *)
  11.122 +  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
  11.123 +for as_dir in $PATH
  11.124 +do
  11.125 +  IFS=$as_save_IFS
  11.126 +  test -z "$as_dir" && as_dir=.
  11.127 +    for ac_exec_ext in '' $ac_executable_extensions; do
  11.128 +  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
  11.129 +    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
  11.130 +    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
  11.131 +    break 2
  11.132 +  fi
  11.133 +done
  11.134 +  done
  11.135 +IFS=$as_save_IFS
  11.136 +
  11.137 +  test -z "$ac_cv_path_PKG_CONFIG" && ac_cv_path_PKG_CONFIG="no"
  11.138 +  ;;
  11.139 +esac
  11.140 +fi
  11.141 +PKG_CONFIG=$ac_cv_path_PKG_CONFIG
  11.142 +if test -n "$PKG_CONFIG"; then
  11.143 +  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
  11.144 +$as_echo "$PKG_CONFIG" >&6; }
  11.145 +else
  11.146 +  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
  11.147 +$as_echo "no" >&6; }
  11.148 +fi
  11.149 +
  11.150 +
  11.151 +        if test x$PKG_CONFIG != xno; then
  11.152 +            LIBUSB_CFLAGS=`$PKG_CONFIG --cflags libusb-1.0`
  11.153 +            LIBUSB_LDFLAGS=`$PKG_CONFIG --libs libusb-1.0`
  11.154 +            save_CFLAGS="$CFLAGS"
  11.155 +            CFLAGS="$save_CFLAGS $LIBUSB_CFLAGS"
  11.156 +            ac_fn_c_check_header_mongrel "$LINENO" "libusb.h" "ac_cv_header_libusb_h" "$ac_includes_default"
  11.157 +if test "x$ac_cv_header_libusb_h" = xyes; then :
  11.158 +  have_libusb_h=yes
  11.159 +fi
  11.160 +
  11.161 +
  11.162 +            CFLAGS="$save_CFLAGS"
  11.163 +        fi
  11.164 +        if test x$have_libusb_h = xyes; then
  11.165 +            hidapi_support=yes
  11.166 +
  11.167 +$as_echo "#define SDL_JOYSTICK_HIDAPI 1" >>confdefs.h
  11.168 +
  11.169 +            EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
  11.170 +            SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
  11.171 +            SOURCES="$SOURCES $srcdir/src/hidapi/libusb/hid.c"
  11.172 +            EXTRA_CFLAGS="$EXTRA_CFLAGS $LIBUSB_CFLAGS"
  11.173 +            EXTRA_LDFLAGS="$EXTRA_LDFLAGS $LIBUSB_LDFLAGS"
  11.174 +        fi
  11.175 +        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for hidapi support" >&5
  11.176 +$as_echo_n "checking for hidapi support... " >&6; }
  11.177 +        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hidapi_support" >&5
  11.178 +$as_echo "$hidapi_support" >&6; }
  11.179 +    fi
  11.180 +}
  11.181 +
  11.182  CheckClockGettime()
  11.183  {
  11.184      # Check whether --enable-clock_gettime was given.
  11.185 @@ -23939,6 +24029,7 @@
  11.186          esac
  11.187          CheckTslib
  11.188          CheckUSBHID
  11.189 +        CheckHIDAPI
  11.190          CheckPTHREAD
  11.191          CheckClockGettime
  11.192          CheckLinuxVersion
  11.193 @@ -24514,7 +24605,13 @@
  11.194  
  11.195  $as_echo "#define SDL_JOYSTICK_IOKIT 1" >>confdefs.h
  11.196  
  11.197 +
  11.198 +$as_echo "#define SDL_JOYSTICK_HIDAPI 1" >>confdefs.h
  11.199 +
  11.200              SOURCES="$SOURCES $srcdir/src/joystick/darwin/*.c"
  11.201 +            SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
  11.202 +            SOURCES="$SOURCES $srcdir/src/hidapi/mac/hid.c"
  11.203 +            EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
  11.204              have_joystick=yes
  11.205          fi
  11.206          # Set up files for the haptic library
    12.1 --- a/configure.in	Wed Aug 08 15:39:20 2018 -0400
    12.2 +++ b/configure.in	Thu Aug 09 16:00:17 2018 -0700
    12.3 @@ -285,7 +285,7 @@
    12.4  
    12.5      AC_CHECK_MEMBER(struct sigaction.sa_sigaction,[AC_DEFINE([HAVE_SA_SIGACTION], 1, [ ])], ,[#include <signal.h>])
    12.6  
    12.7 -	dnl Check for additional non-standard headers
    12.8 +    dnl Check for additional non-standard headers
    12.9      AC_CHECK_HEADERS(libunwind.h)
   12.10  fi
   12.11  
   12.12 @@ -963,7 +963,7 @@
   12.13          AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
   12.14          AC_MSG_CHECKING(for PulseAudio $PULSEAUDIO_REQUIRED_VERSION support)
   12.15          if test x$PKG_CONFIG != xno; then
   12.16 -        if $PKG_CONFIG --atleast-pkgconfig-version 0.7 && $PKG_CONFIG --atleast-version $PULSEAUDIO_REQUIRED_VERSION libpulse-simple; then
   12.17 +            if $PKG_CONFIG --atleast-pkgconfig-version 0.7 && $PKG_CONFIG --atleast-version $PULSEAUDIO_REQUIRED_VERSION libpulse-simple; then
   12.18                  PULSEAUDIO_CFLAGS=`$PKG_CONFIG --cflags libpulse-simple`
   12.19                  PULSEAUDIO_LIBS=`$PKG_CONFIG --libs libpulse-simple`
   12.20                  audio_pulseaudio=yes
   12.21 @@ -1748,7 +1748,7 @@
   12.22              AC_MSG_CHECKING([for XGenericEvent])
   12.23              have_XGenericEvent=no
   12.24              AC_TRY_COMPILE([
   12.25 -              	#include <X11/Xlib.h>
   12.26 +                #include <X11/Xlib.h>
   12.27              ],[
   12.28  Display *display;
   12.29  XEvent event;
   12.30 @@ -1862,20 +1862,20 @@
   12.31                  SUMMARY_video_x11="${SUMMARY_video_x11} xinput2"
   12.32                  AC_DEFINE(SDL_VIDEO_DRIVER_X11_XINPUT2, 1, [ ])
   12.33                  AC_MSG_CHECKING(for xinput2 multitouch)
   12.34 -            	have_xinput2_multitouch=no
   12.35 -            	AC_TRY_COMPILE([
   12.36 -              		#include <X11/Xlib.h>
   12.37 -             		#include <X11/Xproto.h>
   12.38 -			#include <X11/extensions/XInput2.h>
   12.39 -            	],[
   12.40 +                have_xinput2_multitouch=no
   12.41 +                AC_TRY_COMPILE([
   12.42 +                    #include <X11/Xlib.h>
   12.43 +                    #include <X11/Xproto.h>
   12.44 +                    #include <X11/extensions/XInput2.h>
   12.45 +                    ],[
   12.46  int event_type = XI_TouchBegin;
   12.47  XITouchClassInfo *t;
   12.48 -            	],[
   12.49 -            	have_xinput2_multitouch=yes
   12.50 -            	AC_DEFINE([SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH], 1, [])
   12.51 -                SUMMARY_video_x11="${SUMMARY_video_x11} xinput2_multitouch"
   12.52 -            	])
   12.53 -            	AC_MSG_RESULT($have_xinput2_multitouch)
   12.54 +                    ],[
   12.55 +                    have_xinput2_multitouch=yes
   12.56 +                    AC_DEFINE([SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH], 1, [])
   12.57 +                    SUMMARY_video_x11="${SUMMARY_video_x11} xinput2_multitouch"
   12.58 +                    ])
   12.59 +                AC_MSG_RESULT($have_xinput2_multitouch)
   12.60              fi
   12.61              AC_ARG_ENABLE(video-x11-xrandr,
   12.62  AC_HELP_STRING([--enable-video-x11-xrandr], [enable X11 Xrandr extension for fullscreen [[default=yes]]]),
   12.63 @@ -3265,6 +3265,41 @@
   12.64      esac
   12.65  }
   12.66  
   12.67 +dnl Check for HIDAPI joystick drivers
   12.68 +CheckHIDAPI()
   12.69 +{
   12.70 +    # The hidraw support doesn't catch Xbox, PS4 and Nintendo controllers,
   12.71 +    # so we'll just use libusb when it's available.
   12.72 +    #
   12.73 +    # Except that libusb requires root permissions to open devices, so that's not generally useful, and we'll disable this by default.
   12.74 +    AC_ARG_ENABLE(hidapi,
   12.75 +AC_HELP_STRING([--enable-hidapi], [use HIDAPI for low level joystick drivers [[default=no]]]),
   12.76 +                  , enable_hidapi=no)
   12.77 +    if test x$enable_joystick = xyes -a x$enable_hidapi = xyes; then
   12.78 +        hidapi_support=no
   12.79 +        AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
   12.80 +        if test x$PKG_CONFIG != xno; then
   12.81 +            LIBUSB_CFLAGS=`$PKG_CONFIG --cflags libusb-1.0`
   12.82 +            LIBUSB_LDFLAGS=`$PKG_CONFIG --libs libusb-1.0`
   12.83 +            save_CFLAGS="$CFLAGS"
   12.84 +            CFLAGS="$save_CFLAGS $LIBUSB_CFLAGS"
   12.85 +            AC_CHECK_HEADER(libusb.h, have_libusb_h=yes)
   12.86 +            CFLAGS="$save_CFLAGS"
   12.87 +        fi
   12.88 +        if test x$have_libusb_h = xyes; then
   12.89 +            hidapi_support=yes
   12.90 +            AC_DEFINE(SDL_JOYSTICK_HIDAPI, 1, [ ])
   12.91 +            EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
   12.92 +            SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
   12.93 +            SOURCES="$SOURCES $srcdir/src/hidapi/libusb/hid.c"
   12.94 +            EXTRA_CFLAGS="$EXTRA_CFLAGS $LIBUSB_CFLAGS"
   12.95 +            EXTRA_LDFLAGS="$EXTRA_LDFLAGS $LIBUSB_LDFLAGS"
   12.96 +        fi
   12.97 +        AC_MSG_CHECKING(for hidapi support)
   12.98 +        AC_MSG_RESULT($hidapi_support)
   12.99 +    fi
  12.100 +}
  12.101 +
  12.102  dnl Check for clock_gettime()
  12.103  CheckClockGettime()
  12.104  {
  12.105 @@ -3386,6 +3421,7 @@
  12.106          esac
  12.107          CheckTslib
  12.108          CheckUSBHID
  12.109 +        CheckHIDAPI
  12.110          CheckPTHREAD
  12.111          CheckClockGettime
  12.112          CheckLinuxVersion
  12.113 @@ -3811,7 +3847,11 @@
  12.114          # Set up files for the joystick library
  12.115          if test x$enable_joystick = xyes; then
  12.116              AC_DEFINE(SDL_JOYSTICK_IOKIT, 1, [ ])
  12.117 +            AC_DEFINE(SDL_JOYSTICK_HIDAPI, 1, [ ])
  12.118              SOURCES="$SOURCES $srcdir/src/joystick/darwin/*.c"
  12.119 +            SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
  12.120 +            SOURCES="$SOURCES $srcdir/src/hidapi/mac/hid.c"
  12.121 +            EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
  12.122              have_joystick=yes
  12.123          fi
  12.124          # Set up files for the haptic library
    13.1 --- a/include/SDL_config.h.in	Wed Aug 08 15:39:20 2018 -0400
    13.2 +++ b/include/SDL_config.h.in	Thu Aug 09 16:00:17 2018 -0700
    13.3 @@ -279,6 +279,7 @@
    13.4  #undef SDL_JOYSTICK_WINMM
    13.5  #undef SDL_JOYSTICK_USBHID
    13.6  #undef SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H
    13.7 +#undef SDL_JOYSTICK_HIDAPI
    13.8  #undef SDL_JOYSTICK_EMSCRIPTEN
    13.9  #undef SDL_HAPTIC_DUMMY
   13.10  #undef SDL_HAPTIC_ANDROID
    14.1 --- a/include/SDL_config_android.h	Wed Aug 08 15:39:20 2018 -0400
    14.2 +++ b/include/SDL_config_android.h	Thu Aug 09 16:00:17 2018 -0700
    14.3 @@ -134,6 +134,7 @@
    14.4  
    14.5  /* Enable various input drivers */
    14.6  #define SDL_JOYSTICK_ANDROID    1
    14.7 +#define SDL_JOYSTICK_HIDAPI    1
    14.8  #define SDL_HAPTIC_ANDROID    1
    14.9  
   14.10  /* Enable various shared object loading systems */
    15.1 --- a/include/SDL_config_iphoneos.h	Wed Aug 08 15:39:20 2018 -0400
    15.2 +++ b/include/SDL_config_iphoneos.h	Thu Aug 09 16:00:17 2018 -0700
    15.3 @@ -137,6 +137,7 @@
    15.4  
    15.5  /* Enable MFi joystick support */
    15.6  #define SDL_JOYSTICK_MFI 1
    15.7 +#define SDL_JOYSTICK_HIDAPI 1
    15.8  
    15.9  /* Enable Unix style SO loading */
   15.10  #define SDL_LOADSO_DLOPEN 1
    16.1 --- a/include/SDL_config_macosx.h	Wed Aug 08 15:39:20 2018 -0400
    16.2 +++ b/include/SDL_config_macosx.h	Thu Aug 09 16:00:17 2018 -0700
    16.3 @@ -137,6 +137,7 @@
    16.4  
    16.5  /* Enable various input drivers */
    16.6  #define SDL_JOYSTICK_IOKIT  1
    16.7 +#define SDL_JOYSTICK_HIDAPI  1
    16.8  #define SDL_HAPTIC_IOKIT    1
    16.9  
   16.10  /* Enable various shared object loading systems */
    17.1 --- a/include/SDL_config_windows.h	Wed Aug 08 15:39:20 2018 -0400
    17.2 +++ b/include/SDL_config_windows.h	Thu Aug 09 16:00:17 2018 -0700
    17.3 @@ -190,6 +190,7 @@
    17.4  /* Enable various input drivers */
    17.5  #define SDL_JOYSTICK_DINPUT 1
    17.6  #define SDL_JOYSTICK_XINPUT 1
    17.7 +#define SDL_JOYSTICK_HIDAPI 1
    17.8  #define SDL_HAPTIC_DINPUT   1
    17.9  #define SDL_HAPTIC_XINPUT   1
   17.10  
    18.1 --- a/include/SDL_gamecontroller.h	Wed Aug 08 15:39:20 2018 -0400
    18.2 +++ b/include/SDL_gamecontroller.h	Thu Aug 09 16:00:17 2018 -0700
    18.3 @@ -354,6 +354,19 @@
    18.4                                                            SDL_GameControllerButton button);
    18.5  
    18.6  /**
    18.7 + *  Trigger a rumble effect
    18.8 + *  Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling.
    18.9 + *
   18.10 + *  \param gamecontroller The controller to vibrate
   18.11 + *  \param low_frequency_rumble The intensity of the low frequency (left) rumble motor, from 0 to 0xFFFF
   18.12 + *  \param high_frequency_rumble The intensity of the high frequency (right) rumble motor, from 0 to 0xFFFF
   18.13 + *  \param duration_ms The duration of the rumble effect, in milliseconds
   18.14 + *
   18.15 + *  \return 0, or -1 if rumble isn't supported on this joystick
   18.16 + */
   18.17 +extern DECLSPEC int SDLCALL SDL_GameControllerRumble(SDL_GameController *gamecontroller, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
   18.18 +
   18.19 +/**
   18.20   *  Close a controller previously opened with SDL_GameControllerOpen().
   18.21   */
   18.22  extern DECLSPEC void SDLCALL SDL_GameControllerClose(SDL_GameController *gamecontroller);
    19.1 --- a/include/SDL_haptic.h	Wed Aug 08 15:39:20 2018 -0400
    19.2 +++ b/include/SDL_haptic.h	Thu Aug 09 16:00:17 2018 -0700
    19.3 @@ -656,8 +656,8 @@
    19.4   * This struct is exclusively for the ::SDL_HAPTIC_LEFTRIGHT effect.
    19.5   *
    19.6   * The Left/Right effect is used to explicitly control the large and small
    19.7 - * motors, commonly found in modern game controllers. One motor is high
    19.8 - * frequency, the other is low frequency.
    19.9 + * motors, commonly found in modern game controllers. The small (right) motor
   19.10 + * is high frequency, and the large (left) motor is low frequency.
   19.11   *
   19.12   * \sa SDL_HAPTIC_LEFTRIGHT
   19.13   * \sa SDL_HapticEffect
   19.14 @@ -668,7 +668,7 @@
   19.15      Uint16 type;            /**< ::SDL_HAPTIC_LEFTRIGHT */
   19.16  
   19.17      /* Replay */
   19.18 -    Uint32 length;          /**< Duration of the effect. */
   19.19 +    Uint32 length;          /**< Duration of the effect in milliseconds. */
   19.20  
   19.21      /* Rumble */
   19.22      Uint16 large_magnitude; /**< Control of the large controller motor. */
    20.1 --- a/include/SDL_hints.h	Wed Aug 08 15:39:20 2018 -0400
    20.2 +++ b/include/SDL_hints.h	Thu Aug 09 16:00:17 2018 -0700
    20.3 @@ -466,6 +466,84 @@
    20.4  #define SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS"
    20.5  
    20.6  /**
    20.7 + *  \brief  A variable controlling whether the HIDAPI joystick drivers should be used.
    20.8 + *
    20.9 + *  This variable can be set to the following values:
   20.10 + *    "0"       - HIDAPI drivers are not used
   20.11 + *    "1"       - HIDAPI drivers are used (the default)
   20.12 + *
   20.13 + *  This variable is the default for all drivers, but can be overridden by the hints for specific drivers below.
   20.14 + */
   20.15 +#define SDL_HINT_JOYSTICK_HIDAPI "SDL_JOYSTICK_HIDAPI"
   20.16 +
   20.17 +/**
   20.18 + *  \brief  A variable controlling whether the HIDAPI driver for PS4 controllers should be used.
   20.19 + *
   20.20 + *  This variable can be set to the following values:
   20.21 + *    "0"       - HIDAPI driver is not used
   20.22 + *    "1"       - HIDAPI driver is used
   20.23 + *
   20.24 + *  The default is the value of SDL_HINT_JOYSTICK_HIDAPI
   20.25 + */
   20.26 +#define SDL_HINT_JOYSTICK_HIDAPI_PS4 "SDL_JOYSTICK_HIDAPI_PS4"
   20.27 +
   20.28 +/**
   20.29 + *  \brief  A variable controlling whether the HIDAPI driver for Steam Controllers should be used.
   20.30 + *
   20.31 + *  This variable can be set to the following values:
   20.32 + *    "0"       - HIDAPI driver is not used
   20.33 + *    "1"       - HIDAPI driver is used
   20.34 + *
   20.35 + *  The default is the value of SDL_HINT_JOYSTICK_HIDAPI
   20.36 + */
   20.37 +#define SDL_HINT_JOYSTICK_HIDAPI_STEAM "SDL_JOYSTICK_HIDAPI_STEAM"
   20.38 +
   20.39 +/**
   20.40 + *  \brief  A variable controlling whether the HIDAPI driver for Nintendo Switch controllers should be used.
   20.41 + *
   20.42 + *  This variable can be set to the following values:
   20.43 + *    "0"       - HIDAPI driver is not used
   20.44 + *    "1"       - HIDAPI driver is used
   20.45 + *
   20.46 + *  The default is the value of SDL_HINT_JOYSTICK_HIDAPI
   20.47 + */
   20.48 +#define SDL_HINT_JOYSTICK_HIDAPI_SWITCH "SDL_JOYSTICK_HIDAPI_SWITCH"
   20.49 +
   20.50 +/**
   20.51 + *  \brief  A variable controlling whether the HIDAPI driver for XBox 360 controllers should be used.
   20.52 + *
   20.53 + *  This variable can be set to the following values:
   20.54 + *    "0"       - HIDAPI driver is not used
   20.55 + *    "1"       - HIDAPI driver is used
   20.56 + *
   20.57 + *  The default is the value of SDL_HINT_JOYSTICK_HIDAPI
   20.58 + */
   20.59 +#define SDL_HINT_JOYSTICK_HIDAPI_XBOX360 "SDL_JOYSTICK_HIDAPI_XBOX360"
   20.60 +
   20.61 +/**
   20.62 + *  \brief  A variable controlling whether the HIDAPI driver for XBox One controllers should be used.
   20.63 + *
   20.64 + *  This variable can be set to the following values:
   20.65 + *    "0"       - HIDAPI driver is not used
   20.66 + *    "1"       - HIDAPI driver is used
   20.67 + *
   20.68 + *  The default is the value of SDL_HINT_JOYSTICK_HIDAPI
   20.69 + */
   20.70 +#define SDL_HINT_JOYSTICK_HIDAPI_XBOXONE "SDL_JOYSTICK_HIDAPI_XBOXONE"
   20.71 +
   20.72 +/**
   20.73 + *  \brief  A variable that controls whether Steam Controllers should be exposed using the SDL joystick and game controller APIs
   20.74 + *
   20.75 + *  The variable can be set to the following values:
   20.76 + *    "0"       - Do not scan for Steam Controllers
   20.77 + *    "1"       - Scan for Steam Controllers (the default)
   20.78 + *
   20.79 + *  The default value is "1".  This hint must be set before initializing the joystick subsystem.
   20.80 + */
   20.81 +#define SDL_HINT_ENABLE_STEAM_CONTROLLERS "SDL_ENABLE_STEAM_CONTROLLERS"
   20.82 +
   20.83 +
   20.84 +/**
   20.85   *  \brief If set to "0" then never set the top most bit on a SDL Window, even if the video mode expects it.
   20.86   *      This is a debugging aid for developers and not expected to be used by end users. The default is "1"
   20.87   *
    21.1 --- a/include/SDL_joystick.h	Wed Aug 08 15:39:20 2018 -0400
    21.2 +++ b/include/SDL_joystick.h	Thu Aug 09 16:00:17 2018 -0700
    21.3 @@ -97,10 +97,10 @@
    21.4  typedef enum
    21.5  {
    21.6      SDL_JOYSTICK_POWER_UNKNOWN = -1,
    21.7 -    SDL_JOYSTICK_POWER_EMPTY,
    21.8 -    SDL_JOYSTICK_POWER_LOW,
    21.9 -    SDL_JOYSTICK_POWER_MEDIUM,
   21.10 -    SDL_JOYSTICK_POWER_FULL,
   21.11 +    SDL_JOYSTICK_POWER_EMPTY,   /* <= 5% */
   21.12 +    SDL_JOYSTICK_POWER_LOW,     /* <= 20% */
   21.13 +    SDL_JOYSTICK_POWER_MEDIUM,  /* <= 70% */
   21.14 +    SDL_JOYSTICK_POWER_FULL,    /* <= 100% */
   21.15      SDL_JOYSTICK_POWER_WIRED,
   21.16      SDL_JOYSTICK_POWER_MAX
   21.17  } SDL_JoystickPowerLevel;
   21.18 @@ -362,6 +362,19 @@
   21.19                                                      int button);
   21.20  
   21.21  /**
   21.22 + *  Trigger a rumble effect
   21.23 + *  Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling.
   21.24 + *
   21.25 + *  \param joystick The joystick to vibrate
   21.26 + *  \param low_frequency_rumble The intensity of the low frequency (left) rumble motor, from 0 to 0xFFFF
   21.27 + *  \param high_frequency_rumble The intensity of the high frequency (right) rumble motor, from 0 to 0xFFFF
   21.28 + *  \param duration_ms The duration of the rumble effect, in milliseconds
   21.29 + *
   21.30 + *  \return 0, or -1 if rumble isn't supported on this joystick
   21.31 + */
   21.32 +extern DECLSPEC int SDLCALL SDL_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
   21.33 +
   21.34 +/**
   21.35   *  Close a joystick previously opened with SDL_JoystickOpen().
   21.36   */
   21.37  extern DECLSPEC void SDLCALL SDL_JoystickClose(SDL_Joystick * joystick);
    22.1 --- a/include/SDL_stdinc.h	Wed Aug 08 15:39:20 2018 -0400
    22.2 +++ b/include/SDL_stdinc.h	Thu Aug 09 16:00:17 2018 -0700
    22.3 @@ -450,6 +450,7 @@
    22.4  extern DECLSPEC void *SDLCALL SDL_memmove(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len);
    22.5  extern DECLSPEC int SDLCALL SDL_memcmp(const void *s1, const void *s2, size_t len);
    22.6  
    22.7 +extern DECLSPEC wchar_t *SDLCALL SDL_wcsdup(const wchar_t *wstr);
    22.8  extern DECLSPEC size_t SDLCALL SDL_wcslen(const wchar_t *wstr);
    22.9  extern DECLSPEC size_t SDLCALL SDL_wcslcpy(SDL_OUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen);
   22.10  extern DECLSPEC size_t SDLCALL SDL_wcslcat(SDL_INOUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen);
    23.1 --- a/src/dynapi/SDL_dynapi_overrides.h	Wed Aug 08 15:39:20 2018 -0400
    23.2 +++ b/src/dynapi/SDL_dynapi_overrides.h	Thu Aug 09 16:00:17 2018 -0700
    23.3 @@ -677,3 +677,6 @@
    23.4  #define SDL_AndroidBackButton SDL_AndroidBackButton_REAL
    23.5  #define SDL_exp SDL_exp_REAL
    23.6  #define SDL_expf SDL_expf_REAL
    23.7 +#define SDL_wcsdup SDL_wcsdup_REAL
    23.8 +#define SDL_GameControllerRumble SDL_GameControllerRumble_REAL
    23.9 +#define SDL_JoystickRumble SDL_JoystickRumble_REAL
    24.1 --- a/src/dynapi/SDL_dynapi_procs.h	Wed Aug 08 15:39:20 2018 -0400
    24.2 +++ b/src/dynapi/SDL_dynapi_procs.h	Thu Aug 09 16:00:17 2018 -0700
    24.3 @@ -715,9 +715,10 @@
    24.4  #ifdef __ANDROID__
    24.5  SDL_DYNAPI_PROC(SDL_bool,SDL_IsChromebook,(void),(),return)
    24.6  SDL_DYNAPI_PROC(SDL_bool,SDL_IsDeXMode,(void),(),return)
    24.7 -#endif
    24.8 -#ifdef __ANDROID__
    24.9  SDL_DYNAPI_PROC(void,SDL_AndroidBackButton,(void),(),return)
   24.10  #endif
   24.11  SDL_DYNAPI_PROC(double,SDL_exp,(double a),(a),return)
   24.12  SDL_DYNAPI_PROC(float,SDL_expf,(float a),(a),return)
   24.13 +SDL_DYNAPI_PROC(wchar_t*,SDL_wcsdup,(const wchar_t *a),(a),return)
   24.14 +SDL_DYNAPI_PROC(int,SDL_GameControllerRumble,(SDL_GameController *a, Uint16 b, Uint16 c, Uint32 d),(a,b,c,d),return)
   24.15 +SDL_DYNAPI_PROC(int,SDL_JoystickRumble,(SDL_Joystick *a, Uint16 b, Uint16 c, Uint32 d),(a,b,c,d),return)
    25.1 --- a/src/joystick/SDL_gamecontroller.c	Wed Aug 08 15:39:20 2018 -0400
    25.2 +++ b/src/joystick/SDL_gamecontroller.c	Thu Aug 09 16:00:17 2018 -0700
    25.3 @@ -102,6 +102,7 @@
    25.4  static SDL_JoystickGUID s_zeroGUID;
    25.5  static ControllerMapping_t *s_pSupportedControllers = NULL;
    25.6  static ControllerMapping_t *s_pDefaultMapping = NULL;
    25.7 +static ControllerMapping_t *s_pHIDAPIMapping = NULL;
    25.8  static ControllerMapping_t *s_pXInputMapping = NULL;
    25.9  
   25.10  /* The SDL game controller structure */
   25.11 @@ -430,6 +431,10 @@
   25.12          }
   25.13          pSupportedController = pSupportedController->next;
   25.14      }
   25.15 +    if (guid->data[14] == 'h') {
   25.16 +        /* This is a HIDAPI device */
   25.17 +        return s_pHIDAPIMapping;
   25.18 +    }
   25.19  #if SDL_JOYSTICK_XINPUT
   25.20      if (guid->data[14] == 'x') {
   25.21          /* This is an XInput device */
   25.22 @@ -1130,6 +1135,7 @@
   25.23      char *pchGUID;
   25.24      SDL_JoystickGUID jGUID;
   25.25      SDL_bool is_default_mapping = SDL_FALSE;
   25.26 +    SDL_bool is_hidapi_mapping = SDL_FALSE;
   25.27      SDL_bool is_xinput_mapping = SDL_FALSE;
   25.28      SDL_bool existing = SDL_FALSE;
   25.29      ControllerMapping_t *pControllerMapping;
   25.30 @@ -1144,6 +1150,8 @@
   25.31      }
   25.32      if (!SDL_strcasecmp(pchGUID, "default")) {
   25.33          is_default_mapping = SDL_TRUE;
   25.34 +    } else if (!SDL_strcasecmp(pchGUID, "hidapi")) {
   25.35 +        is_hidapi_mapping = SDL_TRUE;
   25.36      } else if (!SDL_strcasecmp(pchGUID, "xinput")) {
   25.37          is_xinput_mapping = SDL_TRUE;
   25.38      }
   25.39 @@ -1160,6 +1168,8 @@
   25.40      } else {
   25.41          if (is_default_mapping) {
   25.42              s_pDefaultMapping = pControllerMapping;
   25.43 +        } else if (is_hidapi_mapping) {
   25.44 +            s_pHIDAPIMapping = pControllerMapping;
   25.45          } else if (is_xinput_mapping) {
   25.46              s_pXInputMapping = pControllerMapping;
   25.47          }
   25.48 @@ -1458,7 +1468,6 @@
   25.49      }
   25.50  
   25.51      SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL);
   25.52 -    vidpid = MAKE_VIDPID(vendor, product);
   25.53  
   25.54      if (SDL_GetHintBoolean("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD", SDL_FALSE)) {
   25.55          /* We shouldn't ignore Steam's virtual gamepad since it's using the hints to filter out the real controllers so it can remap input for the virtual controller */
   25.56 @@ -1476,6 +1485,8 @@
   25.57          }
   25.58      }
   25.59  
   25.60 +    vidpid = MAKE_VIDPID(vendor, product);
   25.61 +
   25.62      if (SDL_allowed_controllers.num_entries > 0) {
   25.63          for (i = 0; i < SDL_allowed_controllers.num_entries; ++i) {
   25.64              if (vidpid == SDL_allowed_controllers.entries[i]) {
   25.65 @@ -1503,22 +1514,18 @@
   25.66  SDL_GameController *
   25.67  SDL_GameControllerOpen(int device_index)
   25.68  {
   25.69 +    SDL_JoystickID instance_id;
   25.70      SDL_GameController *gamecontroller;
   25.71      SDL_GameController *gamecontrollerlist;
   25.72      ControllerMapping_t *pSupportedController = NULL;
   25.73  
   25.74      SDL_LockJoysticks();
   25.75  
   25.76 -    if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) {
   25.77 -        SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
   25.78 -        SDL_UnlockJoysticks();
   25.79 -        return (NULL);
   25.80 -    }
   25.81 -
   25.82      gamecontrollerlist = SDL_gamecontrollers;
   25.83      /* If the controller is already open, return it */
   25.84 +    instance_id = SDL_JoystickGetDeviceInstanceID(device_index);
   25.85      while (gamecontrollerlist) {
   25.86 -        if (SDL_SYS_GetInstanceIdOfDeviceIndex(device_index) == gamecontrollerlist->joystick->instance_id) {
   25.87 +        if (instance_id == gamecontrollerlist->joystick->instance_id) {
   25.88                  gamecontroller = gamecontrollerlist;
   25.89                  ++gamecontroller->ref_count;
   25.90                  SDL_UnlockJoysticks();
   25.91 @@ -1834,6 +1841,12 @@
   25.92  }
   25.93  
   25.94  
   25.95 +int
   25.96 +SDL_GameControllerRumble(SDL_GameController *gamecontroller, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   25.97 +{
   25.98 +    return SDL_JoystickRumble(SDL_GameControllerGetJoystick(gamecontroller), low_frequency_rumble, high_frequency_rumble, duration_ms);
   25.99 +}
  25.100 +
  25.101  void
  25.102  SDL_GameControllerClose(SDL_GameController * gamecontroller)
  25.103  {
    26.1 --- a/src/joystick/SDL_gamecontrollerdb.h	Wed Aug 08 15:39:20 2018 -0400
    26.2 +++ b/src/joystick/SDL_gamecontrollerdb.h	Thu Aug 09 16:00:17 2018 -0700
    26.3 @@ -555,6 +555,7 @@
    26.4      "05000000bc20000000550000ffff3f00,GameSir G3w,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
    26.5      "050000005509000003720000cf7f3f00,NVIDIA Controller v01.01,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
    26.6      "050000005509000010720000ffff3f00,NVIDIA Controller v01.03,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
    26.7 +    "050000007e05000009200000ffff0f00,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b17,y:b2,", /* Extremely slow in Bluetooth mode on Android */
    26.8      "050000004c05000068020000dfff3f00,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
    26.9      "050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,",
   26.10      "050000004c050000cc090000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,",
   26.11 @@ -575,6 +576,7 @@
   26.12  #if defined(SDL_JOYSTICK_EMSCRIPTEN)
   26.13      "default,Standard Gamepad,a:b0,b:b1,back:b8,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b16,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
   26.14  #endif
   26.15 +    "hidapi,*,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
   26.16      NULL
   26.17  };
   26.18  
    27.1 --- a/src/joystick/SDL_joystick.c	Wed Aug 08 15:39:20 2018 -0400
    27.2 +++ b/src/joystick/SDL_joystick.c	Thu Aug 09 16:00:17 2018 -0700
    27.3 @@ -23,6 +23,7 @@
    27.4  /* This is the joystick API for Simple DirectMedia Layer */
    27.5  
    27.6  #include "SDL.h"
    27.7 +#include "SDL_atomic.h"
    27.8  #include "SDL_events.h"
    27.9  #include "SDL_sysjoystick.h"
   27.10  #include "SDL_assert.h"
   27.11 @@ -33,11 +34,45 @@
   27.12  #endif
   27.13  #include "../video/SDL_sysvideo.h"
   27.14  
   27.15 +/* This is included in only one place because it has a large static list of controllers */
   27.16 +#include "controller_type.h"
   27.17  
   27.18 +#ifdef __WIN32__
   27.19 +/* Needed for checking for input remapping programs */
   27.20 +#include "../../core/windows/SDL_windows.h"
   27.21 +
   27.22 +#undef UNICODE          /* We want ASCII functions */
   27.23 +#include <tlhelp32.h>
   27.24 +#endif
   27.25 +
   27.26 +static SDL_JoystickDriver *SDL_joystick_drivers[] = {
   27.27 +#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
   27.28 +    &SDL_WINDOWS_JoystickDriver,
   27.29 +#endif
   27.30 +#ifdef SDL_JOYSTICK_LINUX
   27.31 +    &SDL_LINUX_JoystickDriver,
   27.32 +#endif
   27.33 +#ifdef SDL_JOYSTICK_IOKIT
   27.34 +    &SDL_DARWIN_JoystickDriver,
   27.35 +#endif
   27.36 +#if defined(__IPHONEOS__) || defined(__TVOS__)
   27.37 +    &SDL_IOS_JoystickDriver,
   27.38 +#endif
   27.39 +#ifdef SDL_JOYSTICK_ANDROID
   27.40 +    &SDL_ANDROID_JoystickDriver,
   27.41 +#endif
   27.42 +#ifdef SDL_JOYSTICK_HIDAPI
   27.43 +    &SDL_HIDAPI_JoystickDriver,
   27.44 +#endif
   27.45 +#if defined(SDL_JOYSTICK_DUMMY) || defined(SDL_JOYSTICK_DISABLED)
   27.46 +    &SDL_DUMMY_JoystickDriver
   27.47 +#endif
   27.48 +};
   27.49  static SDL_bool SDL_joystick_allows_background_events = SDL_FALSE;
   27.50  static SDL_Joystick *SDL_joysticks = NULL;
   27.51  static SDL_bool SDL_updating_joystick = SDL_FALSE;
   27.52  static SDL_mutex *SDL_joystick_lock = NULL; /* This needs to support recursive locks */
   27.53 +static SDL_atomic_t SDL_next_joystick_instance_id;
   27.54  
   27.55  void
   27.56  SDL_LockJoysticks(void)
   27.57 @@ -69,7 +104,7 @@
   27.58  int
   27.59  SDL_JoystickInit(void)
   27.60  {
   27.61 -    int status;
   27.62 +    int i, status;
   27.63  
   27.64      SDL_GameControllerInitMappings();
   27.65  
   27.66 @@ -88,11 +123,13 @@
   27.67      }
   27.68  #endif /* !SDL_EVENTS_DISABLED */
   27.69  
   27.70 -    status = SDL_SYS_JoystickInit();
   27.71 -    if (status >= 0) {
   27.72 -        status = 0;
   27.73 +    status = -1;
   27.74 +    for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
   27.75 +        if (SDL_joystick_drivers[i]->Init() >= 0) {
   27.76 +            status = 0;
   27.77 +        }
   27.78      }
   27.79 -    return (status);
   27.80 +    return status;
   27.81  }
   27.82  
   27.83  /*
   27.84 @@ -101,7 +138,48 @@
   27.85  int
   27.86  SDL_NumJoysticks(void)
   27.87  {
   27.88 -    return SDL_SYS_NumJoysticks();
   27.89 +    int i, total_joysticks = 0;
   27.90 +    SDL_LockJoysticks();
   27.91 +    for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
   27.92 +        total_joysticks += SDL_joystick_drivers[i]->GetCount();
   27.93 +    }
   27.94 +    SDL_UnlockJoysticks();
   27.95 +    return total_joysticks;
   27.96 +}
   27.97 +
   27.98 +/*
   27.99 + * Return the next available joystick instance ID
  27.100 + * This may be called by drivers from multiple threads, unprotected by any locks
  27.101 + */
  27.102 +SDL_JoystickID SDL_GetNextJoystickInstanceID()
  27.103 +{
  27.104 +    return SDL_AtomicIncRef(&SDL_next_joystick_instance_id);
  27.105 +}
  27.106 +
  27.107 +/*
  27.108 + * Get the driver and device index for an API device index
  27.109 + * This should be called while the joystick lock is held, to prevent another thread from updating the list
  27.110 + */
  27.111 +SDL_bool
  27.112 +SDL_GetDriverAndJoystickIndex(int device_index, SDL_JoystickDriver **driver, int *driver_index)
  27.113 +{
  27.114 +    int i, num_joysticks, total_joysticks = 0;
  27.115 +
  27.116 +    if (device_index >= 0) {
  27.117 +        for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
  27.118 +            num_joysticks = SDL_joystick_drivers[i]->GetCount();
  27.119 +            if (device_index < num_joysticks) {
  27.120 +                *driver = SDL_joystick_drivers[i];
  27.121 +                *driver_index = device_index;
  27.122 +                return SDL_TRUE;
  27.123 +            }
  27.124 +            device_index -= num_joysticks;
  27.125 +            total_joysticks += num_joysticks;
  27.126 +        }
  27.127 +    }
  27.128 +
  27.129 +    SDL_SetError("There are %d joysticks available", total_joysticks);
  27.130 +    return SDL_FALSE;
  27.131  }
  27.132  
  27.133  /*
  27.134 @@ -127,11 +205,17 @@
  27.135  const char *
  27.136  SDL_JoystickNameForIndex(int device_index)
  27.137  {
  27.138 -    if (device_index < 0 || device_index >= SDL_NumJoysticks()) {
  27.139 -        SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
  27.140 -        return (NULL);
  27.141 +    SDL_JoystickDriver *driver;
  27.142 +    const char *name = NULL;
  27.143 +
  27.144 +    SDL_LockJoysticks();
  27.145 +    if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
  27.146 +        name = SDL_FixupJoystickName(driver->GetDeviceName(device_index));
  27.147      }
  27.148 -    return SDL_FixupJoystickName(SDL_SYS_JoystickNameForDeviceIndex(device_index));
  27.149 +    SDL_UnlockJoysticks();
  27.150 +
  27.151 +    /* FIXME: Really we should reference count this name so it doesn't go away after unlock */
  27.152 +    return name;
  27.153  }
  27.154  
  27.155  /*
  27.156 @@ -176,27 +260,30 @@
  27.157  SDL_Joystick *
  27.158  SDL_JoystickOpen(int device_index)
  27.159  {
  27.160 +    SDL_JoystickDriver *driver;
  27.161 +    SDL_JoystickID instance_id;
  27.162      SDL_Joystick *joystick;
  27.163      SDL_Joystick *joysticklist;
  27.164      const char *joystickname = NULL;
  27.165  
  27.166 -    if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) {
  27.167 -        SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
  27.168 -        return (NULL);
  27.169 +    SDL_LockJoysticks();
  27.170 +
  27.171 +    if (!SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
  27.172 +        SDL_UnlockJoysticks();
  27.173 +        return NULL;
  27.174      }
  27.175  
  27.176 -    SDL_LockJoysticks();
  27.177 -
  27.178      joysticklist = SDL_joysticks;
  27.179      /* If the joystick is already open, return it
  27.180       * it is important that we have a single joystick * for each instance id
  27.181       */
  27.182 +    instance_id = driver->GetDeviceInstanceID(device_index);
  27.183      while (joysticklist) {
  27.184 -        if (SDL_JoystickGetDeviceInstanceID(device_index) == joysticklist->instance_id) {
  27.185 +        if (instance_id == joysticklist->instance_id) {
  27.186                  joystick = joysticklist;
  27.187                  ++joystick->ref_count;
  27.188                  SDL_UnlockJoysticks();
  27.189 -                return (joystick);
  27.190 +                return joystick;
  27.191          }
  27.192          joysticklist = joysticklist->next;
  27.193      }
  27.194 @@ -208,18 +295,23 @@
  27.195          SDL_UnlockJoysticks();
  27.196          return NULL;
  27.197      }
  27.198 +    joystick->driver = driver;
  27.199 +    joystick->instance_id = instance_id;
  27.200  
  27.201 -    if (SDL_SYS_JoystickOpen(joystick, device_index) < 0) {
  27.202 +    if (driver->Open(joystick, device_index) < 0) {
  27.203          SDL_free(joystick);
  27.204          SDL_UnlockJoysticks();
  27.205          return NULL;
  27.206      }
  27.207  
  27.208 -    joystickname = SDL_SYS_JoystickNameForDeviceIndex(device_index);
  27.209 -    if (joystickname)
  27.210 +    joystickname = driver->GetDeviceName(device_index);
  27.211 +    if (joystickname) {
  27.212          joystick->name = SDL_strdup(joystickname);
  27.213 -    else
  27.214 +    } else {
  27.215          joystick->name = NULL;
  27.216 +    }
  27.217 +
  27.218 +    joystick->guid = driver->GetDeviceGUID(device_index);
  27.219  
  27.220      if (joystick->naxes > 0) {
  27.221          joystick->axes = (SDL_JoystickAxisInfo *) SDL_calloc(joystick->naxes, sizeof(SDL_JoystickAxisInfo));
  27.222 @@ -263,9 +355,9 @@
  27.223  
  27.224      SDL_UnlockJoysticks();
  27.225  
  27.226 -    SDL_SYS_JoystickUpdate(joystick);
  27.227 +    driver->Update(joystick);
  27.228  
  27.229 -    return (joystick);
  27.230 +    return joystick;
  27.231  }
  27.232  
  27.233  
  27.234 @@ -294,9 +386,9 @@
  27.235  SDL_JoystickNumAxes(SDL_Joystick * joystick)
  27.236  {
  27.237      if (!SDL_PrivateJoystickValid(joystick)) {
  27.238 -        return (-1);
  27.239 +        return -1;
  27.240      }
  27.241 -    return (joystick->naxes);
  27.242 +    return joystick->naxes;
  27.243  }
  27.244  
  27.245  /*
  27.246 @@ -306,9 +398,9 @@
  27.247  SDL_JoystickNumHats(SDL_Joystick * joystick)
  27.248  {
  27.249      if (!SDL_PrivateJoystickValid(joystick)) {
  27.250 -        return (-1);
  27.251 +        return -1;
  27.252      }
  27.253 -    return (joystick->nhats);
  27.254 +    return joystick->nhats;
  27.255  }
  27.256  
  27.257  /*
  27.258 @@ -318,9 +410,9 @@
  27.259  SDL_JoystickNumBalls(SDL_Joystick * joystick)
  27.260  {
  27.261      if (!SDL_PrivateJoystickValid(joystick)) {
  27.262 -        return (-1);
  27.263 +        return -1;
  27.264      }
  27.265 -    return (joystick->nballs);
  27.266 +    return joystick->nballs;
  27.267  }
  27.268  
  27.269  /*
  27.270 @@ -330,9 +422,9 @@
  27.271  SDL_JoystickNumButtons(SDL_Joystick * joystick)
  27.272  {
  27.273      if (!SDL_PrivateJoystickValid(joystick)) {
  27.274 -        return (-1);
  27.275 +        return -1;
  27.276      }
  27.277 -    return (joystick->nbuttons);
  27.278 +    return joystick->nbuttons;
  27.279  }
  27.280  
  27.281  /*
  27.282 @@ -344,7 +436,7 @@
  27.283      Sint16 state;
  27.284  
  27.285      if (!SDL_PrivateJoystickValid(joystick)) {
  27.286 -        return (0);
  27.287 +        return 0;
  27.288      }
  27.289      if (axis < joystick->naxes) {
  27.290          state = joystick->axes[axis].value;
  27.291 @@ -352,7 +444,7 @@
  27.292          SDL_SetError("Joystick only has %d axes", joystick->naxes);
  27.293          state = 0;
  27.294      }
  27.295 -    return (state);
  27.296 +    return state;
  27.297  }
  27.298  
  27.299  /*
  27.300 @@ -383,7 +475,7 @@
  27.301      Uint8 state;
  27.302  
  27.303      if (!SDL_PrivateJoystickValid(joystick)) {
  27.304 -        return (0);
  27.305 +        return 0;
  27.306      }
  27.307      if (hat < joystick->nhats) {
  27.308          state = joystick->hats[hat];
  27.309 @@ -391,7 +483,7 @@
  27.310          SDL_SetError("Joystick only has %d hats", joystick->nhats);
  27.311          state = 0;
  27.312      }
  27.313 -    return (state);
  27.314 +    return state;
  27.315  }
  27.316  
  27.317  /*
  27.318 @@ -403,7 +495,7 @@
  27.319      int retval;
  27.320  
  27.321      if (!SDL_PrivateJoystickValid(joystick)) {
  27.322 -        return (-1);
  27.323 +        return -1;
  27.324      }
  27.325  
  27.326      retval = 0;
  27.327 @@ -419,7 +511,7 @@
  27.328      } else {
  27.329          return SDL_SetError("Joystick only has %d balls", joystick->nballs);
  27.330      }
  27.331 -    return (retval);
  27.332 +    return retval;
  27.333  }
  27.334  
  27.335  /*
  27.336 @@ -431,7 +523,7 @@
  27.337      Uint8 state;
  27.338  
  27.339      if (!SDL_PrivateJoystickValid(joystick)) {
  27.340 -        return (0);
  27.341 +        return 0;
  27.342      }
  27.343      if (button < joystick->nbuttons) {
  27.344          state = joystick->buttons[button];
  27.345 @@ -439,7 +531,7 @@
  27.346          SDL_SetError("Joystick only has %d buttons", joystick->nbuttons);
  27.347          state = 0;
  27.348      }
  27.349 -    return (state);
  27.350 +    return state;
  27.351  }
  27.352  
  27.353  /*
  27.354 @@ -453,7 +545,7 @@
  27.355          return SDL_FALSE;
  27.356      }
  27.357  
  27.358 -    return SDL_SYS_JoystickAttached(joystick);
  27.359 +    return joystick->driver->IsAttached(joystick);
  27.360  }
  27.361  
  27.362  /*
  27.363 @@ -463,10 +555,10 @@
  27.364  SDL_JoystickInstanceID(SDL_Joystick * joystick)
  27.365  {
  27.366      if (!SDL_PrivateJoystickValid(joystick)) {
  27.367 -        return (-1);
  27.368 +        return -1;
  27.369      }
  27.370  
  27.371 -    return (joystick->instance_id);
  27.372 +    return joystick->instance_id;
  27.373  }
  27.374  
  27.375  /*
  27.376 @@ -495,12 +587,21 @@
  27.377  SDL_JoystickName(SDL_Joystick * joystick)
  27.378  {
  27.379      if (!SDL_PrivateJoystickValid(joystick)) {
  27.380 -        return (NULL);
  27.381 +        return NULL;
  27.382      }
  27.383  
  27.384      return SDL_FixupJoystickName(joystick->name);
  27.385  }
  27.386  
  27.387 +int
  27.388 +SDL_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
  27.389 +{
  27.390 +    if (!SDL_PrivateJoystickValid(joystick)) {
  27.391 +        return -1;
  27.392 +    }
  27.393 +    return joystick->driver->Rumble(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms);
  27.394 +}
  27.395 +
  27.396  /*
  27.397   * Close a joystick previously opened with SDL_JoystickOpen()
  27.398   */
  27.399 @@ -510,7 +611,7 @@
  27.400      SDL_Joystick *joysticklist;
  27.401      SDL_Joystick *joysticklistprev;
  27.402  
  27.403 -    if (!joystick) {
  27.404 +    if (!SDL_PrivateJoystickValid(joystick)) {
  27.405          return;
  27.406      }
  27.407  
  27.408 @@ -527,7 +628,7 @@
  27.409          return;
  27.410      }
  27.411  
  27.412 -    SDL_SYS_JoystickClose(joystick);
  27.413 +    joystick->driver->Close(joystick);
  27.414      joystick->hwdata = NULL;
  27.415  
  27.416      joysticklist = SDL_joysticks;
  27.417 @@ -561,6 +662,8 @@
  27.418  void
  27.419  SDL_JoystickQuit(void)
  27.420  {
  27.421 +    int i;
  27.422 +
  27.423      /* Make sure we're not getting called in the middle of updating joysticks */
  27.424      SDL_assert(!SDL_updating_joystick);
  27.425  
  27.426 @@ -573,7 +676,9 @@
  27.427      }
  27.428  
  27.429      /* Quit the joystick setup */
  27.430 -    SDL_SYS_JoystickQuit();
  27.431 +    for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
  27.432 +       SDL_joystick_drivers[i]->Quit();
  27.433 +    }
  27.434  
  27.435      SDL_UnlockJoysticks();
  27.436  
  27.437 @@ -609,10 +714,16 @@
  27.438  
  27.439  /* These are global for SDL_sysjoystick.c and SDL_events.c */
  27.440  
  27.441 -void SDL_PrivateJoystickAdded(int device_index)
  27.442 +void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
  27.443  {
  27.444  #if !SDL_EVENTS_DISABLED
  27.445      SDL_Event event;
  27.446 +    int device_index;
  27.447 +
  27.448 +    device_index = SDL_JoystickGetDeviceIndexFromInstanceID(device_instance);
  27.449 +    if (device_index < 0) {
  27.450 +        return;
  27.451 +    }
  27.452  
  27.453      event.type = SDL_JOYDEVICEADDED;
  27.454  
  27.455 @@ -722,7 +833,7 @@
  27.456          posted = SDL_PushEvent(&event) == 1;
  27.457      }
  27.458  #endif /* !SDL_EVENTS_DISABLED */
  27.459 -    return (posted);
  27.460 +    return posted;
  27.461  }
  27.462  
  27.463  int
  27.464 @@ -762,7 +873,7 @@
  27.465          posted = SDL_PushEvent(&event) == 1;
  27.466      }
  27.467  #endif /* !SDL_EVENTS_DISABLED */
  27.468 -    return (posted);
  27.469 +    return posted;
  27.470  }
  27.471  
  27.472  int
  27.473 @@ -798,7 +909,7 @@
  27.474          posted = SDL_PushEvent(&event) == 1;
  27.475      }
  27.476  #endif /* !SDL_EVENTS_DISABLED */
  27.477 -    return (posted);
  27.478 +    return posted;
  27.479  }
  27.480  
  27.481  int
  27.482 @@ -817,7 +928,7 @@
  27.483          break;
  27.484      default:
  27.485          /* Invalid state -- bail */
  27.486 -        return (0);
  27.487 +        return 0;
  27.488      }
  27.489  #endif /* !SDL_EVENTS_DISABLED */
  27.490  
  27.491 @@ -850,12 +961,13 @@
  27.492          posted = SDL_PushEvent(&event) == 1;
  27.493      }
  27.494  #endif /* !SDL_EVENTS_DISABLED */
  27.495 -    return (posted);
  27.496 +    return posted;
  27.497  }
  27.498  
  27.499  void
  27.500  SDL_JoystickUpdate(void)
  27.501  {
  27.502 +    int i;
  27.503      SDL_Joystick *joystick;
  27.504  
  27.505      SDL_LockJoysticks();
  27.506 @@ -872,15 +984,13 @@
  27.507      SDL_UnlockJoysticks();
  27.508  
  27.509      for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
  27.510 -        SDL_SYS_JoystickUpdate(joystick);
  27.511 +        joystick->driver->Update(joystick);
  27.512  
  27.513          if (joystick->delayed_guide_button) {
  27.514              SDL_GameControllerHandleDelayedGuideButton(joystick);
  27.515          }
  27.516  
  27.517          if (joystick->force_recentering) {
  27.518 -            int i;
  27.519 -
  27.520              /* Tell the app that everything is centered/unpressed... */
  27.521              for (i = 0; i < joystick->naxes; i++) {
  27.522                  if (joystick->axes[i].has_initial_value) {
  27.523 @@ -914,7 +1024,9 @@
  27.524      /* this needs to happen AFTER walking the joystick list above, so that any
  27.525         dangling hardware data from removed devices can be free'd
  27.526       */
  27.527 -    SDL_SYS_JoystickDetect();
  27.528 +    for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
  27.529 +        SDL_joystick_drivers[i]->Detect();
  27.530 +    }
  27.531  
  27.532      SDL_UnlockJoysticks();
  27.533  }
  27.534 @@ -947,7 +1059,7 @@
  27.535          }
  27.536          break;
  27.537      }
  27.538 -    return (state);
  27.539 +    return state;
  27.540  #endif /* SDL_EVENTS_DISABLED */
  27.541  }
  27.542  
  27.543 @@ -963,7 +1075,7 @@
  27.544          /* guid16[4] is product ID */
  27.545          guid16[5] == 0x0000
  27.546          /* guid16[6] is product version */
  27.547 -    ) {
  27.548 +   ) {
  27.549          if (vendor) {
  27.550              *vendor = guid16[2];
  27.551          }
  27.552 @@ -986,6 +1098,43 @@
  27.553      }
  27.554  }
  27.555  
  27.556 +SDL_bool
  27.557 +SDL_IsJoystickPS4(Uint16 vendor, Uint16 product)
  27.558 +{
  27.559 +    return (GuessControllerType(vendor, product) == k_eControllerType_PS4Controller);
  27.560 +}
  27.561 +
  27.562 +SDL_bool
  27.563 +SDL_IsJoystickNintendoSwitchPro(Uint16 vendor, Uint16 product)
  27.564 +{
  27.565 +    return (GuessControllerType(vendor, product) == k_eControllerType_SwitchProController);
  27.566 +}
  27.567 +
  27.568 +SDL_bool
  27.569 +SDL_IsJoystickSteamController(Uint16 vendor, Uint16 product)
  27.570 +{
  27.571 +    return BIsSteamController(GuessControllerType(vendor, product)) ? SDL_TRUE : SDL_FALSE;
  27.572 +}
  27.573 +
  27.574 +SDL_bool
  27.575 +SDL_IsJoystickXbox360(Uint16 vendor, Uint16 product)
  27.576 +{
  27.577 +	/* Filter out some bogus values here */
  27.578 +	if (vendor == 0x0000 && product == 0x0000) {
  27.579 +		return SDL_FALSE;
  27.580 +	}
  27.581 +	if (vendor == 0x0001 && product == 0x0001) {
  27.582 +		return SDL_FALSE;
  27.583 +	}
  27.584 +    return (GuessControllerType(vendor, product) == k_eControllerType_XBox360Controller);
  27.585 +}
  27.586 +
  27.587 +SDL_bool
  27.588 +SDL_IsJoystickXboxOne(Uint16 vendor, Uint16 product)
  27.589 +{
  27.590 +    return (GuessControllerType(vendor, product) == k_eControllerType_XBoxOneController);
  27.591 +}
  27.592 +
  27.593  static SDL_bool SDL_IsJoystickProductWheel(Uint32 vidpid)
  27.594  {
  27.595      static Uint32 wheel_joysticks[] = {
  27.596 @@ -1092,19 +1241,80 @@
  27.597          return SDL_JOYSTICK_TYPE_THROTTLE;
  27.598      }
  27.599  
  27.600 +    if (GuessControllerType(vendor, product) != k_eControllerType_UnknownNonSteamController) {
  27.601 +        return SDL_JOYSTICK_TYPE_GAMECONTROLLER;
  27.602 +    }
  27.603 +
  27.604      return SDL_JOYSTICK_TYPE_UNKNOWN;
  27.605  }
  27.606  
  27.607 +static SDL_bool SDL_IsPS4RemapperRunning(void)
  27.608 +{
  27.609 +#ifdef __WIN32__
  27.610 +    const char *mapper_processes[] = {
  27.611 +        "DS4Windows.exe",
  27.612 +        "InputMapper.exe",
  27.613 +    };
  27.614 +    int i;
  27.615 +    PROCESSENTRY32 pe32;
  27.616 +    SDL_bool found = SDL_FALSE;
  27.617 +
  27.618 +    /* Take a snapshot of all processes in the system */
  27.619 +    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  27.620 +    if (hProcessSnap != INVALID_HANDLE_VALUE) {
  27.621 +        pe32.dwSize = sizeof(PROCESSENTRY32);
  27.622 +        if (Process32First(hProcessSnap, &pe32)) {
  27.623 +            do
  27.624 +            {
  27.625 +                for (i = 0; i < SDL_arraysize(mapper_processes); ++i) {
  27.626 +                    if (SDL_strcasecmp(pe32.szExeFile, mapper_processes[i]) == 0) {
  27.627 +                        found = SDL_TRUE;
  27.628 +                    }
  27.629 +                }
  27.630 +            } while (Process32Next(hProcessSnap, &pe32) && !found);
  27.631 +        }
  27.632 +        CloseHandle(hProcessSnap);
  27.633 +    }
  27.634 +    return found;
  27.635 +#else
  27.636 +    return SDL_FALSE;
  27.637 +#endif
  27.638 +}
  27.639 +
  27.640 +SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid)
  27.641 +{
  27.642 +    Uint16 vendor;
  27.643 +    Uint16 product;
  27.644 +
  27.645 +    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL);
  27.646 +
  27.647 +    if (SDL_IsJoystickPS4(vendor, product) && SDL_IsPS4RemapperRunning()) {
  27.648 +        return SDL_TRUE;
  27.649 +    }
  27.650 +
  27.651 +    if (SDL_IsGameControllerNameAndGUID(name, guid) &&
  27.652 +        SDL_ShouldIgnoreGameController(name, guid)) {
  27.653 +        return SDL_TRUE;
  27.654 +    }
  27.655 +
  27.656 +    return SDL_FALSE;
  27.657 +}
  27.658 +
  27.659  /* return the guid for this index */
  27.660  SDL_JoystickGUID SDL_JoystickGetDeviceGUID(int device_index)
  27.661  {
  27.662 -    if (device_index < 0 || device_index >= SDL_NumJoysticks()) {
  27.663 -        SDL_JoystickGUID emptyGUID;
  27.664 -        SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
  27.665 -        SDL_zero(emptyGUID);
  27.666 -        return emptyGUID;
  27.667 +    SDL_JoystickDriver *driver;
  27.668 +    SDL_JoystickGUID guid;
  27.669 +
  27.670 +    SDL_LockJoysticks();
  27.671 +    if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
  27.672 +        guid = driver->GetDeviceGUID(device_index);
  27.673 +    } else {
  27.674 +        SDL_zero(guid);
  27.675      }
  27.676 -    return SDL_SYS_JoystickGetDeviceGUID(device_index);
  27.677 +    SDL_UnlockJoysticks();
  27.678 +
  27.679 +    return guid;
  27.680  }
  27.681  
  27.682  Uint16 SDL_JoystickGetDeviceVendor(int device_index)
  27.683 @@ -1150,11 +1360,33 @@
  27.684  
  27.685  SDL_JoystickID SDL_JoystickGetDeviceInstanceID(int device_index)
  27.686  {
  27.687 -    if (device_index < 0 || device_index >= SDL_NumJoysticks()) {
  27.688 -        SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
  27.689 -        return -1;
  27.690 +    SDL_JoystickDriver *driver;
  27.691 +    SDL_JoystickID instance_id = -1;
  27.692 +
  27.693 +    SDL_LockJoysticks();
  27.694 +    if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
  27.695 +        instance_id = driver->GetDeviceInstanceID(device_index);
  27.696      }
  27.697 -    return SDL_SYS_GetInstanceIdOfDeviceIndex(device_index);
  27.698 +    SDL_UnlockJoysticks();
  27.699 +
  27.700 +    return instance_id;
  27.701 +}
  27.702 +
  27.703 +int SDL_JoystickGetDeviceIndexFromInstanceID(SDL_JoystickID instance_id)
  27.704 +{
  27.705 +    int i, num_joysticks, device_index = -1;
  27.706 +
  27.707 +    SDL_LockJoysticks();
  27.708 +    num_joysticks = SDL_NumJoysticks();
  27.709 +    for (i = 0; i < num_joysticks; ++i) {
  27.710 +        if (SDL_JoystickGetDeviceInstanceID(i) == instance_id) {
  27.711 +            device_index = i;
  27.712 +            break;
  27.713 +        }
  27.714 +    }
  27.715 +    SDL_UnlockJoysticks();
  27.716 +
  27.717 +    return device_index;
  27.718  }
  27.719  
  27.720  SDL_JoystickGUID SDL_JoystickGetGUID(SDL_Joystick * joystick)
  27.721 @@ -1164,7 +1396,7 @@
  27.722          SDL_zero(emptyGUID);
  27.723          return emptyGUID;
  27.724      }
  27.725 -    return SDL_SYS_JoystickGetGUID(joystick);
  27.726 +    return joystick->guid;
  27.727  }
  27.728  
  27.729  Uint16 SDL_JoystickGetVendor(SDL_Joystick * joystick)
  27.730 @@ -1229,7 +1461,6 @@
  27.731      *pszGUID = '\0';
  27.732  }
  27.733  
  27.734 -
  27.735  /*-----------------------------------------------------------------------------
  27.736   * Purpose: Returns the 4 bit nibble for a hex character
  27.737   * Input  : c -
  27.738 @@ -1254,7 +1485,6 @@
  27.739      return 0;
  27.740  }
  27.741  
  27.742 -
  27.743  /* convert the string version of a joystick guid to the struct */
  27.744  SDL_JoystickGUID SDL_JoystickGetGUIDFromString(const char *pchGUID)
  27.745  {
  27.746 @@ -1277,19 +1507,17 @@
  27.747      return guid;
  27.748  }
  27.749  
  27.750 -
  27.751  /* update the power level for this joystick */
  27.752  void SDL_PrivateJoystickBatteryLevel(SDL_Joystick * joystick, SDL_JoystickPowerLevel ePowerLevel)
  27.753  {
  27.754      joystick->epowerlevel = ePowerLevel;
  27.755  }
  27.756  
  27.757 -
  27.758  /* return its power level */
  27.759  SDL_JoystickPowerLevel SDL_JoystickCurrentPowerLevel(SDL_Joystick * joystick)
  27.760  {
  27.761      if (!SDL_PrivateJoystickValid(joystick)) {
  27.762 -        return (SDL_JOYSTICK_POWER_UNKNOWN);
  27.763 +        return SDL_JOYSTICK_POWER_UNKNOWN;
  27.764      }
  27.765      return joystick->epowerlevel;
  27.766  }
    28.1 --- a/src/joystick/SDL_joystick_c.h	Wed Aug 08 15:39:20 2018 -0400
    28.2 +++ b/src/joystick/SDL_joystick_c.h	Thu Aug 09 16:00:17 2018 -0700
    28.3 @@ -23,19 +23,48 @@
    28.4  /* Useful functions and variables from SDL_joystick.c */
    28.5  #include "SDL_joystick.h"
    28.6  
    28.7 +struct _SDL_JoystickDriver;
    28.8 +
    28.9  /* Initialization and shutdown functions */
   28.10  extern int SDL_JoystickInit(void);
   28.11  extern void SDL_JoystickQuit(void);
   28.12  
   28.13 +/* Function to get the next available joystick instance ID */
   28.14 +extern SDL_JoystickID SDL_GetNextJoystickInstanceID(void);
   28.15 +
   28.16  /* Initialization and shutdown functions */
   28.17  extern int SDL_GameControllerInitMappings(void);
   28.18  extern void SDL_GameControllerQuitMappings(void);
   28.19  extern int SDL_GameControllerInit(void);
   28.20  extern void SDL_GameControllerQuit(void);
   28.21  
   28.22 +/* Function to get the joystick driver and device index for an API device index */
   28.23 +extern SDL_bool SDL_GetDriverAndJoystickIndex(int device_index, struct _SDL_JoystickDriver **driver, int *driver_index);
   28.24 +
   28.25 +/* Function to return the device index for a joystick ID, or -1 if not found */
   28.26 +extern int SDL_JoystickGetDeviceIndexFromInstanceID(SDL_JoystickID instance_id);
   28.27 +
   28.28  /* Function to extract information from an SDL joystick GUID */
   28.29  extern void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version);
   28.30  
   28.31 +/* Function to return whether a joystick is a PS4 controller */
   28.32 +extern SDL_bool SDL_IsJoystickPS4(Uint16 vendor_id, Uint16 product_id);
   28.33 +
   28.34 +/* Function to return whether a joystick is a Nintendo Switch Pro controller */
   28.35 +extern SDL_bool SDL_IsJoystickNintendoSwitchPro(Uint16 vendor_id, Uint16 product_id);
   28.36 +
   28.37 +/* Function to return whether a joystick is a Steam Controller */
   28.38 +extern SDL_bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id);
   28.39 +
   28.40 +/* Function to return whether a joystick is an Xbox 360 controller */
   28.41 +extern SDL_bool SDL_IsJoystickXbox360(Uint16 vendor_id, Uint16 product_id);
   28.42 +
   28.43 +/* Function to return whether a joystick is an Xbox One controller */
   28.44 +extern SDL_bool SDL_IsJoystickXboxOne(Uint16 vendor_id, Uint16 product_id);
   28.45 +
   28.46 +/* Function to return whether a joystick should be ignored */
   28.47 +extern SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid);
   28.48 +
   28.49  /* Function to return whether a joystick name and GUID is a game controller  */
   28.50  extern SDL_bool SDL_IsGameControllerNameAndGUID(const char *name, SDL_JoystickGUID guid);
   28.51  
   28.52 @@ -46,7 +75,7 @@
   28.53  extern void SDL_GameControllerHandleDelayedGuideButton(SDL_Joystick *joystick);
   28.54  
   28.55  /* Internal event queueing functions */
   28.56 -extern void SDL_PrivateJoystickAdded(int device_index);
   28.57 +extern void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance);
   28.58  extern void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance);
   28.59  extern int SDL_PrivateJoystickAxis(SDL_Joystick * joystick,
   28.60                                     Uint8 axis, Sint16 value);
    29.1 --- a/src/joystick/SDL_sysjoystick.h	Wed Aug 08 15:39:20 2018 -0400
    29.2 +++ b/src/joystick/SDL_sysjoystick.h	Thu Aug 09 16:00:17 2018 -0700
    29.3 @@ -42,6 +42,7 @@
    29.4  {
    29.5      SDL_JoystickID instance_id; /* Device instance, monotonically increasing from 0 */
    29.6      char *name;                 /* Joystick name - system dependent */
    29.7 +    SDL_JoystickGUID guid;      /* Joystick guid */
    29.8  
    29.9      int naxes;                  /* Number of axis controls on the joystick */
   29.10      SDL_JoystickAxisInfo *axes;
   29.11 @@ -58,69 +59,95 @@
   29.12      int nbuttons;               /* Number of buttons on the joystick */
   29.13      Uint8 *buttons;             /* Current button states */
   29.14  
   29.15 +    SDL_bool is_game_controller;
   29.16 +    SDL_bool delayed_guide_button; /* SDL_TRUE if this device has the guide button event delayed */
   29.17 +    SDL_bool force_recentering; /* SDL_TRUE if this device needs to have its state reset to 0 */
   29.18 +    SDL_JoystickPowerLevel epowerlevel; /* power level of this joystick, SDL_JOYSTICK_POWER_UNKNOWN if not supported */
   29.19 +    struct _SDL_JoystickDriver *driver;
   29.20 +
   29.21      struct joystick_hwdata *hwdata;     /* Driver dependent information */
   29.22  
   29.23      int ref_count;              /* Reference count for multiple opens */
   29.24  
   29.25 -    SDL_bool is_game_controller;
   29.26 -    SDL_bool delayed_guide_button; /* SDL_TRUE if this device has the guide button event delayed */
   29.27 -    SDL_bool force_recentering; /* SDL_TRUE if this device needs to have its state reset to 0 */
   29.28 -    SDL_JoystickPowerLevel epowerlevel; /* power level of this joystick, SDL_JOYSTICK_POWER_UNKNOWN if not supported */
   29.29      struct _SDL_Joystick *next; /* pointer to next joystick we have allocated */
   29.30  };
   29.31  
   29.32 +#if defined(__IPHONEOS__) || defined(__ANDROID__)
   29.33 +#define HAVE_STEAMCONTROLLERS
   29.34 +#define USE_STEAMCONTROLLER_HIDAPI
   29.35 +#elif defined(__LINUX__)
   29.36 +#define HAVE_STEAMCONTROLLERS
   29.37 +#define USE_STEAMCONTROLLER_LINUX
   29.38 +#endif
   29.39 +
   29.40 +/* Device bus definitions */
   29.41 +#define SDL_HARDWARE_BUS_USB        0x03
   29.42 +#define SDL_HARDWARE_BUS_BLUETOOTH  0x05
   29.43 +
   29.44  /* Macro to combine a USB vendor ID and product ID into a single Uint32 value */
   29.45  #define MAKE_VIDPID(VID, PID)   (((Uint32)(VID))<<16|(PID))
   29.46  
   29.47 -/* Function to scan the system for joysticks.
   29.48 - * Joystick 0 should be the system default joystick.
   29.49 - * This function should return the number of available joysticks, or -1
   29.50 - * on an unrecoverable fatal error.
   29.51 - */
   29.52 -extern int SDL_SYS_JoystickInit(void);
   29.53 +typedef struct _SDL_JoystickDriver
   29.54 +{
   29.55 +    /* Function to scan the system for joysticks.
   29.56 +     * Joystick 0 should be the system default joystick.
   29.57 +     * This function should return 0, or -1 on an unrecoverable error.
   29.58 +     */
   29.59 +    int (*Init)(void);
   29.60  
   29.61 -/* Function to return the number of joystick devices plugged in right now */
   29.62 -extern int SDL_SYS_NumJoysticks(void);
   29.63 +    /* Function to return the number of joystick devices plugged in right now */
   29.64 +    int (*GetCount)(void);
   29.65  
   29.66 -/* Function to cause any queued joystick insertions to be processed */
   29.67 -extern void SDL_SYS_JoystickDetect(void);
   29.68 +    /* Function to cause any queued joystick insertions to be processed */
   29.69 +    void (*Detect)(void);
   29.70  
   29.71 -/* Function to get the device-dependent name of a joystick */
   29.72 -extern const char *SDL_SYS_JoystickNameForDeviceIndex(int device_index);
   29.73 +    /* Function to get the device-dependent name of a joystick */
   29.74 +    const char *(*GetDeviceName)(int device_index);
   29.75  
   29.76 -/* Function to get the current instance id of the joystick located at device_index */
   29.77 -extern SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index);
   29.78 +    /* Function to return the stable GUID for a plugged in device */
   29.79 +    SDL_JoystickGUID (*GetDeviceGUID)(int device_index);
   29.80  
   29.81 -/* Function to open a joystick for use.
   29.82 -   The joystick to open is specified by the device index.
   29.83 -   This should fill the nbuttons and naxes fields of the joystick structure.
   29.84 -   It returns 0, or -1 if there is an error.
   29.85 - */
   29.86 -extern int SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index);
   29.87 +    /* Function to get the current instance id of the joystick located at device_index */
   29.88 +    SDL_JoystickID (*GetDeviceInstanceID)(int device_index);
   29.89  
   29.90 -/* Function to query if the joystick is currently attached
   29.91 - * It returns SDL_TRUE if attached, SDL_FALSE otherwise.
   29.92 - */
   29.93 -extern SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick * joystick);
   29.94 +    /* Function to open a joystick for use.
   29.95 +       The joystick to open is specified by the device index.
   29.96 +       This should fill the nbuttons and naxes fields of the joystick structure.
   29.97 +       It returns 0, or -1 if there is an error.
   29.98 +     */
   29.99 +    int (*Open)(SDL_Joystick * joystick, int device_index);
  29.100  
  29.101 -/* Function to update the state of a joystick - called as a device poll.
  29.102 - * This function shouldn't update the joystick structure directly,
  29.103 - * but instead should call SDL_PrivateJoystick*() to deliver events
  29.104 - * and update joystick device state.
  29.105 - */
  29.106 -extern void SDL_SYS_JoystickUpdate(SDL_Joystick * joystick);
  29.107 +    /* Function to query if the joystick is currently attached
  29.108 +     * It returns SDL_TRUE if attached, SDL_FALSE otherwise.
  29.109 +     */
  29.110 +    SDL_bool (*IsAttached)(SDL_Joystick * joystick);
  29.111  
  29.112 -/* Function to close a joystick after use */
  29.113 -extern void SDL_SYS_JoystickClose(SDL_Joystick * joystick);
  29.114 +    /* Rumble functionality */
  29.115 +    int (*Rumble)(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
  29.116  
  29.117 -/* Function to perform any system-specific joystick related cleanup */
  29.118 -extern void SDL_SYS_JoystickQuit(void);
  29.119 +    /* Function to update the state of a joystick - called as a device poll.
  29.120 +     * This function shouldn't update the joystick structure directly,
  29.121 +     * but instead should call SDL_PrivateJoystick*() to deliver events
  29.122 +     * and update joystick device state.
  29.123 +     */
  29.124 +    void (*Update)(SDL_Joystick * joystick);
  29.125  
  29.126 -/* Function to return the stable GUID for a plugged in device */
  29.127 -extern SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index);
  29.128 +    /* Function to close a joystick after use */
  29.129 +    void (*Close)(SDL_Joystick * joystick);
  29.130  
  29.131 -/* Function to return the stable GUID for a opened joystick */
  29.132 -extern SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick);
  29.133 +    /* Function to perform any system-specific joystick related cleanup */
  29.134 +    void (*Quit)(void);
  29.135 +
  29.136 +} SDL_JoystickDriver;
  29.137 +
  29.138 +/* The available joystick drivers */
  29.139 +extern SDL_JoystickDriver SDL_ANDROID_JoystickDriver;
  29.140 +extern SDL_JoystickDriver SDL_DARWIN_JoystickDriver;
  29.141 +extern SDL_JoystickDriver SDL_DUMMY_JoystickDriver;
  29.142 +extern SDL_JoystickDriver SDL_HIDAPI_JoystickDriver;
  29.143 +extern SDL_JoystickDriver SDL_IOS_JoystickDriver;
  29.144 +extern SDL_JoystickDriver SDL_LINUX_JoystickDriver;
  29.145 +extern SDL_JoystickDriver SDL_WINDOWS_JoystickDriver;
  29.146  
  29.147  #endif /* SDL_sysjoystick_h_ */
  29.148  
    30.1 --- a/src/joystick/android/SDL_sysjoystick.c	Wed Aug 08 15:39:20 2018 -0400
    30.2 +++ b/src/joystick/android/SDL_sysjoystick.c	Thu Aug 09 16:00:17 2018 -0700
    30.3 @@ -36,7 +36,7 @@
    30.4  #include "../SDL_joystick_c.h"
    30.5  #include "../../events/SDL_keyboard_c.h"
    30.6  #include "../../core/android/SDL_android.h"
    30.7 -#include "../steam/SDL_steamcontroller.h"
    30.8 +#include "../hidapi/SDL_hidapijoystick_c.h"
    30.9  
   30.10  #include "android/keycodes.h"
   30.11  
   30.12 @@ -69,7 +69,6 @@
   30.13  static SDL_joylist_item *SDL_joylist = NULL;
   30.14  static SDL_joylist_item *SDL_joylist_tail = NULL;
   30.15  static int numjoysticks = 0;
   30.16 -static int instance_counter = 0;
   30.17  
   30.18  
   30.19  /* Public domain CRC implementation adapted from:
   30.20 @@ -326,7 +325,6 @@
   30.21  int
   30.22  Android_AddJoystick(int device_id, const char *name, const char *desc, int vendor_id, int product_id, SDL_bool is_accelerometer, int button_mask, int naxes, int nhats, int nballs)
   30.23  {
   30.24 -    const Uint16 BUS_BLUETOOTH = 0x05;
   30.25      SDL_joylist_item *item;
   30.26      SDL_JoystickGUID guid;
   30.27      Uint16 *guid16 = (Uint16 *)guid.data;
   30.28 @@ -344,7 +342,14 @@
   30.29      if (JoystickByDeviceId(device_id) != NULL || name == NULL) {
   30.30          return -1;
   30.31      }
   30.32 -    
   30.33 +
   30.34 +#ifdef SDL_JOYSTICK_HIDAPI
   30.35 +    if (HIDAPI_IsDevicePresent(vendor_id, product_id)) {
   30.36 +        /* The HIDAPI driver is taking care of this device */
   30.37 +        return -1;
   30.38 +    }
   30.39 +#endif
   30.40 +
   30.41  #ifdef DEBUG_JOYSTICK
   30.42      SDL_Log("Joystick: %s, descriptor %s, vendor = 0x%.4x, product = 0x%.4x, %d axes, %d hats\n", name, desc, vendor_id, product_id, naxes, nhats);
   30.43  #endif
   30.44 @@ -378,7 +383,7 @@
   30.45  
   30.46      /* We only need 16 bits for each of these; space them out to fill 128. */
   30.47      /* Byteswap so devices get same GUID on little/big endian platforms. */
   30.48 -    *guid16++ = SDL_SwapLE16(BUS_BLUETOOTH);
   30.49 +    *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
   30.50      *guid16++ = 0;
   30.51  
   30.52      if (vendor_id && product_id) {
   30.53 @@ -424,7 +429,7 @@
   30.54      item->naxes = naxes;
   30.55      item->nhats = nhats;
   30.56      item->nballs = nballs;
   30.57 -    item->device_instance = instance_counter++;
   30.58 +    item->device_instance = SDL_GetNextJoystickInstanceID();
   30.59      if (SDL_joylist_tail == NULL) {
   30.60          SDL_joylist = SDL_joylist_tail = item;
   30.61      } else {
   30.62 @@ -435,7 +440,7 @@
   30.63      /* Need to increment the joystick count before we post the event */
   30.64      ++numjoysticks;
   30.65  
   30.66 -    SDL_PrivateJoystickAdded(numjoysticks - 1);
   30.67 +    SDL_PrivateJoystickAdded(item->device_instance);
   30.68  
   30.69  #ifdef DEBUG_JOYSTICK
   30.70      SDL_Log("Added joystick %s with device_id %d", name, device_id);
   30.71 @@ -492,104 +497,29 @@
   30.72  }
   30.73  
   30.74  
   30.75 -static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickGUID guid, int *device_instance)
   30.76 +static void ANDROID_JoystickDetect();
   30.77 +
   30.78 +static int
   30.79 +ANDROID_JoystickInit(void)
   30.80  {
   30.81 -    SDL_joylist_item *item;
   30.82 -    
   30.83 -    item = (SDL_joylist_item *)SDL_calloc(1, sizeof (SDL_joylist_item));
   30.84 -    if (item == NULL) {
   30.85 -        return SDL_FALSE;
   30.86 -    }
   30.87 -
   30.88 -    *device_instance = item->device_instance = instance_counter++;
   30.89 -    item->device_id = -1;
   30.90 -    item->name = SDL_strdup(name);
   30.91 -    item->guid = guid;
   30.92 -    SDL_GetSteamControllerInputs(&item->nbuttons,
   30.93 -                                 &item->naxes,
   30.94 -                                 &item->nhats);
   30.95 -    item->m_bSteamController = SDL_TRUE;
   30.96 -
   30.97 -    if (SDL_joylist_tail == NULL) {
   30.98 -        SDL_joylist = SDL_joylist_tail = item;
   30.99 -    } else {
  30.100 -        SDL_joylist_tail->next = item;
  30.101 -        SDL_joylist_tail = item;
  30.102 -    }
  30.103 -
  30.104 -    /* Need to increment the joystick count before we post the event */
  30.105 -    ++numjoysticks;
  30.106 -
  30.107 -    SDL_PrivateJoystickAdded(numjoysticks - 1);
  30.108 -
  30.109 -    return SDL_TRUE;
  30.110 -}
  30.111 -
  30.112 -static void SteamControllerDisconnectedCallback(int device_instance)
  30.113 -{
  30.114 -    SDL_joylist_item *item = SDL_joylist;
  30.115 -    SDL_joylist_item *prev = NULL;
  30.116 -    
  30.117 -    while (item != NULL) {
  30.118 -        if (item->device_instance == device_instance) {
  30.119 -            break;
  30.120 -        }
  30.121 -        prev = item;
  30.122 -        item = item->next;
  30.123 -    }
  30.124 -    
  30.125 -    if (item == NULL) {
  30.126 -        return;
  30.127 -    }
  30.128 -
  30.129 -    if (item->joystick) {
  30.130 -        item->joystick->hwdata = NULL;
  30.131 -    }
  30.132 -        
  30.133 -    if (prev != NULL) {
  30.134 -        prev->next = item->next;
  30.135 -    } else {
  30.136 -        SDL_assert(SDL_joylist == item);
  30.137 -        SDL_joylist = item->next;
  30.138 -    }
  30.139 -    if (item == SDL_joylist_tail) {
  30.140 -        SDL_joylist_tail = prev;
  30.141 -    }
  30.142 -
  30.143 -    /* Need to decrement the joystick count before we post the event */
  30.144 -    --numjoysticks;
  30.145 -
  30.146 -    SDL_PrivateJoystickRemoved(item->device_instance);
  30.147 -
  30.148 -    SDL_free(item->name);
  30.149 -    SDL_free(item);
  30.150 -}
  30.151 -
  30.152 -int
  30.153 -SDL_SYS_JoystickInit(void)
  30.154 -{
  30.155 -    SDL_SYS_JoystickDetect();
  30.156 +    ANDROID_JoystickDetect();
  30.157      
  30.158      if (SDL_GetHintBoolean(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, SDL_TRUE)) {
  30.159          /* Default behavior, accelerometer as joystick */
  30.160          Android_AddJoystick(ANDROID_ACCELEROMETER_DEVICE_ID, ANDROID_ACCELEROMETER_NAME, ANDROID_ACCELEROMETER_NAME, 0, 0, SDL_TRUE, 0, 3, 0, 0);
  30.161      }
  30.162 -   
  30.163 -    SDL_InitSteamControllers(SteamControllerConnectedCallback,
  30.164 -                             SteamControllerDisconnectedCallback);
  30.165 -
  30.166 -    return (numjoysticks);
  30.167 +    return 0;
  30.168  
  30.169  }
  30.170  
  30.171 -int
  30.172 -SDL_SYS_NumJoysticks(void)
  30.173 +static int
  30.174 +ANDROID_JoystickGetCount(void)
  30.175  {
  30.176      return numjoysticks;
  30.177  }
  30.178  
  30.179 -void
  30.180 -SDL_SYS_JoystickDetect(void)
  30.181 +static void
  30.182 +ANDROID_JoystickDetect(void)
  30.183  {
  30.184      /* Support for device connect/disconnect is API >= 16 only,
  30.185       * so we poll every three seconds
  30.186 @@ -600,8 +530,6 @@
  30.187          timeout = SDL_GetTicks() + 3000;
  30.188          Android_JNI_PollInputDevices();
  30.189      }
  30.190 -
  30.191 -    SDL_UpdateSteamControllers();
  30.192  }
  30.193  
  30.194  static SDL_joylist_item *
  30.195 @@ -635,7 +563,7 @@
  30.196      }
  30.197      
  30.198      /* Joystick not found, try adding it */
  30.199 -    SDL_SYS_JoystickDetect();
  30.200 +    ANDROID_JoystickDetect();
  30.201      
  30.202      while (item != NULL) {
  30.203          if (item->device_id == device_id) {
  30.204 @@ -647,26 +575,26 @@
  30.205      return NULL;
  30.206  }
  30.207  
  30.208 -/* Function to get the device-dependent name of a joystick */
  30.209 -const char *
  30.210 -SDL_SYS_JoystickNameForDeviceIndex(int device_index)
  30.211 +static const char *
  30.212 +ANDROID_JoystickGetDeviceName(int device_index)
  30.213  {
  30.214      return JoystickByDevIndex(device_index)->name;
  30.215  }
  30.216  
  30.217 -/* Function to perform the mapping from device index to the instance id for this index */
  30.218 -SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
  30.219 +static SDL_JoystickGUID
  30.220 +ANDROID_JoystickGetDeviceGUID(int device_index)
  30.221 +{
  30.222 +    return JoystickByDevIndex(device_index)->guid;
  30.223 +}
  30.224 +
  30.225 +static SDL_JoystickID
  30.226 +ANDROID_JoystickGetDeviceInstanceID(int device_index)
  30.227  {
  30.228      return JoystickByDevIndex(device_index)->device_instance;
  30.229  }
  30.230  
  30.231 -/* Function to open a joystick for use.
  30.232 -   The joystick to open is specified by the device index.
  30.233 -   This should fill the nbuttons and naxes fields of the joystick structure.
  30.234 -   It returns 0, or -1 if there is an error.
  30.235 - */
  30.236 -int
  30.237 -SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
  30.238 +static int
  30.239 +ANDROID_JoystickOpen(SDL_Joystick * joystick, int device_index)
  30.240  {
  30.241      SDL_joylist_item *item = JoystickByDevIndex(device_index);
  30.242  
  30.243 @@ -689,14 +617,20 @@
  30.244      return (0);
  30.245  }
  30.246  
  30.247 -/* Function to determine if this joystick is attached to the system right now */
  30.248 -SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
  30.249 +static SDL_bool
  30.250 +ANDROID_JoystickIsAttached(SDL_Joystick *joystick)
  30.251  {
  30.252      return joystick->hwdata != NULL;
  30.253  }
  30.254  
  30.255 -void
  30.256 -SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
  30.257 +static int
  30.258 +ANDROID_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
  30.259 +{
  30.260 +    return SDL_Unsupported();
  30.261 +}
  30.262 +
  30.263 +static void
  30.264 +ANDROID_JoystickUpdate(SDL_Joystick * joystick)
  30.265  {
  30.266      SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
  30.267  
  30.268 @@ -704,11 +638,6 @@
  30.269          return;
  30.270      }
  30.271   
  30.272 -    if (item->m_bSteamController) {
  30.273 -        SDL_UpdateSteamController(joystick);
  30.274 -        return;
  30.275 -    }
  30.276 -
  30.277      if (item->is_accelerometer) {
  30.278          int i;
  30.279          Sint16 value;
  30.280 @@ -729,9 +658,8 @@
  30.281      }
  30.282  }
  30.283  
  30.284 -/* Function to close a joystick after use */
  30.285 -void
  30.286 -SDL_SYS_JoystickClose(SDL_Joystick * joystick)
  30.287 +static void
  30.288 +ANDROID_JoystickClose(SDL_Joystick * joystick)
  30.289  {
  30.290      SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
  30.291      if (item) {
  30.292 @@ -739,9 +667,8 @@
  30.293      }
  30.294  }
  30.295  
  30.296 -/* Function to perform any system-specific joystick related cleanup */
  30.297 -void
  30.298 -SDL_SYS_JoystickQuit(void)
  30.299 +static void
  30.300 +ANDROID_JoystickQuit(void)
  30.301  {
  30.302  /* We don't have any way to scan for joysticks at init, so don't wipe the list
  30.303   * of joysticks here in case this is a reinit.
  30.304 @@ -759,28 +686,24 @@
  30.305      SDL_joylist = SDL_joylist_tail = NULL;
  30.306  
  30.307      numjoysticks = 0;
  30.308 -    instance_counter = 0;
  30.309  #endif /* 0 */
  30.310 -
  30.311 -    SDL_QuitSteamControllers();
  30.312  }
  30.313  
  30.314 -SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index)
  30.315 +SDL_JoystickDriver SDL_ANDROID_JoystickDriver =
  30.316  {
  30.317 -    return JoystickByDevIndex(device_index)->guid;
  30.318 -}
  30.319 -
  30.320 -SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
  30.321 -{
  30.322 -    SDL_JoystickGUID guid;
  30.323 -    
  30.324 -    if (joystick->hwdata != NULL) {
  30.325 -        return ((SDL_joylist_item*)joystick->hwdata)->guid;
  30.326 -    }
  30.327 -    
  30.328 -    SDL_zero(guid);
  30.329 -    return guid;
  30.330 -}
  30.331 +    ANDROID_JoystickInit,
  30.332 +    ANDROID_JoystickGetCount,
  30.333 +    ANDROID_JoystickDetect,
  30.334 +    ANDROID_JoystickGetDeviceName,
  30.335 +    ANDROID_JoystickGetDeviceGUID,
  30.336 +    ANDROID_JoystickGetDeviceInstanceID,
  30.337 +    ANDROID_JoystickOpen,
  30.338 +    ANDROID_JoystickIsAttached,
  30.339 +    ANDROID_JoystickRumble,
  30.340 +    ANDROID_JoystickUpdate,
  30.341 +    ANDROID_JoystickClose,
  30.342 +    ANDROID_JoystickQuit,
  30.343 +};
  30.344  
  30.345  #endif /* SDL_JOYSTICK_ANDROID */
  30.346  
    31.1 --- a/src/joystick/android/SDL_sysjoystick_c.h	Wed Aug 08 15:39:20 2018 -0400
    31.2 +++ b/src/joystick/android/SDL_sysjoystick_c.h	Thu Aug 09 16:00:17 2018 -0700
    31.3 @@ -47,9 +47,6 @@
    31.4      int nbuttons, naxes, nhats, nballs;
    31.5      int dpad_state;
    31.6      
    31.7 -    /* Steam Controller support */
    31.8 -    SDL_bool m_bSteamController;
    31.9 -
   31.10      struct SDL_joylist_item *next;
   31.11  } SDL_joylist_item;
   31.12  
    32.1 --- a/src/joystick/darwin/SDL_sysjoystick.c	Wed Aug 08 15:39:20 2018 -0400
    32.2 +++ b/src/joystick/darwin/SDL_sysjoystick.c	Thu Aug 09 16:00:17 2018 -0700
    32.3 @@ -22,29 +22,79 @@
    32.4  
    32.5  #ifdef SDL_JOYSTICK_IOKIT
    32.6  
    32.7 -#include <IOKit/hid/IOHIDLib.h>
    32.8 -
    32.9 -/* For force feedback testing. */
   32.10 -#include <ForceFeedback/ForceFeedback.h>
   32.11 -#include <ForceFeedback/ForceFeedbackConstants.h>
   32.12 -
   32.13 +#include "SDL_events.h"
   32.14  #include "SDL_joystick.h"
   32.15  #include "../SDL_sysjoystick.h"
   32.16  #include "../SDL_joystick_c.h"
   32.17  #include "SDL_sysjoystick_c.h"
   32.18 -#include "SDL_events.h"
   32.19 +#include "../hidapi/SDL_hidapijoystick_c.h"
   32.20  #include "../../haptic/darwin/SDL_syshaptic_c.h"    /* For haptic hot plugging */
   32.21  
   32.22 +
   32.23  #define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick")
   32.24  
   32.25 +#define CONVERT_MAGNITUDE(x)    (((x)*10000) / 0x7FFF)
   32.26 +
   32.27  /* The base object of the HID Manager API */
   32.28  static IOHIDManagerRef hidman = NULL;
   32.29  
   32.30  /* Linked list of all available devices */
   32.31  static recDevice *gpDeviceList = NULL;
   32.32  
   32.33 -/* static incrementing counter for new joystick devices seen on the system. Devices should start with index 0 */
   32.34 -static int s_joystick_instance_id = -1;
   32.35 +void FreeRumbleEffectData(FFEFFECT *effect)
   32.36 +{
   32.37 +    if (!effect) {
   32.38 +        return;
   32.39 +    }
   32.40 +    SDL_free(effect->rgdwAxes);
   32.41 +    SDL_free(effect->rglDirection);
   32.42 +    SDL_free(effect->lpvTypeSpecificParams);
   32.43 +    SDL_free(effect);
   32.44 +}
   32.45 +
   32.46 +FFEFFECT *CreateRumbleEffectData(Sint16 magnitude, Uint32 duration_ms)
   32.47 +{
   32.48 +    FFEFFECT *effect;
   32.49 +    FFPERIODIC *periodic;
   32.50 +
   32.51 +    /* Create the effect */
   32.52 +    effect = (FFEFFECT *)SDL_calloc(1, sizeof(*effect));
   32.53 +    if (!effect) {
   32.54 +        return NULL;
   32.55 +    }
   32.56 +    effect->dwSize = sizeof(*effect);
   32.57 +    effect->dwGain = 10000;
   32.58 +    effect->dwFlags = FFEFF_OBJECTOFFSETS;
   32.59 +    effect->dwDuration = duration_ms * 1000; /* In microseconds. */
   32.60 +    effect->dwTriggerButton = FFEB_NOTRIGGER;
   32.61 +
   32.62 +    effect->cAxes = 2;
   32.63 +    effect->rgdwAxes = (DWORD *)SDL_calloc(effect->cAxes, sizeof(DWORD));
   32.64 +    if (!effect->rgdwAxes) {
   32.65 +        FreeRumbleEffectData(effect);
   32.66 +        return NULL;
   32.67 +    }
   32.68 +
   32.69 +    effect->rglDirection = (LONG *)SDL_calloc(effect->cAxes, sizeof(LONG));
   32.70 +    if (!effect->rglDirection) {
   32.71 +        FreeRumbleEffectData(effect);
   32.72 +        return NULL;
   32.73 +    }
   32.74 +    effect->dwFlags |= FFEFF_CARTESIAN;
   32.75 +
   32.76 +    periodic = (FFPERIODIC *)SDL_calloc(1, sizeof(*periodic));
   32.77 +    if (!periodic) {
   32.78 +        FreeRumbleEffectData(effect);
   32.79 +        return NULL;
   32.80 +    }
   32.81 +    periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
   32.82 +    periodic->dwPeriod = 1000000;
   32.83 +
   32.84 +    effect->cbTypeSpecificParams = sizeof(*periodic);
   32.85 +    effect->lpvTypeSpecificParams = periodic;
   32.86 +
   32.87 +    return effect;
   32.88 +}
   32.89  
   32.90  static recDevice *GetDeviceForIndex(int device_index)
   32.91  {
   32.92 @@ -157,6 +207,19 @@
   32.93      recDevice *device = (recDevice *) ctx;
   32.94      device->removed = SDL_TRUE;
   32.95      device->deviceRef = NULL; // deviceRef was invalidated due to the remove
   32.96 +    if (device->ffeffect_ref) {
   32.97 +        FFDeviceReleaseEffect(device->ffdevice, device->ffeffect_ref);
   32.98 +        device->ffeffect_ref = NULL;
   32.99 +    }
  32.100 +    if (device->ffeffect) {
  32.101 +        FreeRumbleEffectData(device->ffeffect);
  32.102 +        device->ffeffect = NULL;
  32.103 +    }
  32.104 +    if (device->ffdevice) {
  32.105 +        FFReleaseDevice(device->ffdevice);
  32.106 +        device->ffdevice = NULL;
  32.107 +        device->ff_initialized = SDL_FALSE;
  32.108 +    }
  32.109  #if SDL_HAPTIC_IOKIT
  32.110      MacHaptic_MaybeRemoveDevice(device->ffservice);
  32.111  #endif
  32.112 @@ -333,8 +396,6 @@
  32.113  static SDL_bool
  32.114  GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
  32.115  {
  32.116 -    const Uint16 BUS_USB = 0x03;
  32.117 -    const Uint16 BUS_BLUETOOTH = 0x05;
  32.118      Sint32 vendor = 0;
  32.119      Sint32 product = 0;
  32.120      Sint32 version = 0;
  32.121 @@ -389,10 +450,17 @@
  32.122          CFNumberGetValue(refCF, kCFNumberSInt32Type, &version);
  32.123      }
  32.124  
  32.125 +#ifdef SDL_JOYSTICK_HIDAPI
  32.126 +    if (HIDAPI_IsDevicePresent(vendor, product)) {
  32.127 +        /* The HIDAPI driver is taking care of this device */
  32.128 +        return 0;
  32.129 +    }
  32.130 +#endif
  32.131 +
  32.132      SDL_memset(pDevice->guid.data, 0, sizeof(pDevice->guid.data));
  32.133  
  32.134      if (vendor && product) {
  32.135 -        *guid16++ = SDL_SwapLE16(BUS_USB);
  32.136 +        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
  32.137          *guid16++ = 0;
  32.138          *guid16++ = SDL_SwapLE16((Uint16)vendor);
  32.139          *guid16++ = 0;
  32.140 @@ -401,7 +469,7 @@
  32.141          *guid16++ = SDL_SwapLE16((Uint16)version);
  32.142          *guid16++ = 0;
  32.143      } else {
  32.144 -        *guid16++ = SDL_SwapLE16(BUS_BLUETOOTH);
  32.145 +        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
  32.146          *guid16++ = 0;
  32.147          SDL_strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4);
  32.148      }
  32.149 @@ -444,7 +512,6 @@
  32.150      }
  32.151  
  32.152      device = (recDevice *) SDL_calloc(1, sizeof(recDevice));
  32.153 -
  32.154      if (!device) {
  32.155          SDL_OutOfMemory();
  32.156          return;
  32.157 @@ -455,8 +522,7 @@
  32.158          return;   /* not a device we care about, probably. */
  32.159      }
  32.160  
  32.161 -    if (SDL_IsGameControllerNameAndGUID(device->product, device->guid) &&
  32.162 -        SDL_ShouldIgnoreGameController(device->product, device->guid)) {
  32.163 +    if (SDL_ShouldIgnoreJoystick(device->product, device->guid)) {
  32.164          SDL_free(device);
  32.165          return;
  32.166      }
  32.167 @@ -466,16 +532,16 @@
  32.168      IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
  32.169  
  32.170      /* Allocate an instance ID for this device */
  32.171 -    device->instance_id = ++s_joystick_instance_id;
  32.172 +    device->instance_id = SDL_GetNextJoystickInstanceID();
  32.173  
  32.174      /* We have to do some storage of the io_service_t for SDL_HapticOpenFromJoystick */
  32.175      ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
  32.176 -#if SDL_HAPTIC_IOKIT
  32.177      if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
  32.178          device->ffservice = ioservice;
  32.179 +#if SDL_HAPTIC_IOKIT
  32.180          MacHaptic_MaybeAddDevice(ioservice);
  32.181 +#endif
  32.182      }
  32.183 -#endif
  32.184  
  32.185      /* Add device to the end of the list */
  32.186      if ( !gpDeviceList ) {
  32.187 @@ -492,7 +558,7 @@
  32.188          ++device_index;  /* bump by one since we counted by pNext. */
  32.189      }
  32.190  
  32.191 -    SDL_PrivateJoystickAdded(device_index);
  32.192 +    SDL_PrivateJoystickAdded(device->instance_id);
  32.193  }
  32.194  
  32.195  static SDL_bool
  32.196 @@ -577,13 +643,8 @@
  32.197  }
  32.198  
  32.199  
  32.200 -/* Function to scan the system for joysticks.
  32.201 - * Joystick 0 should be the system default joystick.
  32.202 - * This function should return the number of available joysticks, or -1
  32.203 - * on an unrecoverable fatal error.
  32.204 - */
  32.205 -int
  32.206 -SDL_SYS_JoystickInit(void)
  32.207 +static int
  32.208 +DARWIN_JoystickInit(void)
  32.209  {
  32.210      if (gpDeviceList) {
  32.211          return SDL_SetError("Joystick: Device list already inited.");
  32.212 @@ -593,12 +654,11 @@
  32.213          return SDL_SetError("Joystick: Couldn't initialize HID Manager");
  32.214      }
  32.215  
  32.216 -    return SDL_SYS_NumJoysticks();
  32.217 +    return 0;
  32.218  }
  32.219  
  32.220 -/* Function to return the number of joystick devices plugged in right now */
  32.221 -int
  32.222 -SDL_SYS_NumJoysticks(void)
  32.223 +static int
  32.224 +DARWIN_JoystickGetCount(void)
  32.225  {
  32.226      recDevice *device = gpDeviceList;
  32.227      int nJoySticks = 0;
  32.228 @@ -613,10 +673,8 @@
  32.229      return nJoySticks;
  32.230  }
  32.231  
  32.232 -/* Function to cause any queued joystick insertions to be processed
  32.233 - */
  32.234 -void
  32.235 -SDL_SYS_JoystickDetect(void)
  32.236 +static void
  32.237 +DARWIN_JoystickDetect(void)
  32.238  {
  32.239      recDevice *device = gpDeviceList;
  32.240      while (device) {
  32.241 @@ -627,37 +685,43 @@
  32.242          }
  32.243      }
  32.244  
  32.245 -	/* run this after the checks above so we don't set device->removed and delete the device before
  32.246 -	   SDL_SYS_JoystickUpdate can run to clean up the SDL_Joystick object that owns this device */
  32.247 -	while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
  32.248 -		/* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
  32.249 -	}
  32.250 +    /* run this after the checks above so we don't set device->removed and delete the device before
  32.251 +       DARWIN_JoystickUpdate can run to clean up the SDL_Joystick object that owns this device */
  32.252 +    while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
  32.253 +        /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
  32.254 +    }
  32.255  }
  32.256  
  32.257  /* Function to get the device-dependent name of a joystick */
  32.258  const char *
  32.259 -SDL_SYS_JoystickNameForDeviceIndex(int device_index)
  32.260 +DARWIN_JoystickGetDeviceName(int device_index)
  32.261  {
  32.262      recDevice *device = GetDeviceForIndex(device_index);
  32.263      return device ? device->product : "UNKNOWN";
  32.264  }
  32.265  
  32.266 -/* Function to return the instance id of the joystick at device_index
  32.267 - */
  32.268 -SDL_JoystickID
  32.269 -SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
  32.270 +static SDL_JoystickGUID
  32.271 +DARWIN_JoystickGetDeviceGUID( int device_index )
  32.272 +{
  32.273 +    recDevice *device = GetDeviceForIndex(device_index);
  32.274 +    SDL_JoystickGUID guid;
  32.275 +    if (device) {
  32.276 +        guid = device->guid;
  32.277 +    } else {
  32.278 +        SDL_zero(guid);
  32.279 +    }
  32.280 +    return guid;
  32.281 +}
  32.282 +
  32.283 +static SDL_JoystickID
  32.284 +DARWIN_JoystickGetDeviceInstanceID(int device_index)
  32.285  {
  32.286      recDevice *device = GetDeviceForIndex(device_index);
  32.287      return device ? device->instance_id : 0;
  32.288  }
  32.289  
  32.290 -/* Function to open a joystick for use.
  32.291 - * The joystick to open is specified by the device index.
  32.292 - * This should fill the nbuttons and naxes fields of the joystick structure.
  32.293 - * It returns 0, or -1 if there is an error.
  32.294 - */
  32.295 -int
  32.296 -SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
  32.297 +static int
  32.298 +DARWIN_JoystickOpen(SDL_Joystick * joystick, int device_index)
  32.299  {
  32.300      recDevice *device = GetDeviceForIndex(device_index);
  32.301  
  32.302 @@ -672,22 +736,144 @@
  32.303      return 0;
  32.304  }
  32.305  
  32.306 -/* Function to query if the joystick is currently attached
  32.307 - * It returns SDL_TRUE if attached, SDL_FALSE otherwise.
  32.308 - */
  32.309 -SDL_bool
  32.310 -SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
  32.311 +static SDL_bool
  32.312 +DARWIN_JoystickIsAttached(SDL_Joystick * joystick)
  32.313  {
  32.314      return joystick->hwdata != NULL;
  32.315  }
  32.316  
  32.317 -/* Function to update the state of a joystick - called as a device poll.
  32.318 - * This function shouldn't update the joystick structure directly,
  32.319 - * but instead should call SDL_PrivateJoystick*() to deliver events
  32.320 - * and update joystick device state.
  32.321 +/*
  32.322 + * Like strerror but for force feedback errors.
  32.323   */
  32.324 -void
  32.325 -SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
  32.326 +static const char *
  32.327 +FFStrError(unsigned int err)
  32.328 +{
  32.329 +    switch (err) {
  32.330 +    case FFERR_DEVICEFULL:
  32.331 +        return "device full";
  32.332 +    /* This should be valid, but for some reason isn't defined... */
  32.333 +    /* case FFERR_DEVICENOTREG:
  32.334 +        return "device not registered"; */
  32.335 +    case FFERR_DEVICEPAUSED:
  32.336 +        return "device paused";
  32.337 +    case FFERR_DEVICERELEASED:
  32.338 +        return "device released";
  32.339 +    case FFERR_EFFECTPLAYING:
  32.340 +        return "effect playing";
  32.341 +    case FFERR_EFFECTTYPEMISMATCH:
  32.342 +        return "effect type mismatch";
  32.343 +    case FFERR_EFFECTTYPENOTSUPPORTED:
  32.344 +        return "effect type not supported";
  32.345 +    case FFERR_GENERIC:
  32.346 +        return "undetermined error";
  32.347 +    case FFERR_HASEFFECTS:
  32.348 +        return "device has effects";
  32.349 +    case FFERR_INCOMPLETEEFFECT:
  32.350 +        return "incomplete effect";
  32.351 +    case FFERR_INTERNAL:
  32.352 +        return "internal fault";
  32.353 +    case FFERR_INVALIDDOWNLOADID:
  32.354 +        return "invalid download id";
  32.355 +    case FFERR_INVALIDPARAM:
  32.356 +        return "invalid parameter";
  32.357 +    case FFERR_MOREDATA:
  32.358 +        return "more data";
  32.359 +    case FFERR_NOINTERFACE:
  32.360 +        return "interface not supported";
  32.361 +    case FFERR_NOTDOWNLOADED:
  32.362 +        return "effect is not downloaded";
  32.363 +    case FFERR_NOTINITIALIZED:
  32.364 +        return "object has not been initialized";
  32.365 +    case FFERR_OUTOFMEMORY:
  32.366 +        return "out of memory";
  32.367 +    case FFERR_UNPLUGGED:
  32.368 +        return "device is unplugged";
  32.369 +    case FFERR_UNSUPPORTED:
  32.370 +        return "function call unsupported";
  32.371 +    case FFERR_UNSUPPORTEDAXIS:
  32.372 +        return "axis unsupported";
  32.373 +
  32.374 +    default:
  32.375 +        return "unknown error";
  32.376 +    }
  32.377 +}
  32.378 +
  32.379 +static int
  32.380 +DARWIN_JoystickInitRumble(recDevice *device, Sint16 magnitude, Uint32 duration_ms)
  32.381 +{
  32.382 +    HRESULT result;
  32.383 +
  32.384 +    if (!device->ffdevice) {
  32.385 +        result = FFCreateDevice(device->ffservice, &device->ffdevice);
  32.386 +        if (result != FF_OK) {
  32.387 +            return SDL_SetError("Unable to create force feedback device from service: %s", FFStrError(result));
  32.388 +        }
  32.389 +    }
  32.390 +
  32.391 +    /* Reset and then enable actuators */
  32.392 +    result = FFDeviceSendForceFeedbackCommand(device->ffdevice, FFSFFC_RESET);
  32.393 +    if (result != FF_OK) {
  32.394 +        return SDL_SetError("Unable to reset force feedback device: %s", FFStrError(result));
  32.395 +    }
  32.396 +
  32.397 +    result = FFDeviceSendForceFeedbackCommand(device->ffdevice, FFSFFC_SETACTUATORSON);
  32.398 +    if (result != FF_OK) {
  32.399 +        return SDL_SetError("Unable to enable force feedback actuators: %s", FFStrError(result));
  32.400 +    }
  32.401 +
  32.402 +    /* Create the effect */
  32.403 +    device->ffeffect = CreateRumbleEffectData(magnitude, duration_ms);
  32.404 +    if (!device->ffeffect) {
  32.405 +        return SDL_OutOfMemory();
  32.406 +    }
  32.407 +
  32.408 +    result = FFDeviceCreateEffect(device->ffdevice, kFFEffectType_Sine_ID,
  32.409 +                               device->ffeffect, &device->ffeffect_ref);
  32.410 +    if (result != FF_OK) {
  32.411 +        return SDL_SetError("Haptic: Unable to create effect: %s", FFStrError(result));
  32.412 +    }
  32.413 +    return 0;
  32.414 +}
  32.415 +
  32.416 +static int
  32.417 +DARWIN_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
  32.418 +{
  32.419 +    HRESULT result;
  32.420 +    recDevice *device = joystick->hwdata;
  32.421 +
  32.422 +    /* Scale and average the two rumble strengths */
  32.423 +    Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
  32.424 +
  32.425 +    if (!device->ffservice) {
  32.426 +        return SDL_Unsupported();
  32.427 +    }
  32.428 +
  32.429 +    if (device->ff_initialized) {
  32.430 +        FFPERIODIC *periodic = ((FFPERIODIC *)device->ffeffect->lpvTypeSpecificParams);
  32.431 +        device->ffeffect->dwDuration = duration_ms * 1000; /* In microseconds. */
  32.432 +        periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
  32.433 +
  32.434 +        result = FFEffectSetParameters(device->ffeffect_ref, device->ffeffect,
  32.435 +                                    (FFEP_DURATION | FFEP_TYPESPECIFICPARAMS));
  32.436 +        if (result != FF_OK) {
  32.437 +            return SDL_SetError("Unable to update rumble effect: %s", FFStrError(result));
  32.438 +        }
  32.439 +    } else {
  32.440 +        if (DARWIN_JoystickInitRumble(device, magnitude, duration_ms) < 0) {
  32.441 +            return -1;
  32.442 +        }
  32.443 +        device->ff_initialized = SDL_TRUE;
  32.444 +    }
  32.445 +
  32.446 +    result = FFEffectStart(device->ffeffect_ref, 1, 0);
  32.447 +    if (result != FF_OK) {
  32.448 +        return SDL_SetError("Unable to run the rumble effect: %s", FFStrError(result));
  32.449 +    }
  32.450 +    return 0;
  32.451 +}
  32.452 +
  32.453 +static void
  32.454 +DARWIN_JoystickUpdate(SDL_Joystick * joystick)
  32.455  {
  32.456      recDevice *device = joystick->hwdata;
  32.457      recElement *element;
  32.458 @@ -792,15 +978,13 @@
  32.459      }
  32.460  }
  32.461  
  32.462 -/* Function to close a joystick after use */
  32.463 -void
  32.464 -SDL_SYS_JoystickClose(SDL_Joystick * joystick)
  32.465 +static void
  32.466 +DARWIN_JoystickClose(SDL_Joystick * joystick)
  32.467  {
  32.468  }
  32.469  
  32.470 -/* Function to perform any system-specific joystick related cleanup */
  32.471 -void
  32.472 -SDL_SYS_JoystickQuit(void)
  32.473 +static void
  32.474 +DARWIN_JoystickQuit(void)
  32.475  {
  32.476      while (FreeDevice(gpDeviceList)) {
  32.477          /* spin */
  32.478 @@ -814,23 +998,21 @@
  32.479      }
  32.480  }
  32.481  
  32.482 -
  32.483 -SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
  32.484 +SDL_JoystickDriver SDL_DARWIN_JoystickDriver =
  32.485  {
  32.486 -    recDevice *device = GetDeviceForIndex(device_index);
  32.487 -    SDL_JoystickGUID guid;
  32.488 -    if (device) {
  32.489 -        guid = device->guid;
  32.490 -    } else {
  32.491 -        SDL_zero(guid);
  32.492 -    }
  32.493 -    return guid;
  32.494 -}
  32.495 -
  32.496 -SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick *joystick)
  32.497 -{
  32.498 -    return joystick->hwdata->guid;
  32.499 -}
  32.500 +    DARWIN_JoystickInit,
  32.501 +    DARWIN_JoystickGetCount,
  32.502 +    DARWIN_JoystickDetect,
  32.503 +    DARWIN_JoystickGetDeviceName,
  32.504 +    DARWIN_JoystickGetDeviceGUID,
  32.505 +    DARWIN_JoystickGetDeviceInstanceID,
  32.506 +    DARWIN_JoystickOpen,
  32.507 +    DARWIN_JoystickIsAttached,
  32.508 +    DARWIN_JoystickRumble,
  32.509 +    DARWIN_JoystickUpdate,
  32.510 +    DARWIN_JoystickClose,
  32.511 +    DARWIN_JoystickQuit,
  32.512 +};
  32.513  
  32.514  #endif /* SDL_JOYSTICK_IOKIT */
  32.515  
    33.1 --- a/src/joystick/darwin/SDL_sysjoystick_c.h	Wed Aug 08 15:39:20 2018 -0400
    33.2 +++ b/src/joystick/darwin/SDL_sysjoystick_c.h	Thu Aug 09 16:00:17 2018 -0700
    33.3 @@ -24,6 +24,8 @@
    33.4  #define SDL_JOYSTICK_IOKIT_H
    33.5  
    33.6  #include <IOKit/hid/IOHIDLib.h>
    33.7 +#include <ForceFeedback/ForceFeedback.h>
    33.8 +#include <ForceFeedback/ForceFeedbackConstants.h>
    33.9  
   33.10  struct recElement
   33.11  {
   33.12 @@ -45,6 +47,10 @@
   33.13  {
   33.14      IOHIDDeviceRef deviceRef;   /* HIDManager device handle */
   33.15      io_service_t ffservice;     /* Interface for force feedback, 0 = no ff */
   33.16 +    FFDeviceObjectReference ffdevice;
   33.17 +    FFEFFECT *ffeffect;
   33.18 +    FFEffectObjectReference ffeffect_ref;
   33.19 +    SDL_bool ff_initialized;
   33.20  
   33.21      char product[256];          /* name of product */
   33.22      uint32_t usage;                 /* usage page from IOUSBHID Parser.h which defines general usage */
    34.1 --- a/src/joystick/dummy/SDL_sysjoystick.c	Wed Aug 08 15:39:20 2018 -0400
    34.2 +++ b/src/joystick/dummy/SDL_sysjoystick.c	Thu Aug 09 16:00:17 2018 -0700
    34.3 @@ -28,99 +28,92 @@
    34.4  #include "../SDL_sysjoystick.h"
    34.5  #include "../SDL_joystick_c.h"
    34.6  
    34.7 -/* Function to scan the system for joysticks.
    34.8 - * It should return 0, or -1 on an unrecoverable fatal error.
    34.9 - */
   34.10 -int
   34.11 -SDL_SYS_JoystickInit(void)
   34.12 +
   34.13 +static int
   34.14 +DUMMY_JoystickInit(void)
   34.15  {
   34.16      return 0;
   34.17  }
   34.18  
   34.19 -int
   34.20 -SDL_SYS_NumJoysticks(void)
   34.21 +static int
   34.22 +DUMMY_JoystickGetCount(void)
   34.23  {
   34.24      return 0;
   34.25  }
   34.26  
   34.27 -void
   34.28 -SDL_SYS_JoystickDetect(void)
   34.29 +static void
   34.30 +DUMMY_JoystickDetect(void)
   34.31  {
   34.32  }
   34.33  
   34.34 -/* Function to get the device-dependent name of a joystick */
   34.35 -const char *
   34.36 -SDL_SYS_JoystickNameForDeviceIndex(int device_index)
   34.37 +static const char *
   34.38 +DUMMY_JoystickGetDeviceName(int device_index)
   34.39  {
   34.40 -    SDL_SetError("Logic error: No joysticks available");
   34.41 -    return (NULL);
   34.42 +    return NULL;
   34.43  }
   34.44  
   34.45 -/* Function to perform the mapping from device index to the instance id for this index */
   34.46 -SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
   34.47 +static SDL_JoystickGUID
   34.48 +DUMMY_JoystickGetDeviceGUID(int device_index)
   34.49  {
   34.50 -    return device_index;
   34.51 +    SDL_JoystickGUID guid;
   34.52 +    SDL_zero(guid);
   34.53 +    return guid;
   34.54  }
   34.55  
   34.56 -/* Function to open a joystick for use.
   34.57 -   The joystick to open is specified by the device index.
   34.58 -   This should fill the nbuttons and naxes fields of the joystick structure.
   34.59 -   It returns 0, or -1 if there is an error.
   34.60 - */
   34.61 -int
   34.62 -SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
   34.63 +static SDL_JoystickID
   34.64 +DUMMY_JoystickGetDeviceInstanceID(int device_index)
   34.65 +{
   34.66 +    return -1;
   34.67 +}
   34.68 +
   34.69 +static int
   34.70 +DUMMY_JoystickOpen(SDL_Joystick * joystick, int device_index)
   34.71  {
   34.72      return SDL_SetError("Logic error: No joysticks available");
   34.73  }
   34.74  
   34.75 -/* Function to determine if this joystick is attached to the system right now */
   34.76 -SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
   34.77 +static SDL_bool
   34.78 +DUMMY_JoystickIsAttached(SDL_Joystick *joystick)
   34.79  {
   34.80 -    return SDL_TRUE;
   34.81 +    return SDL_FALSE;
   34.82  }
   34.83  
   34.84 -/* Function to update the state of a joystick - called as a device poll.
   34.85 - * This function shouldn't update the joystick structure directly,
   34.86 - * but instead should call SDL_PrivateJoystick*() to deliver events
   34.87 - * and update joystick device state.
   34.88 - */
   34.89 -void
   34.90 -SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
   34.91 +static int
   34.92 +DUMMY_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   34.93 +{
   34.94 +    return SDL_Unsupported();
   34.95 +}
   34.96 +
   34.97 +static void
   34.98 +DUMMY_JoystickUpdate(SDL_Joystick * joystick)
   34.99  {
  34.100  }
  34.101  
  34.102 -/* Function to close a joystick after use */
  34.103 -void
  34.104 -SDL_SYS_JoystickClose(SDL_Joystick * joystick)
  34.105 +static void
  34.106 +DUMMY_JoystickClose(SDL_Joystick * joystick)
  34.107  {
  34.108  }
  34.109  
  34.110 -/* Function to perform any system-specific joystick related cleanup */
  34.111 -void
  34.112 -SDL_SYS_JoystickQuit(void)
  34.113 +static void
  34.114 +DUMMY_JoystickQuit(void)
  34.115  {
  34.116  }
  34.117  
  34.118 -SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
  34.119 +SDL_JoystickDriver SDL_DUMMY_JoystickDriver =
  34.120  {
  34.121 -    SDL_JoystickGUID guid;
  34.122 -    /* the GUID is just the first 16 chars of the name for now */
  34.123 -    const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index );
  34.124 -    SDL_zero( guid );
  34.125 -    SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
  34.126 -    return guid;
  34.127 -}
  34.128 -
  34.129 -
  34.130 -SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
  34.131 -{
  34.132 -    SDL_JoystickGUID guid;
  34.133 -    /* the GUID is just the first 16 chars of the name for now */
  34.134 -    const char *name = joystick->name;
  34.135 -    SDL_zero( guid );
  34.136 -    SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
  34.137 -    return guid;
  34.138 -}
  34.139 +    DUMMY_JoystickInit,
  34.140 +    DUMMY_JoystickGetCount,
  34.141 +    DUMMY_JoystickDetect,
  34.142 +    DUMMY_JoystickGetDeviceName,
  34.143 +    DUMMY_JoystickGetDeviceGUID,
  34.144 +    DUMMY_JoystickGetDeviceInstanceID,
  34.145 +    DUMMY_JoystickOpen,
  34.146 +    DUMMY_JoystickIsAttached,
  34.147 +    DUMMY_JoystickRumble,
  34.148 +    DUMMY_JoystickUpdate,
  34.149 +    DUMMY_JoystickClose,
  34.150 +    DUMMY_JoystickQuit,
  34.151 +};
  34.152  
  34.153  #endif /* SDL_JOYSTICK_DUMMY || SDL_JOYSTICK_DISABLED */
  34.154  
    35.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    35.2 +++ b/src/joystick/hidapi/SDL_hidapi_ps4.c	Thu Aug 09 16:00:17 2018 -0700
    35.3 @@ -0,0 +1,533 @@
    35.4 +/*
    35.5 +  Simple DirectMedia Layer
    35.6 +  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
    35.7 +
    35.8 +  This software is provided 'as-is', without any express or implied
    35.9 +  warranty.  In no event will the authors be held liable for any damages
   35.10 +  arising from the use of this software.
   35.11 +
   35.12 +  Permission is granted to anyone to use this software for any purpose,
   35.13 +  including commercial applications, and to alter it and redistribute it
   35.14 +  freely, subject to the following restrictions:
   35.15 +
   35.16 +  1. The origin of this software must not be misrepresented; you must not
   35.17 +     claim that you wrote the original software. If you use this software
   35.18 +     in a product, an acknowledgment in the product documentation would be
   35.19 +     appreciated but is not required.
   35.20 +  2. Altered source versions must be plainly marked as such, and must not be
   35.21 +     misrepresented as being the original software.
   35.22 +  3. This notice may not be removed or altered from any source distribution.
   35.23 +*/
   35.24 +#include "../../SDL_internal.h"
   35.25 +
   35.26 +#ifdef SDL_JOYSTICK_HIDAPI
   35.27 +
   35.28 +#include "SDL_hints.h"
   35.29 +#include "SDL_log.h"
   35.30 +#include "SDL_events.h"
   35.31 +#include "SDL_timer.h"
   35.32 +#include "SDL_joystick.h"
   35.33 +#include "SDL_gamecontroller.h"
   35.34 +#include "../SDL_sysjoystick.h"
   35.35 +#include "SDL_hidapijoystick_c.h"
   35.36 +
   35.37 +
   35.38 +#ifdef SDL_JOYSTICK_HIDAPI_PS4
   35.39 +
   35.40 +#define SONY_USB_VID        0x054C
   35.41 +#define SONY_DS4_PID        0x05C4
   35.42 +#define SONY_DS4_DONGLE_PID 0x0BA0
   35.43 +#define SONY_DS4_SLIM_PID   0x09CC
   35.44 +
   35.45 +#define USB_PACKET_LENGTH   64
   35.46 +
   35.47 +#define VOLUME_CHECK_INTERVAL_MS    (10 * 1000)
   35.48 +
   35.49 +typedef enum
   35.50 +{
   35.51 +    k_EPS4ReportIdUsbState = 1,
   35.52 +    k_EPS4ReportIdUsbEffects = 5,
   35.53 +    k_EPS4ReportIdBluetoothState = 17,
   35.54 +    k_EPS4ReportIdBluetoothEffects = 17,
   35.55 +    k_EPS4ReportIdDisconnectMessage = 226,
   35.56 +} EPS4ReportId;
   35.57 +
   35.58 +typedef enum 
   35.59 +{
   35.60 +    k_ePS4FeatureReportIdGyroCalibration_USB = 0x02,
   35.61 +    k_ePS4FeatureReportIdGyroCalibration_BT = 0x05,
   35.62 +    k_ePS4FeatureReportIdSerialNumber = 0x12,
   35.63 +} EPS4FeatureReportID;
   35.64 +
   35.65 +typedef struct
   35.66 +{
   35.67 +    Uint8 ucLeftJoystickX;
   35.68 +    Uint8 ucLeftJoystickY;
   35.69 +    Uint8 ucRightJoystickX;
   35.70 +    Uint8 ucRightJoystickY;
   35.71 +    Uint8 rgucButtonsHatAndCounter[ 3 ];
   35.72 +    Uint8 ucTriggerLeft;
   35.73 +    Uint8 ucTriggerRight;
   35.74 +    Uint8 _rgucPad0[ 3 ];
   35.75 +    Sint16 sGyroX;
   35.76 +    Sint16 sGyroY;
   35.77 +    Sint16 sGyroZ;
   35.78 +    Sint16 sAccelX;
   35.79 +    Sint16 sAccelY;
   35.80 +    Sint16 sAccelZ;
   35.81 +    Uint8 _rgucPad1[ 5 ];
   35.82 +    Uint8 ucBatteryLevel;
   35.83 +    Uint8 _rgucPad2[ 4 ];
   35.84 +    Uint8 ucTrackpadCounter1;
   35.85 +    Uint8 rgucTrackpadData1[ 3 ];
   35.86 +    Uint8 ucTrackpadCounter2;
   35.87 +    Uint8 rgucTrackpadData2[ 3 ];
   35.88 +} PS4StatePacket_t;
   35.89 +
   35.90 +typedef struct
   35.91 +{
   35.92 +    Uint8 ucRumbleRight;
   35.93 +    Uint8 ucRumbleLeft;
   35.94 +    Uint8 ucLedRed;
   35.95 +    Uint8 ucLedGreen;
   35.96 +    Uint8 ucLedBlue;
   35.97 +    Uint8 ucLedDelayOn;
   35.98 +    Uint8 ucLedDelayOff;
   35.99 +    Uint8 _rgucPad0[ 8 ];
  35.100 +    Uint8 ucVolumeLeft;
  35.101 +    Uint8 ucVolumeRight;
  35.102 +    Uint8 ucVolumeMic;
  35.103 +    Uint8 ucVolumeSpeaker;
  35.104 +} DS4EffectsState_t;
  35.105 +
  35.106 +typedef struct {
  35.107 +    SDL_bool is_dongle;
  35.108 +    SDL_bool is_bluetooth;
  35.109 +    SDL_bool audio_supported;
  35.110 +    Uint8 volume;
  35.111 +    Uint32 last_volume_check;
  35.112 +    Uint32 rumble_expiration;
  35.113 +    PS4StatePacket_t last_state;
  35.114 +} SDL_DriverPS4_Context;
  35.115 +
  35.116 +
  35.117 +/* Public domain CRC implementation adapted from:
  35.118 +   http://home.thep.lu.se/~bjorn/crc/crc32_simple.c
  35.119 +*/
  35.120 +static Uint32 crc32_for_byte(Uint32 r)
  35.121 +{
  35.122 +    int i;
  35.123 +    for(i = 0; i < 8; ++i) {
  35.124 +        r = (r & 1? 0: (Uint32)0xEDB88320L) ^ r >> 1;
  35.125 +    }
  35.126 +    return r ^ (Uint32)0xFF000000L;
  35.127 +}
  35.128 +
  35.129 +static Uint32 crc32(Uint32 crc, const void *data, int count)
  35.130 +{
  35.131 +    int i;
  35.132 +    for(i = 0; i < count; ++i) {
  35.133 +        crc = crc32_for_byte((Uint8)crc ^ ((const Uint8*)data)[i]) ^ crc >> 8;
  35.134 +    }
  35.135 +    return crc;
  35.136 +}
  35.137 +
  35.138 +#ifdef __WIN32__
  35.139 +#include "../../core/windows/SDL_windows.h"
  35.140 +
  35.141 +/* Define Vista for the Audio related includes below to work */
  35.142 +#undef NTDDI_VERSION
  35.143 +#define NTDDI_VERSION NTDDI_VISTA
  35.144 +#undef _WIN32_WINNT
  35.145 +#define _WIN32_WINNT _WIN32_WINNT_VISTA
  35.146 +#define COBJMACROS
  35.147 +#include <Mmdeviceapi.h>
  35.148 +#include <Audioclient.h>
  35.149 +#include <Endpointvolume.h>
  35.150 +
  35.151 +#undef DEFINE_GUID
  35.152 +#define DEFINE_GUID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID n = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
  35.153 +DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E);
  35.154 +DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6);
  35.155 +DEFINE_GUID(IID_IAudioEndpointVolume, 0x5CDF2C82, 0x841E, 0x4546, 0x97, 0x22, 0x0C, 0xF7, 0x40, 0x78, 0x22, 0x9A);
  35.156 +#endif
  35.157 +
  35.158 +
  35.159 +
  35.160 +static float GetSystemVolume(void)
  35.161 +{
  35.162 +    float volume = -1.0f;    /* Return this if we can't get system volume */
  35.163 +
  35.164 +#ifdef __WIN32__
  35.165 +    HRESULT hr = WIN_CoInitialize();
  35.166 +    if (SUCCEEDED(hr)) {
  35.167 +        IMMDeviceEnumerator *pEnumerator;
  35.168 +
  35.169 +        /* This should gracefully fail on XP and succeed on everything Vista and above */
  35.170 +        hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (LPVOID*)&pEnumerator);
  35.171 +        if (SUCCEEDED(hr)) {
  35.172 +            IMMDevice *pDevice;
  35.173 +
  35.174 +            hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eRender, eConsole, &pDevice);
  35.175 +            if (SUCCEEDED(hr)) {
  35.176 +                IAudioEndpointVolume *pEndpointVolume;
  35.177 +
  35.178 +                hr = IMMDevice_Activate(pDevice, &IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (LPVOID*)&pEndpointVolume);
  35.179 +                if (SUCCEEDED(hr)) {
  35.180 +                    IAudioEndpointVolume_GetMasterVolumeLevelScalar(pEndpointVolume, &volume);
  35.181 +                    IUnknown_Release(pEndpointVolume);
  35.182 +                }
  35.183 +                IUnknown_Release(pDevice);
  35.184 +            }
  35.185 +            IUnknown_Release(pEnumerator);
  35.186 +        }
  35.187 +        WIN_CoUninitialize();
  35.188 +    }
  35.189 +#endif /* __WIN32__ */
  35.190 +
  35.191 +    return volume;
  35.192 +}
  35.193 +
  35.194 +static uint8_t GetPlaystationVolumeFromFloat(float fVolume)
  35.195 +{
  35.196 +    const int k_nVolumeFitRatio = 15;
  35.197 +    const int k_nVolumeFitOffset = 9;
  35.198 +    float fVolLog;
  35.199 +
  35.200 +    if (fVolume > 1.0f || fVolume < 0.0f) {
  35.201 +        fVolume = 0.30f;
  35.202 +    }
  35.203 +    fVolLog = SDL_logf(fVolume * 100);
  35.204 +
  35.205 +    return (Uint8)((fVolLog * k_nVolumeFitRatio) + k_nVolumeFitOffset);
  35.206 +}
  35.207 +
  35.208 +static SDL_bool
  35.209 +HIDAPI_DriverPS4_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, int interface_number, Uint16 usage_page, Uint16 usage)
  35.210 +{
  35.211 +    /* The Revolution Pro Controller exposes multiple interfaces on Windows */
  35.212 +    const Uint16 NACON_USB_VID = 0x146b;
  35.213 +    if (vendor_id == NACON_USB_VID && usage_page != 0 && usage_page != 1) {
  35.214 +        return SDL_FALSE;
  35.215 +    }
  35.216 +
  35.217 +    return SDL_IsJoystickPS4(vendor_id, product_id);
  35.218 +}
  35.219 +
  35.220 +static const char *
  35.221 +HIDAPI_DriverPS4_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
  35.222 +{
  35.223 +    if (vendor_id == SONY_USB_VID) {
  35.224 +        return "PS4 Controller";
  35.225 +    }
  35.226 +    return NULL;
  35.227 +}
  35.228 +
  35.229 +static SDL_bool ReadFeatureReport(hid_device *dev, Uint8 report_id, Uint8 *data, size_t size)
  35.230 +{
  35.231 +    Uint8 report[USB_PACKET_LENGTH + 1];
  35.232 +
  35.233 +    SDL_memset(report, 0, sizeof(report));
  35.234 +    report[0] = report_id;
  35.235 +    if (hid_get_feature_report(dev, report, sizeof(report)) < 0) {
  35.236 +        return SDL_FALSE;
  35.237 +    }
  35.238 +    SDL_memcpy(data, report, SDL_min(size, sizeof(report)));
  35.239 +    return SDL_TRUE;
  35.240 +}
  35.241 +
  35.242 +static SDL_bool CheckUSBConnected(hid_device *dev)
  35.243 +{
  35.244 +    int i;
  35.245 +    Uint8 data[16];
  35.246 +
  35.247 +    /* This will fail if we're on Bluetooth */
  35.248 +    if (ReadFeatureReport(dev, k_ePS4FeatureReportIdSerialNumber, data, sizeof(data))) {
  35.249 +        for (i = 0; i < sizeof(data); ++i) {
  35.250 +            if (data[i] != 0x00) {
  35.251 +                return SDL_TRUE;
  35.252 +            }
  35.253 +        }
  35.254 +        /* Maybe the dongle without a connected controller? */
  35.255 +    }
  35.256 +    return SDL_FALSE;
  35.257 +}
  35.258 +
  35.259 +static int HIDAPI_DriverPS4_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
  35.260 +
  35.261 +static SDL_bool
  35.262 +HIDAPI_DriverPS4_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
  35.263 +{
  35.264 +    SDL_DriverPS4_Context *ctx;
  35.265 +
  35.266 +    ctx = (SDL_DriverPS4_Context *)SDL_calloc(1, sizeof(*ctx));
  35.267 +    if (!ctx) {
  35.268 +        SDL_OutOfMemory();
  35.269 +        return SDL_FALSE;
  35.270 +    }
  35.271 +    *context = ctx;
  35.272 +
  35.273 +    /* Check for type of connection */
  35.274 +    ctx->is_dongle = (vendor_id == SONY_USB_VID && product_id == SONY_DS4_DONGLE_PID);
  35.275 +    if (ctx->is_dongle) {
  35.276 +        ctx->is_bluetooth = SDL_FALSE;
  35.277 +    } else if (vendor_id == SONY_USB_VID) {
  35.278 +        ctx->is_bluetooth = !CheckUSBConnected(dev);
  35.279 +    } else {
  35.280 +        /* Third party controllers appear to all be wired */
  35.281 +        ctx->is_bluetooth = SDL_FALSE;
  35.282 +    }
  35.283 +#ifdef DEBUG_PS4
  35.284 +    SDL_Log("PS4 dongle = %s, bluetooth = %s\n", ctx->is_dongle ? "TRUE" : "FALSE", ctx->is_bluetooth ? "TRUE" : "FALSE");
  35.285 +#endif
  35.286 +
  35.287 +    /* Check to see if audio is supported */
  35.288 +    if (vendor_id == SONY_USB_VID &&
  35.289 +        (product_id == SONY_DS4_SLIM_PID || product_id == SONY_DS4_DONGLE_PID )) {
  35.290 +        ctx->audio_supported = SDL_TRUE;
  35.291 +    }
  35.292 +
  35.293 +    /* Initialize LED and effect state */
  35.294 +    HIDAPI_DriverPS4_Rumble(joystick, dev, ctx, 0, 0, 0);
  35.295 +
  35.296 +    /* Initialize the joystick capabilities */
  35.297 +    joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
  35.298 +    joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
  35.299 +    joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
  35.300 +
  35.301 +    return SDL_TRUE;
  35.302 +}
  35.303 +
  35.304 +static int
  35.305 +HIDAPI_DriverPS4_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
  35.306 +{
  35.307 +    SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context;
  35.308 +    DS4EffectsState_t *effects;
  35.309 +    Uint8 data[78];
  35.310 +    int report_size, offset;
  35.311 +
  35.312 +    /* In order to send rumble, we have to send a complete effect packet */
  35.313 +    SDL_memset(data, 0, sizeof(data));
  35.314 +
  35.315 +    if (ctx->is_bluetooth) {
  35.316 +        data[0] = k_EPS4ReportIdBluetoothEffects;
  35.317 +        data[1] = 0xC0 | 0x04;  /* Magic value HID + CRC, also sets interval to 4ms for samples */
  35.318 +        data[3] = 0x03;  /* 0x1 is rumble, 0x2 is lightbar, 0x4 is the blink interval */
  35.319 +
  35.320 +        report_size = 78;
  35.321 +        offset = 6;
  35.322 +    } else {
  35.323 +        data[0] = k_EPS4ReportIdUsbEffects;
  35.324 +        data[1] = 0x07;  /* Magic value */
  35.325 +
  35.326 +        report_size = 32;
  35.327 +        offset = 4;
  35.328 +    }
  35.329 +    effects = (DS4EffectsState_t *)&data[offset];
  35.330 +
  35.331 +    effects->ucRumbleLeft = (low_frequency_rumble >> 8);
  35.332 +    effects->ucRumbleRight = (high_frequency_rumble >> 8);
  35.333 +
  35.334 +    effects->ucLedRed = 0;
  35.335 +    effects->ucLedGreen = 0;
  35.336 +    effects->ucLedBlue = 80;
  35.337 +
  35.338 +    if (ctx->audio_supported) {
  35.339 +        Uint32 now = SDL_GetTicks();
  35.340 +        if (!ctx->last_volume_check ||
  35.341 +            SDL_TICKS_PASSED(now, ctx->last_volume_check + VOLUME_CHECK_INTERVAL_MS)) {
  35.342 +            ctx->volume = GetPlaystationVolumeFromFloat(GetSystemVolume());
  35.343 +            ctx->last_volume_check = now;
  35.344 +        }
  35.345 +
  35.346 +        effects->ucVolumeRight = ctx->volume;
  35.347 +        effects->ucVolumeLeft = ctx->volume;
  35.348 +        effects->ucVolumeSpeaker = ctx->volume;
  35.349 +        effects->ucVolumeMic = 0xFF;
  35.350 +    }
  35.351 +
  35.352 +    if (ctx->is_bluetooth) {
  35.353 +        /* Bluetooth reports need a CRC at the end of the packet (at least on Linux) */
  35.354 +        Uint8 ubHdr = 0xA2; /* hidp header is part of the CRC calculation */
  35.355 +        Uint32 unCRC;
  35.356 +        unCRC = crc32(0, &ubHdr, 1);
  35.357 +        unCRC = crc32(unCRC, data, (Uint32)(report_size - sizeof(unCRC)));
  35.358 +        SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC));
  35.359 +    }
  35.360 +
  35.361 +    if (hid_write(dev, data, report_size) != report_size) {
  35.362 +        return SDL_SetError("Couldn't send rumble packet");
  35.363 +    }
  35.364 +
  35.365 +    if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
  35.366 +        ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
  35.367 +    } else {
  35.368 +        ctx->rumble_expiration = 0;
  35.369 +    }
  35.370 +    return 0;
  35.371 +}
  35.372 +
  35.373 +static void
  35.374 +HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS4_Context *ctx, PS4StatePacket_t *packet)
  35.375 +{
  35.376 +    Sint16 axis;
  35.377 +
  35.378 +    if (ctx->last_state.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) {
  35.379 +        {
  35.380 +            Uint8 data = (packet->rgucButtonsHatAndCounter[0] >> 4);
  35.381 +
  35.382 +            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
  35.383 +            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
  35.384 +            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
  35.385 +            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
  35.386 +        }
  35.387 +        {
  35.388 +            Uint8 data = (packet->rgucButtonsHatAndCounter[0] & 0x0F);
  35.389 +            SDL_bool dpad_up = SDL_FALSE;
  35.390 +            SDL_bool dpad_down = SDL_FALSE;
  35.391 +            SDL_bool dpad_left = SDL_FALSE;
  35.392 +            SDL_bool dpad_right = SDL_FALSE;
  35.393 +
  35.394 +            switch (data) {
  35.395 +            case 0:
  35.396 +                dpad_up = SDL_TRUE;
  35.397 +                break;
  35.398 +            case 1:
  35.399 +                dpad_up = SDL_TRUE;
  35.400 +                dpad_right = SDL_TRUE;
  35.401 +                break;
  35.402 +            case 2:
  35.403 +                dpad_right = SDL_TRUE;
  35.404 +                break;
  35.405 +            case 3:
  35.406 +                dpad_right = SDL_TRUE;
  35.407 +                dpad_down = SDL_TRUE;
  35.408 +                break;
  35.409 +            case 4:
  35.410 +                dpad_down = SDL_TRUE;
  35.411 +                break;
  35.412 +            case 5:
  35.413 +                dpad_left = SDL_TRUE;
  35.414 +                dpad_down = SDL_TRUE;
  35.415 +                break;
  35.416 +            case 6:
  35.417 +                dpad_left = SDL_TRUE;
  35.418 +                break;
  35.419 +            case 7:
  35.420 +                dpad_up = SDL_TRUE;
  35.421 +                dpad_left = SDL_TRUE;
  35.422 +                break;
  35.423 +            default:
  35.424 +                break;
  35.425 +            }
  35.426 +            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
  35.427 +            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
  35.428 +            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
  35.429 +            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
  35.430 +        }
  35.431 +    }
  35.432 +
  35.433 +    if (ctx->last_state.rgucButtonsHatAndCounter[1] != packet->rgucButtonsHatAndCounter[1]) {
  35.434 +        Uint8 data = packet->rgucButtonsHatAndCounter[1];
  35.435 +
  35.436 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
  35.437 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
  35.438 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
  35.439 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
  35.440 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
  35.441 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED);
  35.442 +    }
  35.443 +
  35.444 +    if (ctx->last_state.rgucButtonsHatAndCounter[2] != packet->rgucButtonsHatAndCounter[2]) {
  35.445 +        Uint8 data = (packet->rgucButtonsHatAndCounter[2] & 0x03);
  35.446 +
  35.447 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
  35.448 +    }
  35.449 +
  35.450 +    axis = ((int)packet->ucTriggerLeft * 257) - 32768;
  35.451 +    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
  35.452 +    axis = ((int)packet->ucTriggerRight * 257) - 32768;
  35.453 +    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
  35.454 +    axis = ((int)packet->ucLeftJoystickX * 257) - 32768;
  35.455 +    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
  35.456 +    axis = ((int)packet->ucLeftJoystickY * 257) - 32768;
  35.457 +    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
  35.458 +    axis = ((int)packet->ucRightJoystickX * 257) - 32768;
  35.459 +    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
  35.460 +    axis = ((int)packet->ucRightJoystickY * 257) - 32768;
  35.461 +    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
  35.462 +
  35.463 +    if (packet->ucBatteryLevel & 0x10) {
  35.464 +        joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
  35.465 +    } else {
  35.466 +        /* Battery level ranges from 0 to 10 */
  35.467 +        int level = (packet->ucBatteryLevel & 0xF);
  35.468 +        if (level == 0) {
  35.469 +            joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
  35.470 +        } else if (level <= 2) {
  35.471 +            joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
  35.472 +        } else if (level <= 7) {
  35.473 +            joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
  35.474 +        } else {
  35.475 +            joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
  35.476 +        }
  35.477 +    }
  35.478 +
  35.479 +    SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state));
  35.480 +}
  35.481 +
  35.482 +static void
  35.483 +HIDAPI_DriverPS4_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
  35.484 +{
  35.485 +    SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context;
  35.486 +    Uint8 data[USB_PACKET_LENGTH];
  35.487 +    int size;
  35.488 +
  35.489 +    while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
  35.490 +        switch (data[0]) {
  35.491 +        case k_EPS4ReportIdUsbState:
  35.492 +            HIDAPI_DriverPS4_HandleStatePacket(joystick, dev, ctx, (PS4StatePacket_t *)&data[1]);
  35.493 +            break;
  35.494 +        case k_EPS4ReportIdBluetoothState:
  35.495 +            /* Bluetooth state packets have two additional bytes at the beginning */
  35.496 +            HIDAPI_DriverPS4_HandleStatePacket(joystick, dev, ctx, (PS4StatePacket_t *)&data[3]);
  35.497 +            break;
  35.498 +        default:
  35.499 +#ifdef DEBUG_JOYSTICK
  35.500 +            SDL_Log("Unknown PS4 packet: 0x%.2x\n", data[0]);
  35.501 +#endif
  35.502 +            break;
  35.503 +        }
  35.504 +    }
  35.505 +
  35.506 +    if (ctx->rumble_expiration) {
  35.507 +        Uint32 now = SDL_GetTicks();
  35.508 +        if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
  35.509 +            HIDAPI_DriverPS4_Rumble(joystick, dev, context, 0, 0, 0);
  35.510 +        }
  35.511 +    }
  35.512 +}
  35.513 +
  35.514 +static void
  35.515 +HIDAPI_DriverPS4_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
  35.516 +{
  35.517 +    SDL_free(context);
  35.518 +}
  35.519 +
  35.520 +SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 =
  35.521 +{
  35.522 +    SDL_HINT_JOYSTICK_HIDAPI_PS4,
  35.523 +    SDL_TRUE,
  35.524 +    HIDAPI_DriverPS4_IsSupportedDevice,
  35.525 +    HIDAPI_DriverPS4_GetDeviceName,
  35.526 +    HIDAPI_DriverPS4_Init,
  35.527 +    HIDAPI_DriverPS4_Rumble,
  35.528 +    HIDAPI_DriverPS4_Update,
  35.529 +    HIDAPI_DriverPS4_Quit
  35.530 +};
  35.531 +
  35.532 +#endif /* SDL_JOYSTICK_HIDAPI_PS4 */
  35.533 +
  35.534 +#endif /* SDL_JOYSTICK_HIDAPI */
  35.535 +
  35.536 +/* vi: set ts=4 sw=4 expandtab: */
    36.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    36.2 +++ b/src/joystick/hidapi/SDL_hidapi_switch.c	Thu Aug 09 16:00:17 2018 -0700
    36.3 @@ -0,0 +1,899 @@
    36.4 +/*
    36.5 +  Simple DirectMedia Layer
    36.6 +  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
    36.7 +
    36.8 +  This software is provided 'as-is', without any express or implied
    36.9 +  warranty.  In no event will the authors be held liable for any damages
   36.10 +  arising from the use of this software.
   36.11 +
   36.12 +  Permission is granted to anyone to use this software for any purpose,
   36.13 +  including commercial applications, and to alter it and redistribute it
   36.14 +  freely, subject to the following restrictions:
   36.15 +
   36.16 +  1. The origin of this software must not be misrepresented; you must not
   36.17 +     claim that you wrote the original software. If you use this software
   36.18 +     in a product, an acknowledgment in the product documentation would be
   36.19 +     appreciated but is not required.
   36.20 +  2. Altered source versions must be plainly marked as such, and must not be
   36.21 +     misrepresented as being the original software.
   36.22 +  3. This notice may not be removed or altered from any source distribution.
   36.23 +*/
   36.24 +#include "../../SDL_internal.h"
   36.25 +
   36.26 +#ifdef SDL_JOYSTICK_HIDAPI
   36.27 +
   36.28 +#include "SDL_hints.h"
   36.29 +#include "SDL_log.h"
   36.30 +#include "SDL_events.h"
   36.31 +#include "SDL_timer.h"
   36.32 +#include "SDL_joystick.h"
   36.33 +#include "SDL_gamecontroller.h"
   36.34 +#include "../SDL_sysjoystick.h"
   36.35 +#include "SDL_hidapijoystick_c.h"
   36.36 +
   36.37 +
   36.38 +#ifdef SDL_JOYSTICK_HIDAPI_SWITCH
   36.39 +
   36.40 +typedef enum {
   36.41 +    k_eSwitchInputReportIDs_SubcommandReply       = 0x21,
   36.42 +    k_eSwitchInputReportIDs_FullControllerState   = 0x30,
   36.43 +    k_eSwitchInputReportIDs_SimpleControllerState = 0x3F,
   36.44 +    k_eSwitchInputReportIDs_CommandAck            = 0x81,
   36.45 +} ESwitchInputReportIDs;
   36.46 +
   36.47 +typedef enum {
   36.48 +    k_eSwitchOutputReportIDs_RumbleAndSubcommand = 0x01,
   36.49 +    k_eSwitchOutputReportIDs_Rumble              = 0x10,
   36.50 +    k_eSwitchOutputReportIDs_Proprietary         = 0x80,
   36.51 +} ESwitchOutputReportIDs;
   36.52 +
   36.53 +typedef enum {
   36.54 +    k_eSwitchSubcommandIDs_BluetoothManualPair = 0x01,
   36.55 +    k_eSwitchSubcommandIDs_RequestDeviceInfo   = 0x02,
   36.56 +    k_eSwitchSubcommandIDs_SetInputReportMode  = 0x03,
   36.57 +    k_eSwitchSubcommandIDs_SetHCIState         = 0x06,
   36.58 +    k_eSwitchSubcommandIDs_SPIFlashRead        = 0x10,
   36.59 +    k_eSwitchSubcommandIDs_SetPlayerLights     = 0x30,
   36.60 +    k_eSwitchSubcommandIDs_SetHomeLight        = 0x38,
   36.61 +    k_eSwitchSubcommandIDs_EnableIMU           = 0x40,
   36.62 +    k_eSwitchSubcommandIDs_SetIMUSensitivity   = 0x41,
   36.63 +    k_eSwitchSubcommandIDs_EnableVibration     = 0x48,
   36.64 +} ESwitchSubcommandIDs;
   36.65 +
   36.66 +typedef enum {
   36.67 +    k_eSwitchProprietaryCommandIDs_Handshake = 0x02,
   36.68 +    k_eSwitchProprietaryCommandIDs_HighSpeed = 0x03,
   36.69 +    k_eSwitchProprietaryCommandIDs_ForceUSB  = 0x04,
   36.70 +    k_eSwitchProprietaryCommandIDs_ClearUSB  = 0x05,
   36.71 +    k_eSwitchProprietaryCommandIDs_ResetMCU  = 0x06,
   36.72 +} ESwitchProprietaryCommandIDs;
   36.73 +
   36.74 +typedef enum {
   36.75 +    k_eSwitchDeviceInfoControllerType_JoyConLeft     = 0x1,
   36.76 +    k_eSwitchDeviceInfoControllerType_JoyConRight    = 0x2,
   36.77 +    k_eSwitchDeviceInfoControllerType_ProController  = 0x3,
   36.78 +} ESwitchDeviceInfoControllerType;
   36.79 +
   36.80 +#define k_unSwitchOutputPacketDataLength 49
   36.81 +#define k_unSwitchMaxOutputPacketLength  64
   36.82 +#define k_unSwitchBluetoothPacketLength  k_unSwitchOutputPacketDataLength
   36.83 +#define k_unSwitchUSBPacketLength        k_unSwitchMaxOutputPacketLength
   36.84 +
   36.85 +#define k_unSPIStickCalibrationStartOffset  0x603D
   36.86 +#define k_unSPIStickCalibrationEndOffset    0x604E
   36.87 +#define k_unSPIStickCalibrationLength       (k_unSPIStickCalibrationEndOffset - k_unSPIStickCalibrationStartOffset + 1)
   36.88 +
   36.89 +#pragma pack(1)
   36.90 +typedef struct
   36.91 +{
   36.92 +    Uint8 rgucButtons[2];
   36.93 +    Uint8 ucStickHat;
   36.94 +    Sint16 sJoystickLeft[2];
   36.95 +    Sint16 sJoystickRight[2];
   36.96 +} SwitchSimpleStatePacket_t;
   36.97 +
   36.98 +typedef struct
   36.99 +{
  36.100 +    Uint8 ucCounter;
  36.101 +    Uint8 ucBatteryAndConnection;
  36.102 +    Uint8 rgucButtons[3];
  36.103 +    Uint8 rgucJoystickLeft[3];
  36.104 +    Uint8 rgucJoystickRight[3];
  36.105 +    Uint8 ucVibrationCode;
  36.106 +} SwitchControllerStatePacket_t;
  36.107 +
  36.108 +typedef struct
  36.109 +{
  36.110 +    SwitchControllerStatePacket_t controllerState;
  36.111 +
  36.112 +    struct {
  36.113 +        Sint16 sAccelX;
  36.114 +        Sint16 sAccelY;
  36.115 +        Sint16 sAccelZ;
  36.116 +
  36.117 +        Sint16 sGyroX;
  36.118 +        Sint16 sGyroY;
  36.119 +        Sint16 sGyroZ;
  36.120 +    } imuState[3];
  36.121 +} SwitchStatePacket_t;
  36.122 +
  36.123 +typedef struct
  36.124 +{
  36.125 +    Uint32 unAddress;
  36.126 +    Uint8 ucLength;
  36.127 +} SwitchSPIOpData_t;
  36.128 +
  36.129 +typedef struct
  36.130 +{
  36.131 +    SwitchControllerStatePacket_t m_controllerState;
  36.132 +
  36.133 +    Uint8 ucSubcommandAck;
  36.134 +    Uint8 ucSubcommandID;
  36.135 +
  36.136 +    #define k_unSubcommandDataBytes 35
  36.137 +    union {
  36.138 +        Uint8 rgucSubcommandData[ k_unSubcommandDataBytes ];
  36.139 +
  36.140 +        struct {
  36.141 +            SwitchSPIOpData_t opData;
  36.142 +            Uint8 rgucReadData[ k_unSubcommandDataBytes - sizeof(SwitchSPIOpData_t) ];
  36.143 +        } spiReadData;
  36.144 +
  36.145 +        struct {
  36.146 +            Uint8 rgucFirmwareVersion[2];
  36.147 +            Uint8 ucDeviceType;
  36.148 +            Uint8 ucFiller1;
  36.149 +            Uint8 rgucMACAddress[6];
  36.150 +            Uint8 ucFiller2;
  36.151 +            Uint8 ucColorLocation;
  36.152 +        } deviceInfo;
  36.153 +    };
  36.154 +} SwitchSubcommandInputPacket_t;
  36.155 +
  36.156 +typedef struct
  36.157 +{
  36.158 +    Uint8 rgucData[4];
  36.159 +} SwitchRumbleData_t;
  36.160 +
  36.161 +typedef struct
  36.162 +{
  36.163 +    Uint8 ucPacketType;
  36.164 +    Uint8 ucPacketNumber;
  36.165 +    SwitchRumbleData_t rumbleData[2];
  36.166 +} SwitchCommonOutputPacket_t;
  36.167 +
  36.168 +typedef struct
  36.169 +{
  36.170 +    SwitchCommonOutputPacket_t commonData;
  36.171 +
  36.172 +    Uint8 ucSubcommandID;
  36.173 +    Uint8 rgucSubcommandData[ k_unSwitchOutputPacketDataLength - sizeof(SwitchCommonOutputPacket_t) - 1 ];
  36.174 +} SwitchSubcommandOutputPacket_t;
  36.175 +
  36.176 +typedef struct
  36.177 +{
  36.178 +    Uint8 ucPacketType;
  36.179 +    Uint8 ucProprietaryID;
  36.180 +
  36.181 +    Uint8 rgucProprietaryData[ k_unSwitchOutputPacketDataLength - 1 - 1 ];
  36.182 +} SwitchProprietaryOutputPacket_t;
  36.183 +#pragma pack()
  36.184 +
  36.185 +typedef struct {
  36.186 +    hid_device *dev;
  36.187 +    SDL_bool m_bIsUsingBluetooth;
  36.188 +    Uint8 m_nCommandNumber;
  36.189 +    SwitchCommonOutputPacket_t m_RumblePacket;
  36.190 +    Uint32 m_nRumbleExpiration;
  36.191 +    Uint8 m_rgucReadBuffer[k_unSwitchMaxOutputPacketLength];
  36.192 +    SwitchSimpleStatePacket_t m_lastSimpleState;
  36.193 +    SwitchStatePacket_t m_lastFullState;
  36.194 +
  36.195 +    struct StickCalibrationData {
  36.196 +        struct {
  36.197 +            Sint16 sCenter;
  36.198 +            Sint16 sMin;
  36.199 +            Sint16 sMax;
  36.200 +        } axis[2];
  36.201 +    } m_StickCalData[2];
  36.202 +
  36.203 +    struct StickExtents {
  36.204 +        struct {
  36.205 +            Sint16 sMin;
  36.206 +            Sint16 sMax;
  36.207 +        } axis[2];
  36.208 +    } m_StickExtents[2];
  36.209 +} SDL_DriverSwitch_Context;
  36.210 +
  36.211 +
  36.212 +static SDL_bool
  36.213 +HIDAPI_DriverSwitch_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, int interface_number, Uint16 usage_page, Uint16 usage)
  36.214 +{
  36.215 +    return SDL_IsJoystickNintendoSwitchPro(vendor_id, product_id);
  36.216 +}
  36.217 +
  36.218 +static const char *
  36.219 +HIDAPI_DriverSwitch_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
  36.220 +{
  36.221 +    /* Give a user friendly name for this controller */
  36.222 +    if (SDL_IsJoystickNintendoSwitchPro(vendor_id, product_id)) {
  36.223 +        return "Nintendo Switch Pro Controller";
  36.224 +    }
  36.225 +    return NULL;
  36.226 +}
  36.227 +
  36.228 +static int ReadInput(SDL_DriverSwitch_Context *ctx)
  36.229 +{
  36.230 +    return hid_read_timeout(ctx->dev, ctx->m_rgucReadBuffer, sizeof(ctx->m_rgucReadBuffer), 0);
  36.231 +}
  36.232 +
  36.233 +static int WriteOutput(SDL_DriverSwitch_Context *ctx, Uint8 *data, int size)
  36.234 +{
  36.235 +    return hid_write(ctx->dev, data, size);
  36.236 +}
  36.237 +
  36.238 +static SwitchSubcommandInputPacket_t *ReadSubcommandReply(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs expectedID)
  36.239 +{
  36.240 +    /* Average response time for messages is ~30ms */
  36.241 +    Uint32 TimeoutMs = 100;
  36.242 +    Uint32 startTicks = SDL_GetTicks();
  36.243 +
  36.244 +    int nRead = 0;
  36.245 +    while ((nRead = ReadInput(ctx)) != -1) {
  36.246 +        if (nRead > 0) {
  36.247 +            if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) {
  36.248 +                SwitchSubcommandInputPacket_t *reply = (SwitchSubcommandInputPacket_t *)&ctx->m_rgucReadBuffer[ 1 ];
  36.249 +                if (reply->ucSubcommandID == expectedID && (reply->ucSubcommandAck & 0x80)) {
  36.250 +                    return reply;
  36.251 +                }
  36.252 +            }
  36.253 +        } else {
  36.254 +            SDL_Delay(1);
  36.255 +        }
  36.256 +
  36.257 +        if (SDL_TICKS_PASSED(SDL_GetTicks(), startTicks + TimeoutMs)) {
  36.258 +            break;
  36.259 +        }
  36.260 +    }
  36.261 +    return NULL;
  36.262 +}
  36.263 +
  36.264 +static SDL_bool ReadProprietaryReply(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs expectedID)
  36.265 +{
  36.266 +    /* Average response time for messages is ~30ms */
  36.267 +    Uint32 TimeoutMs = 100;
  36.268 +    Uint32 startTicks = SDL_GetTicks();
  36.269 +
  36.270 +    int nRead = 0;
  36.271 +    while ((nRead = ReadInput(ctx)) != -1) {
  36.272 +        if (nRead > 0) {
  36.273 +            if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_CommandAck && ctx->m_rgucReadBuffer[ 1 ] == expectedID) {
  36.274 +                return SDL_TRUE;
  36.275 +            }
  36.276 +        } else {
  36.277 +            SDL_Delay(1);
  36.278 +        }
  36.279 +
  36.280 +        if (SDL_TICKS_PASSED(SDL_GetTicks(), startTicks + TimeoutMs)) {
  36.281 +            break;
  36.282 +        }
  36.283 +    }
  36.284 +    return SDL_FALSE;
  36.285 +}
  36.286 +
  36.287 +static void ConstructSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandOutputPacket_t *outPacket)
  36.288 +{
  36.289 +    SDL_memset(outPacket, 0, sizeof(*outPacket));
  36.290 +
  36.291 +    outPacket->commonData.ucPacketType = k_eSwitchOutputReportIDs_RumbleAndSubcommand;
  36.292 +    outPacket->commonData.ucPacketNumber = ctx->m_nCommandNumber;
  36.293 +
  36.294 +    SDL_memcpy(&outPacket->commonData.rumbleData, &ctx->m_RumblePacket.rumbleData, sizeof(ctx->m_RumblePacket.rumbleData));
  36.295 +
  36.296 +    outPacket->ucSubcommandID = ucCommandID;
  36.297 +    SDL_memcpy(outPacket->rgucSubcommandData, pBuf, ucLen);
  36.298 +
  36.299 +    ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
  36.300 +}
  36.301 +
  36.302 +static SDL_bool WritePacket(SDL_DriverSwitch_Context *ctx, void *pBuf, Uint8 ucLen)
  36.303 +{
  36.304 +    Uint8 rgucBuf[k_unSwitchMaxOutputPacketLength];
  36.305 +    const size_t unWriteSize = ctx->m_bIsUsingBluetooth ? k_unSwitchBluetoothPacketLength : k_unSwitchUSBPacketLength;
  36.306 +
  36.307 +    if (ucLen > k_unSwitchOutputPacketDataLength) {
  36.308 +        return SDL_FALSE;
  36.309 +    }
  36.310 +
  36.311 +    if (ucLen < unWriteSize) {
  36.312 +        SDL_memcpy(rgucBuf, pBuf, ucLen);
  36.313 +        SDL_memset(rgucBuf+ucLen, 0, unWriteSize-ucLen);
  36.314 +        pBuf = rgucBuf;
  36.315 +        ucLen = (Uint8)unWriteSize;
  36.316 +    }
  36.317 +    return (WriteOutput(ctx, (Uint8 *)pBuf, ucLen) >= 0);
  36.318 +}
  36.319 +
  36.320 +static SDL_bool WriteSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandInputPacket_t **ppReply)
  36.321 +{
  36.322 +    int nRetries = 5;
  36.323 +    SwitchSubcommandInputPacket_t *reply = NULL;
  36.324 +
  36.325 +    while (!reply && nRetries--) {
  36.326 +        SwitchSubcommandOutputPacket_t commandPacket;
  36.327 +        ConstructSubcommand(ctx, ucCommandID, pBuf, ucLen, &commandPacket);
  36.328 +
  36.329 +        if (!WritePacket(ctx, &commandPacket, sizeof(commandPacket))) {
  36.330 +            continue;
  36.331 +        }
  36.332 +
  36.333 +        reply = ReadSubcommandReply(ctx, ucCommandID);
  36.334 +    }
  36.335 +
  36.336 +    if (ppReply) {
  36.337 +        *ppReply = reply;
  36.338 +    }
  36.339 +    return reply != NULL;
  36.340 +}
  36.341 +
  36.342 +static SDL_bool WriteProprietary(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs ucCommand, Uint8 *pBuf, Uint8 ucLen, SDL_bool waitForReply)
  36.343 +{
  36.344 +    int nRetries = 5;
  36.345 +
  36.346 +    while (nRetries--) {
  36.347 +        SwitchProprietaryOutputPacket_t packet;
  36.348 +
  36.349 +        if ((!pBuf && ucLen > 0) || ucLen > sizeof(packet.rgucProprietaryData)) {
  36.350 +            return SDL_FALSE;
  36.351 +        }
  36.352 +
  36.353 +        packet.ucPacketType = k_eSwitchOutputReportIDs_Proprietary;
  36.354 +        packet.ucProprietaryID = ucCommand;
  36.355 +        SDL_memcpy(packet.rgucProprietaryData, pBuf, ucLen);
  36.356 +
  36.357 +        if (!WritePacket(ctx, &packet, sizeof(packet))) {
  36.358 +            continue;
  36.359 +        }
  36.360 +
  36.361 +        if (!waitForReply || ReadProprietaryReply(ctx, ucCommand)) {
  36.362 +            return SDL_TRUE;
  36.363 +        }
  36.364 +    }
  36.365 +    return SDL_FALSE;
  36.366 +}
  36.367 +
  36.368 +static void SetNeutralRumble(SwitchRumbleData_t *pRumble)
  36.369 +{
  36.370 +    pRumble->rgucData[0] = 0x00;
  36.371 +    pRumble->rgucData[1] = 0x01;
  36.372 +    pRumble->rgucData[2] = 0x40;
  36.373 +    pRumble->rgucData[3] = 0x40;
  36.374 +}
  36.375 +
  36.376 +static void EncodeRumble(SwitchRumbleData_t *pRumble, Uint16 usHighFreq, Uint8 ucHighFreqAmp, Uint8 ucLowFreq, Uint16 usLowFreqAmp)
  36.377 +{
  36.378 +    if (ucHighFreqAmp > 0 || usLowFreqAmp > 0) {
  36.379 +        // High-band frequency and low-band amplitude are actually nine-bits each so they
  36.380 +        // take a bit from the high-band amplitude and low-band frequency bytes respectively
  36.381 +        pRumble->rgucData[0] = usHighFreq & 0xFF;
  36.382 +        pRumble->rgucData[1] = ucHighFreqAmp | ((usHighFreq >> 8) & 0x01);
  36.383 +
  36.384 +        pRumble->rgucData[2]  = ucLowFreq | ((usLowFreqAmp >> 8) & 0x80);
  36.385 +        pRumble->rgucData[3]  = usLowFreqAmp & 0xFF;
  36.386 +
  36.387 +#ifdef DEBUG_RUMBLE
  36.388 +        SDL_Log("Freq: %.2X %.2X  %.2X, Amp: %.2X  %.2X %.2X\n",
  36.389 +            usHighFreq & 0xFF, ((usHighFreq >> 8) & 0x01), ucLowFreq,
  36.390 +            ucHighFreqAmp, ((usLowFreqAmp >> 8) & 0x80), usLowFreqAmp & 0xFF);
  36.391 +#endif
  36.392 +    } else {
  36.393 +        SetNeutralRumble(pRumble);
  36.394 +    }
  36.395 +}
  36.396 +
  36.397 +static SDL_bool WriteRumble(SDL_DriverSwitch_Context *ctx)
  36.398 +{
  36.399 +    /* Write into m_RumblePacket rather than a temporary buffer to allow the current rumble state
  36.400 +     * to be retained for subsequent rumble or subcommand packets sent to the controller
  36.401 +     */
  36.402 +    ctx->m_RumblePacket.ucPacketType = k_eSwitchOutputReportIDs_Rumble;
  36.403 +    ctx->m_RumblePacket.ucPacketNumber = ctx->m_nCommandNumber;
  36.404 +    ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
  36.405 +
  36.406 +    return WritePacket(ctx, (Uint8 *)&ctx->m_RumblePacket, sizeof(ctx->m_RumblePacket));
  36.407 +}
  36.408 +
  36.409 +static SDL_bool BTrySetupUSB(SDL_DriverSwitch_Context *ctx)
  36.410 +{
  36.411 +    /* We have to send a connection handshake to the controller when communicating over USB
  36.412 +     * before we're able to send it other commands. Luckily this command is not supported
  36.413 +     * over Bluetooth, so we can use the controller's lack of response as a way to
  36.414 +     * determine if the connection is over USB or Bluetooth
  36.415 +     */
  36.416 +    if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, SDL_TRUE)) {
  36.417 +        return SDL_FALSE;
  36.418 +    }
  36.419 +    if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_HighSpeed, NULL, 0, SDL_TRUE)) {
  36.420 +        return SDL_FALSE;
  36.421 +    }
  36.422 +    if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, SDL_TRUE)) {
  36.423 +        return SDL_FALSE;
  36.424 +    }
  36.425 +    return SDL_TRUE;
  36.426 +}
  36.427 +
  36.428 +static SDL_bool SetVibrationEnabled(SDL_DriverSwitch_Context *ctx, Uint8 enabled)
  36.429 +{
  36.430 +    return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_EnableVibration, &enabled, sizeof(enabled), NULL);
  36.431 +
  36.432 +}
  36.433 +static SDL_bool SetInputMode(SDL_DriverSwitch_Context *ctx, Uint8 input_mode)
  36.434 +{
  36.435 +    return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetInputReportMode, &input_mode, 1, NULL);
  36.436 +}
  36.437 +
  36.438 +static SDL_bool SetHomeLED(SDL_DriverSwitch_Context *ctx, Uint8 brightness)
  36.439 +{
  36.440 +    Uint8 ucLedIntensity = 0;
  36.441 +    Uint8 rgucBuffer[4];
  36.442 +
  36.443 +    if (brightness > 0) {
  36.444 +        if (brightness < 65) {
  36.445 +            ucLedIntensity = (brightness + 5) / 10;
  36.446 +        } else {
  36.447 +            ucLedIntensity = (Uint8)SDL_ceilf(0xF * SDL_powf((float)brightness / 100.f, 2.13f));
  36.448 +        }
  36.449 +    }
  36.450 +
  36.451 +    rgucBuffer[0] = (0x0 << 4) | 0x1;  /* 0 mini cycles (besides first), cycle duration 8ms */
  36.452 +    rgucBuffer[1] = ((ucLedIntensity & 0xF) << 4) | 0x0;  /* LED start intensity (0x0-0xF), 0 cycles (LED stays on at start intensity after first cycle) */
  36.453 +    rgucBuffer[2] = ((ucLedIntensity & 0xF) << 4) | 0x0;  /* First cycle LED intensity, 0x0 intensity for second cycle */
  36.454 +    rgucBuffer[3] = (0x0 << 4) | 0x0;  /* 8ms fade transition to first cycle, 8ms first cycle LED duration */
  36.455 +
  36.456 +    return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetHomeLight, rgucBuffer, sizeof(rgucBuffer), NULL);
  36.457 +}
  36.458 +
  36.459 +static SDL_bool SetSlotLED(SDL_DriverSwitch_Context *ctx, Uint8 slot)
  36.460 +{
  36.461 +    Uint8 led_data = (1 << slot);
  36.462 +    return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetPlayerLights, &led_data, sizeof(led_data), NULL);
  36.463 +}
  36.464 +
  36.465 +static SDL_bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)
  36.466 +{
  36.467 +    Uint8 *pStickCal;
  36.468 +    size_t stick, axis;
  36.469 +    SwitchSubcommandInputPacket_t *reply = NULL;
  36.470 +
  36.471 +    /* Read Calibration Info */
  36.472 +    SwitchSPIOpData_t readParams;
  36.473 +    readParams.unAddress = k_unSPIStickCalibrationStartOffset;
  36.474 +    readParams.ucLength = k_unSPIStickCalibrationLength;
  36.475 +
  36.476 +    if (!WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply)) {
  36.477 +        return SDL_FALSE;
  36.478 +    }
  36.479 +
  36.480 +    /* Stick calibration values are 12-bits each and are packed by bit
  36.481 +     * For whatever reason the fields are in a different order for each stick
  36.482 +     * Left:  X-Max, Y-Max, X-Center, Y-Center, X-Min, Y-Min
  36.483 +     * Right: X-Center, Y-Center, X-Min, Y-Min, X-Max, Y-Max
  36.484 +     */
  36.485 +    pStickCal = reply->spiReadData.rgucReadData;
  36.486 +
  36.487 +    /* Left stick */
  36.488 +    ctx->m_StickCalData[0].axis[0].sMax    = ((pStickCal[1] << 8) & 0xF00) | pStickCal[0];     /* X Axis max above center */
  36.489 +    ctx->m_StickCalData[0].axis[1].sMax    = (pStickCal[2] << 4) | (pStickCal[1] >> 4);         /* Y Axis max above center */
  36.490 +    ctx->m_StickCalData[0].axis[0].sCenter = ((pStickCal[4] << 8) & 0xF00) | pStickCal[3];     /* X Axis center */
  36.491 +    ctx->m_StickCalData[0].axis[1].sCenter = (pStickCal[5] << 4) | (pStickCal[4] >> 4);        /* Y Axis center */
  36.492 +    ctx->m_StickCalData[0].axis[0].sMin    = ((pStickCal[7] << 8) & 0xF00) | pStickCal[6];      /* X Axis min below center */
  36.493 +    ctx->m_StickCalData[0].axis[1].sMin    = (pStickCal[8] << 4) | (pStickCal[7] >> 4);        /* Y Axis min below center */
  36.494 +
  36.495 +    /* Right stick */
  36.496 +    ctx->m_StickCalData[1].axis[0].sCenter = ((pStickCal[10] << 8) & 0xF00) | pStickCal[9];     /* X Axis center */
  36.497 +    ctx->m_StickCalData[1].axis[1].sCenter = (pStickCal[11] << 4) | (pStickCal[10] >> 4);      /* Y Axis center */
  36.498 +    ctx->m_StickCalData[1].axis[0].sMin    = ((pStickCal[13] << 8) & 0xF00) | pStickCal[12];    /* X Axis min below center */
  36.499 +    ctx->m_StickCalData[1].axis[1].sMin    = (pStickCal[14] << 4) | (pStickCal[13] >> 4);      /* Y Axis min below center */
  36.500 +    ctx->m_StickCalData[1].axis[0].sMax    = ((pStickCal[16] << 8) & 0xF00) | pStickCal[15];    /* X Axis max above center */
  36.501 +    ctx->m_StickCalData[1].axis[1].sMax    = (pStickCal[17] << 4) | (pStickCal[16] >> 4);      /* Y Axis max above center */
  36.502 +
  36.503 +    /* Filter out any values that were uninitialized (0xFFF) in the SPI read */
  36.504 +    for (stick = 0; stick < 2; ++stick) {
  36.505 +        for (axis = 0; axis < 2; ++axis) {
  36.506 +            if (ctx->m_StickCalData[stick].axis[axis].sCenter == 0xFFF) {
  36.507 +                ctx->m_StickCalData[stick].axis[axis].sCenter = 0;
  36.508 +            }
  36.509 +            if (ctx->m_StickCalData[stick].axis[axis].sMax == 0xFFF) {
  36.510 +                ctx->m_StickCalData[stick].axis[axis].sMax = 0;
  36.511 +            }
  36.512 +            if (ctx->m_StickCalData[stick].axis[axis].sMin == 0xFFF) {
  36.513 +                ctx->m_StickCalData[stick].axis[axis].sMin = 0;
  36.514 +            }
  36.515 +        }
  36.516 +    }
  36.517 +
  36.518 +    if (ctx->m_bIsUsingBluetooth) {
  36.519 +        for (stick = 0; stick < 2; ++stick) {
  36.520 +            for(axis = 0; axis < 2; ++axis) {
  36.521 +                ctx->m_StickExtents[stick].axis[axis].sMin = (Sint16)(SDL_MIN_SINT16 * 0.5f);
  36.522 +                ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(SDL_MAX_SINT16 * 0.5f);
  36.523 +            }
  36.524 +        }
  36.525 +    } else {
  36.526 +        for (stick = 0; stick < 2; ++stick) {
  36.527 +            for(axis = 0; axis < 2; ++axis) {
  36.528 +                ctx->m_StickExtents[stick].axis[axis].sMin = -(Sint16)(ctx->m_StickCalData[stick].axis[axis].sMin * 0.7f);
  36.529 +                ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sMax * 0.7f);
  36.530 +            }
  36.531 +        }
  36.532 +    }
  36.533 +    return SDL_TRUE;
  36.534 +}
  36.535 +
  36.536 +static float fsel(float fComparand, float fValGE, float fLT)
  36.537 +{
  36.538 +    return fComparand >= 0 ? fValGE : fLT;
  36.539 +}
  36.540 +
  36.541 +static float RemapVal(float val, float A, float B, float C, float D)
  36.542 +{
  36.543 +    if (A == B) {
  36.544 +        return fsel(val - B , D , C);
  36.545 +    }
  36.546 +    return C + (D - C) * (val - A) / (B - A);
  36.547 +}
  36.548 +
  36.549 +static Sint16 ApplyStickCalibrationCentered(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue, Sint16 sCenter)
  36.550 +{
  36.551 +    sRawValue -= sCenter;
  36.552 +
  36.553 +    if (sRawValue > ctx->m_StickExtents[nStick].axis[nAxis].sMax) {
  36.554 +        ctx->m_StickExtents[nStick].axis[nAxis].sMax = sRawValue;
  36.555 +    }
  36.556 +    if (sRawValue < ctx->m_StickExtents[nStick].axis[nAxis].sMin) {
  36.557 +        ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue;
  36.558 +    }
  36.559 +
  36.560 +    if (sRawValue > 0) {
  36.561 +        return (Sint16)(RemapVal(sRawValue, 0, ctx->m_StickExtents[nStick].axis[nAxis].sMax, 0, SDL_MAX_SINT16));
  36.562 +    } else {
  36.563 +        return (Sint16)(RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, 0, SDL_MIN_SINT16, 0));
  36.564 +    }
  36.565 +}
  36.566 +
  36.567 +static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue)
  36.568 +{
  36.569 +    return ApplyStickCalibrationCentered(ctx, nStick, nAxis, sRawValue, ctx->m_StickCalData[nStick].axis[nAxis].sCenter);
  36.570 +}
  36.571 +
  36.572 +static SDL_bool
  36.573 +HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
  36.574 +{
  36.575 +    SDL_DriverSwitch_Context *ctx;
  36.576 +    Uint8 input_mode;
  36.577 +
  36.578 +    ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx));
  36.579 +    if (!ctx) {
  36.580 +        SDL_OutOfMemory();
  36.581 +        return SDL_FALSE;
  36.582 +    }
  36.583 +    ctx->dev = dev;
  36.584 +
  36.585 +    *context = ctx;
  36.586 +
  36.587 +    /* Initialize rumble data */
  36.588 +    SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
  36.589 +    SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
  36.590 +
  36.591 +    /* Try setting up USB mode, and if that fails we're using Bluetooth */
  36.592 +    if (!BTrySetupUSB(ctx)) {
  36.593 +        ctx->m_bIsUsingBluetooth = SDL_TRUE;
  36.594 +    }
  36.595 +
  36.596 +    if (!LoadStickCalibration(ctx)) {
  36.597 +        SDL_SetError("Couldn't load stick calibration");
  36.598 +        SDL_free(ctx);
  36.599 +        return SDL_FALSE;
  36.600 +    }
  36.601 +
  36.602 +    if (!SetVibrationEnabled(ctx, 1)) {
  36.603 +        SDL_SetError("Couldn't enable vibration");
  36.604 +        SDL_free(ctx);
  36.605 +        return SDL_FALSE;
  36.606 +    }
  36.607 +
  36.608 +    /* Set the desired input mode */
  36.609 +    if (ctx->m_bIsUsingBluetooth) {
  36.610 +        input_mode = k_eSwitchInputReportIDs_SimpleControllerState;
  36.611 +    } else {
  36.612 +        input_mode = k_eSwitchInputReportIDs_FullControllerState;
  36.613 +    }
  36.614 +    if (!SetInputMode(ctx, input_mode)) {
  36.615 +        SDL_SetError("Couldn't set input mode");
  36.616 +        SDL_free(ctx);
  36.617 +        return SDL_FALSE;
  36.618 +    }
  36.619 +
  36.620 +    /* Start sending USB reports */
  36.621 +    if (!ctx->m_bIsUsingBluetooth) {
  36.622 +        /* ForceUSB doesn't generate an ACK, so don't wait for a reply */
  36.623 +        if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, SDL_FALSE)) {
  36.624 +            SDL_SetError("Couldn't start USB reports");
  36.625 +            SDL_free(ctx);
  36.626 +            return SDL_FALSE;
  36.627 +        }
  36.628 +    }
  36.629 +
  36.630 +    /* Set the LED state */
  36.631 +    SetHomeLED(ctx, 100);
  36.632 +    SetSlotLED(ctx, (joystick->instance_id % 4));
  36.633 +
  36.634 +    /* Initialize the joystick capabilities */
  36.635 +    joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
  36.636 +    joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
  36.637 +    joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
  36.638 +
  36.639 +    return SDL_TRUE;
  36.640 +}
  36.641 +
  36.642 +static int
  36.643 +HIDAPI_DriverSwitch_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
  36.644 +{
  36.645 +    SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context;
  36.646 +
  36.647 +    /* Experimentally determined rumble values. These will only matter on some controllers as tested ones
  36.648 +     * seem to disregard these and just use any non-zero rumble values as a binary flag for constant rumble
  36.649 +     *
  36.650 +     * More information about these values can be found here:
  36.651 +     * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
  36.652 +     */
  36.653 +    const Uint16 k_usHighFreq = 0x0074;
  36.654 +    const Uint8  k_ucHighFreqAmp = 0xBE;
  36.655 +    const Uint8  k_ucLowFreq = 0x3D;
  36.656 +    const Uint16 k_usLowFreqAmp = 0x806F;
  36.657 +
  36.658 +    if (low_frequency_rumble) {
  36.659 +        EncodeRumble(&ctx->m_RumblePacket.rumbleData[0], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
  36.660 +    } else {
  36.661 +        SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
  36.662 +    }
  36.663 +
  36.664 +    if (high_frequency_rumble) {
  36.665 +        EncodeRumble(&ctx->m_RumblePacket.rumbleData[1], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
  36.666 +    } else {
  36.667 +        SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
  36.668 +    }
  36.669 +
  36.670 +    if (!WriteRumble(ctx)) {
  36.671 +        SDL_SetError("Couldn't send rumble packet");
  36.672 +        return -1;
  36.673 +    }
  36.674 +
  36.675 +    if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
  36.676 +        ctx->m_nRumbleExpiration = SDL_GetTicks() + duration_ms;
  36.677 +    } else {
  36.678 +        ctx->m_nRumbleExpiration = 0;
  36.679 +    }
  36.680 +    return 0;
  36.681 +}
  36.682 +
  36.683 +static void HandleSimpleControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
  36.684 +{
  36.685 +    /* 0x8000 is the neutral value for all joystick axes */
  36.686 +    const Uint16 usJoystickCenter = 0x8000;
  36.687 +    Sint16 axis;
  36.688 +
  36.689 +    if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
  36.690 +        Uint8 data = packet->rgucButtons[0];
  36.691 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
  36.692 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
  36.693 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
  36.694 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
  36.695 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
  36.696 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
  36.697 +
  36.698 +        axis = (data & 0x40) ? 32767 : -32768;
  36.699 +        SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
  36.700 +
  36.701 +        axis = (data & 0x80) ? 32767 : -32768;
  36.702 +        SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
  36.703 +    }
  36.704 +
  36.705 +    if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
  36.706 +        Uint8 data = packet->rgucButtons[1];
  36.707 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
  36.708 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
  36.709 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
  36.710 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
  36.711 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
  36.712 +    }
  36.713 +
  36.714 +    if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
  36.715 +        SDL_bool dpad_up = SDL_FALSE;
  36.716 +        SDL_bool dpad_down = SDL_FALSE;
  36.717 +        SDL_bool dpad_left = SDL_FALSE;
  36.718 +        SDL_bool dpad_right = SDL_FALSE;
  36.719 +
  36.720 +        switch (packet->ucStickHat) {
  36.721 +        case 0:
  36.722 +            dpad_up = SDL_TRUE;
  36.723 +            break;
  36.724 +        case 1:
  36.725 +            dpad_up = SDL_TRUE;
  36.726 +            dpad_right = SDL_TRUE;
  36.727 +            break;
  36.728 +        case 2:
  36.729 +            dpad_right = SDL_TRUE;
  36.730 +            break;
  36.731 +        case 3:
  36.732 +            dpad_right = SDL_TRUE;
  36.733 +            dpad_down = SDL_TRUE;
  36.734 +            break;
  36.735 +        case 4:
  36.736 +            dpad_down = SDL_TRUE;
  36.737 +            break;
  36.738 +        case 5:
  36.739 +            dpad_left = SDL_TRUE;
  36.740 +            dpad_down = SDL_TRUE;
  36.741 +            break;
  36.742 +        case 6:
  36.743 +            dpad_left = SDL_TRUE;
  36.744 +            break;
  36.745 +        case 7:
  36.746 +            dpad_up = SDL_TRUE;
  36.747 +            dpad_left = SDL_TRUE;
  36.748 +            break;
  36.749 +        default:
  36.750 +            break;
  36.751 +        }
  36.752 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
  36.753 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
  36.754 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
  36.755 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
  36.756 +    }
  36.757 +
  36.758 +    axis = ApplyStickCalibrationCentered(ctx, 0, 0, packet->sJoystickLeft[0], (Sint16)usJoystickCenter);
  36.759 +    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
  36.760 +
  36.761 +    axis = ApplyStickCalibrationCentered(ctx, 0, 1, packet->sJoystickLeft[1], (Sint16)usJoystickCenter);
  36.762 +    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
  36.763 +
  36.764 +    axis = ApplyStickCalibrationCentered(ctx, 1, 0, packet->sJoystickRight[0], (Sint16)usJoystickCenter);
  36.765 +    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
  36.766 +
  36.767 +    axis = ApplyStickCalibrationCentered(ctx, 1, 1, packet->sJoystickRight[1], (Sint16)usJoystickCenter);
  36.768 +    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
  36.769 +
  36.770 +    ctx->m_lastSimpleState = *packet;
  36.771 +}
  36.772 +
  36.773 +static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)
  36.774 +{
  36.775 +    Sint16 axis;
  36.776 +
  36.777 +    if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) {
  36.778 +        Uint8 data = packet->controllerState.rgucButtons[0];
  36.779 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
  36.780 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
  36.781 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
  36.782 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
  36.783 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
  36.784 +        axis = (data & 0x80) ? 32767 : -32768;
  36.785 +        SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
  36.786 +    }
  36.787 +
  36.788 +    if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
  36.789 +        Uint8 data = packet->controllerState.rgucButtons[1];
  36.790 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
  36.791 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
  36.792 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
  36.793 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
  36.794 +
  36.795 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
  36.796 +    }
  36.797 +
  36.798 +    if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {
  36.799 +        Uint8 data = packet->controllerState.rgucButtons[2];
  36.800 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
  36.801 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
  36.802 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
  36.803 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
  36.804 +        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
  36.805 +        axis = (data & 0x80) ? 32767 : -32768;
  36.806 +        SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
  36.807 +    }
  36.808 +
  36.809 +    axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);
  36.810 +    axis = ApplyStickCalibration(ctx, 0, 0, axis);
  36.811 +    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
  36.812 +
  36.813 +    axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4);
  36.814 +    axis = ApplyStickCalibration(ctx, 0, 1, axis);
  36.815 +    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, ~axis);
  36.816 +
  36.817 +    axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8);
  36.818 +    axis = ApplyStickCalibration(ctx, 1, 0, axis);
  36.819 +    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
  36.820 +
  36.821 +    axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4);
  36.822 +    axis = ApplyStickCalibration(ctx, 1, 1, axis);
  36.823 +    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, ~axis);
  36.824 +
  36.825 +    /* High nibble of battery/connection byte is battery level, low nibble is connection status
  36.826 +     * LSB of connection nibble is USB/Switch connection status
  36.827 +     */
  36.828 +    if (packet->controllerState.ucBatteryAndConnection & 0x1) {
  36.829 +        joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
  36.830 +    } else {
  36.831 +        /* LSB of the battery nibble is used to report charging.
  36.832 +         * The battery level is reported from 0(empty)-8(full)
  36.833 +         */
  36.834 +        int level = (packet->controllerState.ucBatteryAndConnection & 0xE0) >> 4;
  36.835 +        if (level == 0) {
  36.836 +            joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
  36.837 +        } else if (level <= 2) {
  36.838 +            joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
  36.839 +        } else if (level <= 6) {
  36.840 +            joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
  36.841 +        } else {
  36.842 +            joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
  36.843 +        }
  36.844 +    }
  36.845 +
  36.846 +    ctx->m_lastFullState = *packet;
  36.847 +}
  36.848 +
  36.849 +static void
  36.850 +HIDAPI_DriverSwitch_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
  36.851 +{
  36.852 +    SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context;
  36.853 +
  36.854 +    while (ReadInput(ctx) > 0) {
  36.855 +        switch (ctx->m_rgucReadBuffer[0]) {
  36.856 +        case k_eSwitchInputReportIDs_SimpleControllerState:
  36.857 +            HandleSimpleControllerState(joystick, ctx, (SwitchSimpleStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
  36.858 +            break;
  36.859 +        case k_eSwitchInputReportIDs_FullControllerState:
  36.860 +            HandleFullControllerState(joystick, ctx, (SwitchStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
  36.861 +            break;
  36.862 +        default:
  36.863 +            break;
  36.864 +        }
  36.865 +    }
  36.866 +
  36.867 +    if (ctx->m_nRumbleExpiration) {
  36.868 +        Uint32 now = SDL_GetTicks();
  36.869 +        if (SDL_TICKS_PASSED(now, ctx->m_nRumbleExpiration)) {
  36.870 +            HIDAPI_DriverSwitch_Rumble(joystick, dev, context, 0, 0, 0);
  36.871 +        }
  36.872 +    }
  36.873 +}
  36.874 +
  36.875 +static void
  36.876 +HIDAPI_DriverSwitch_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
  36.877 +{
  36.878 +    SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context;
  36.879 +
  36.880 +    /* Restore simple input mode for other applications */
  36.881 +    SetInputMode(ctx, k_eSwitchInputReportIDs_SimpleControllerState);
  36.882 +
  36.883 +    SDL_free(context);
  36.884 +}
  36.885 +
  36.886 +SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch =
  36.887 +{
  36.888 +    SDL_HINT_JOYSTICK_HIDAPI_SWITCH,
  36.889 +    SDL_TRUE,
  36.890 +    HIDAPI_DriverSwitch_IsSupportedDevice,
  36.891 +    HIDAPI_DriverSwitch_GetDeviceName,
  36.892 +    HIDAPI_DriverSwitch_Init,
  36.893 +    HIDAPI_DriverSwitch_Rumble,
  36.894 +    HIDAPI_DriverSwitch_Update,
  36.895 +    HIDAPI_DriverSwitch_Quit
  36.896 +};
  36.897 +
  36.898 +#endif /* SDL_JOYSTICK_HIDAPI_SWITCH */
  36.899 +
  36.900 +#endif /* SDL_JOYSTICK_HIDAPI */
  36.901 +
  36.902 +/* vi: set ts=4 sw=4 expandtab: */
    37.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    37.2 +++ b/src/joystick/hidapi/SDL_hidapi_xbox360.c	Thu Aug 09 16:00:17 2018 -0700
    37.3 @@ -0,0 +1,363 @@
    37.4 +/*
    37.5 +  Simple DirectMedia Layer
    37.6 +  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
    37.7 +
    37.8 +  This software is provided 'as-is', without any express or implied
    37.9 +  warranty.  In no event will the authors be held liable for any damages
   37.10 +  arising from the use of this software.
   37.11 +
   37.12 +  Permission is granted to anyone to use this software for any purpose,
   37.13 +  including commercial applications, and to alter it and redistribute it
   37.14 +  freely, subject to the following restrictions:
   37.15 +
   37.16 +  1. The origin of this software must not be misrepresented; you must not
   37.17 +     claim that you wrote the original software. If you use this software
   37.18 +     in a product, an acknowledgment in the product documentation would be
   37.19 +     appreciated but is not required.
   37.20 +  2. Altered source versions must be plainly marked as such, and must not be
   37.21 +     misrepresented as being the original software.
   37.22 +  3. This notice may not be removed or altered from any source distribution.
   37.23 +*/
   37.24 +#include "../../SDL_internal.h"
   37.25 +
   37.26 +#ifdef SDL_JOYSTICK_HIDAPI
   37.27 +
   37.28 +#include "SDL_hints.h"
   37.29 +#include "SDL_log.h"
   37.30 +#include "SDL_events.h"
   37.31 +#include "SDL_timer.h"
   37.32 +#include "SDL_joystick.h"
   37.33 +#include "SDL_gamecontroller.h"
   37.34 +#include "../SDL_sysjoystick.h"
   37.35 +#include "SDL_hidapijoystick_c.h"
   37.36 +
   37.37 +
   37.38 +#ifdef SDL_JOYSTICK_HIDAPI_XBOX360
   37.39 +
   37.40 +#define USB_PACKET_LENGTH   64
   37.41 +
   37.42 +typedef struct
   37.43 +{
   37.44 +    Uint16 vendor_id;
   37.45 +    Uint16 product_id;
   37.46 +    const char *name;
   37.47 +} SDL_DriverXbox360_DeviceName;
   37.48 +
   37.49 +static const SDL_DriverXbox360_DeviceName xbox360_devicenames[] = {
   37.50 +    { 0x0079, 0x18d4, "GPD Win 2 X-Box Controller" },
   37.51 +    { 0x044f, 0xb326, "Thrustmaster Gamepad GP XID" },
   37.52 +    { 0x045e, 0x028e, "Microsoft X-Box 360 pad" },
   37.53 +    { 0x045e, 0x028f, "Microsoft X-Box 360 pad v2" },
   37.54 +    { 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)" },
   37.55 +    { 0x045e, 0x0719, "Xbox 360 Wireless Receiver" },
   37.56 +    { 0x046d, 0xc21d, "Logitech Gamepad F310" },
   37.57 +    { 0x046d, 0xc21e, "Logitech Gamepad F510" },
   37.58 +    { 0x046d, 0xc21f, "Logitech Gamepad F710" },
   37.59 +    { 0x046d, 0xc242, "Logitech Chillstream Controller" },
   37.60 +    { 0x046d, 0xcaa3, "Logitech DriveFx Racing Wheel" },
   37.61 +    { 0x056e, 0x2004, "Elecom JC-U3613M" },
   37.62 +    { 0x06a3, 0xf51a, "Saitek P3600" },
   37.63 +    { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller" },
   37.64 +    { 0x0738, 0x4718, "Mad Catz Street Fighter IV FightStick SE" },
   37.65 +    { 0x0738, 0x4726, "Mad Catz Xbox 360 Controller" },
   37.66 +    { 0x0738, 0x4728, "Mad Catz Street Fighter IV FightPad" },
   37.67 +    { 0x0738, 0x4736, "Mad Catz MicroCon Gamepad" },
   37.68 +    { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)" },
   37.69 +    { 0x0738, 0x4740, "Mad Catz Beat Pad" },
   37.70 +    { 0x0738, 0x4758, "Mad Catz Arcade Game Stick" },
   37.71 +    { 0x0738, 0x9871, "Mad Catz Portable Drum" },
   37.72 +    { 0x0738, 0xb726, "Mad Catz Xbox controller - MW2" },
   37.73 +    { 0x0738, 0xb738, "Mad Catz MVC2TE Stick 2" },
   37.74 +    { 0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad" },
   37.75 +    { 0x0738, 0xcb02, "Saitek Cyborg Rumble Pad - PC/Xbox 360" },
   37.76 +    { 0x0738, 0xcb03, "Saitek P3200 Rumble Pad - PC/Xbox 360" },
   37.77 +    { 0x0738, 0xcb29, "Saitek Aviator Stick AV8R02" },
   37.78 +    { 0x0738, 0xf738, "Super SFIV FightStick TE S" },
   37.79 +    { 0x07ff, 0xffff, "Mad Catz GamePad" },
   37.80 +    { 0x0e6f, 0x0105, "HSM3 Xbox360 dancepad" },
   37.81 +    { 0x0e6f, 0x0113, "Afterglow AX.1 Gamepad for Xbox 360" },
   37.82 +    { 0x0e6f, 0x011f, "Rock Candy Gamepad Wired Controller" },
   37.83 +    { 0x0e6f, 0x0131, "PDP EA Sports Controller" },
   37.84 +    { 0x0e6f, 0x0133, "Xbox 360 Wired Controller" },
   37.85 +    { 0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller" },
   37.86 +    { 0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360" },
   37.87 +    { 0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360" },
   37.88 +    { 0x0e6f, 0x0301, "Logic3 Controller" },
   37.89 +    { 0x0e6f, 0x0401, "Logic3 Controller" },
   37.90 +    { 0x0e6f, 0x0413, "Afterglow AX.1 Gamepad for Xbox 360" },
   37.91 +    { 0x0e6f, 0x0501, "PDP Xbox 360 Controller" },
   37.92 +    { 0x0e6f, 0xf900, "PDP Afterglow AX.1" },
   37.93 +    { 0x0f0d, 0x000a, "Hori Co. DOA4 FightStick" },
   37.94 +    { 0x0f0d, 0x000c, "Hori PadEX Turbo" },
   37.95 +    { 0x0f0d, 0x000d, "Hori Fighting Stick EX2" },
   37.96 +    { 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX" },
   37.97 +    { 0x0f0d, 0x001b, "Hori Real Arcade Pro VX" },
   37.98 +    { 0x11c9, 0x55f0, "Nacon GC-100XF" },
   37.99 +    { 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad" },
  37.100 +    { 0x12ab, 0x0301, "PDP AFTERGLOW AX.1" },
  37.101 +    { 0x12ab, 0x0303, "Mortal Kombat Klassic FightStick" },
  37.102 +    { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer" },
  37.103 +    { 0x1430, 0xf801, "RedOctane Controller" },
  37.104 +    { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller" },
  37.105 +    { 0x1532, 0x0037, "Razer Sabertooth" },
  37.106 +    { 0x15e4, 0x3f00, "Power A Mini Pro Elite" },
  37.107 +    { 0x15e4, 0x3f0a, "Xbox Airflo wired controller" },
  37.108 +    { 0x15e4, 0x3f10, "Batarang Xbox 360 controller" },
  37.109 +    { 0x162e, 0xbeef, "Joytech Neo-Se Take2" },
  37.110 +    { 0x1689, 0xfd00, "Razer Onza Tournament Edition" },
  37.111 +    { 0x1689, 0xfd01, "Razer Onza Classic Edition" },
  37.112 +    { 0x1689, 0xfe00, "Razer Sabertooth" },
  37.113 +    { 0x1bad, 0x0002, "Harmonix Rock Band Guitar" },
  37.114 +    { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit" },
  37.115 +    { 0x1bad, 0x0130, "Ion Drum Rocker" },
  37.116 +    { 0x1bad, 0xf016, "Mad Catz Xbox 360 Controller" },
  37.117 +    { 0x1bad, 0xf018, "Mad Catz Street Fighter IV SE Fighting Stick" },
  37.118 +    { 0x1bad, 0xf019, "Mad Catz Brawlstick for Xbox 360" },
  37.119 +    { 0x1bad, 0xf021, "Mad Cats Ghost Recon FS GamePad" },
  37.120 +    { 0x1bad, 0xf023, "MLG Pro Circuit Controller (Xbox)" },
  37.121 +    { 0x1bad, 0xf025, "Mad Catz Call Of Duty" },
  37.122 +    { 0x1bad, 0xf027, "Mad Catz FPS Pro" },
  37.123 +    { 0x1bad, 0xf028, "Street Fighter IV FightPad" },
  37.124 +    { 0x1bad, 0xf02e, "Mad Catz Fightpad" },
  37.125 +    { 0x1bad, 0xf030, "Mad Catz Xbox 360 MC2 MicroCon Racing Wheel" },
  37.126 +    { 0x1bad, 0xf036, "Mad Catz MicroCon GamePad Pro" },
  37.127 +    { 0x1bad, 0xf038, "Street Fighter IV FightStick TE" },
  37.128 +    { 0x1bad, 0xf039, "Mad Catz MvC2 TE" },
  37.129 +    { 0x1bad, 0xf03a, "Mad Catz SFxT Fightstick Pro" },
  37.130 +    { 0x1bad, 0xf03d, "Street Fighter IV Arcade Stick TE - Chun Li" },
  37.131 +    { 0x1bad, 0xf03e, "Mad Catz MLG FightStick TE" },
  37.132 +    { 0x1bad, 0xf03f, "Mad Catz FightStick SoulCaliber" },
  37.133 +    { 0x1bad, 0xf042, "Mad Catz FightStick TES+" },
  37.134 +    { 0x1bad, 0xf080, "Mad Catz FightStick TE2" },
  37.135 +    { 0x1bad, 0xf501, "HoriPad EX2 Turbo" },
  37.136 +    { 0x1bad, 0xf502, "Hori Real Arcade Pro.VX SA" },
  37.137 +    { 0x1bad, 0xf503, "Hori Fighting Stick VX" },
  37.138 +    { 0x1bad, 0xf504, "Hori Real Arcade Pro. EX" },
  37.139 +    { 0x1bad, 0xf505, "Hori Fighting Stick EX2B" },
  37.140 +    { 0x1bad, 0xf506, "Hori Real Arcade Pro.EX Premium VLX" },
  37.141 +    { 0x1bad, 0xf900, "Harmonix Xbox 360 Controller" },
  37.142 +    { 0x1bad, 0xf901, "Gamestop Xbox 360 Controller" },
  37.143 +    { 0x1bad, 0xf903, "Tron Xbox 360 controller" },
  37.144 +    { 0x1bad, 0xf904, "PDP Versus Fighting Pad" },
  37.145 +    { 0x1bad, 0xf906, "MortalKombat FightStick" },
  37.146 +    { 0x1bad, 0xfa01, "MadCatz GamePad" },
  37.147 +    { 0x1bad, 0xfd00, "Razer Onza TE" },
  37.148 +    { 0x1bad, 0xfd01, "Razer Onza" },
  37.149 +    { 0x24c6, 0x5000, "Razer Atrox Arcade Stick" },
  37.150 +    { 0x24c6, 0x5300, "PowerA MINI PROEX Controller" },
  37.151 +    { 0x24c6, 0x5303, "Xbox Airflo wired controller" },
  37.152 +    { 0x24c6, 0x530a, "Xbox 360 Pro EX Controller" },
  37.153 +    { 0x24c6, 0x531a, "PowerA Pro Ex" },
  37.154 +    { 0x24c6, 0x5397, "FUS1ON Tournament Controller" },
  37.155 +    { 0x24c6, 0x5500, "Hori XBOX 360 EX 2 with Turbo" },
  37.156 +    { 0x24c6, 0x5501, "Hori Real Arcade Pro VX-SA" },
  37.157 +    { 0x24c6, 0x5502, "Hori Fighting Stick VX Alt" },
  37.158 +    { 0x24c6, 0x5503, "Hori Fighting Edge" },
  37.159 +    { 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick" },
  37.160 +    { 0x24c6, 0x550d, "Hori GEM Xbox controller" },
  37.161 +    { 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360" },
  37.162 +    { 0x24c6, 0x5b00, "ThrustMaster Ferrari 458 Racing Wheel" },
  37.163 +    { 0x24c6, 0x5b02, "Thrustmaster, Inc. GPX Controller" },
  37.164 +    { 0x24c6, 0x5b03, "Thrustmaster Ferrari 458 Racing Wheel" },
  37.165 +    { 0x24c6, 0x5d04, "Razer Sabertooth" },
  37.166 +    { 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360" },
  37.167 +};
  37.168 +
  37.169 +typedef struct {
  37.170 +    Uint8 last_state[USB_PACKET_LENGTH];
  37.171 +    Uint32 rumble_expiration;
  37.172 +} SDL_DriverXbox360_Context;
  37.173 +
  37.174 +
  37.175 +static SDL_bool
  37.176 +HIDAPI_DriverXbox360_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, int interface_number, Uint16 usage_page, Uint16 usage)
  37.177 +{
  37.178 +#ifdef __MACOSX__
  37.179 +    return SDL_IsJoystickXbox360(vendor_id, product_id) || SDL_IsJoystickXboxOne(vendor_id, product_id);
  37.180 +#else
</