diff --git a/Android.mk b/Android.mk
index 976cbab98..76125d3d5 100755
--- a/Android.mk
+++ b/Android.mk
@@ -31,7 +31,7 @@ LOCAL_SRC_FILES := \
$(wildcard $(LOCAL_PATH)/src/haptic/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/joystick/*.c) \
$(wildcard $(LOCAL_PATH)/src/joystick/android/*.c) \
- $(LOCAL_PATH)/src/joystick/steam/SDL_steamcontroller.c \
+ $(wildcard $(LOCAL_PATH)/src/joystick/hidapi/*.c) \
$(wildcard $(LOCAL_PATH)/src/loadso/dlopen/*.c) \
$(wildcard $(LOCAL_PATH)/src/power/*.c) \
$(wildcard $(LOCAL_PATH)/src/power/android/*.c) \
@@ -48,6 +48,8 @@ LOCAL_SRC_FILES := \
$(wildcard $(LOCAL_PATH)/src/video/yuv2rgb/*.c) \
$(wildcard $(LOCAL_PATH)/src/test/*.c))
+LOCAL_SHARED_LIBRARIES := hidapi
+
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES
LOCAL_LDLIBS := -ldl -lGLESv1_CM -lGLESv2 -llog -landroid
@@ -88,4 +90,19 @@ LOCAL_MODULE_FILENAME := libSDL2main
include $(BUILD_STATIC_LIBRARY)
+###########################
+#
+# hidapi library
+#
+###########################
+include $(CLEAR_VARS)
+
+LOCAL_CPPFLAGS += -std=c++11
+
+LOCAL_SRC_FILES := $(LOCAL_PATH)/src/hidapi/android/hid.cpp
+
+LOCAL_MODULE := libhidapi
+LOCAL_LDLIBS := -llog
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index e2c1909b6..7883e405c 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -80,6 +80,18 @@
C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Lib\x86;$(LibraryPath)
+
+ D:\dev\steam\rel\streaming_client\SDL\src\hidapi\hidapi;$(IncludePath)
+
+
+ D:\dev\steam\rel\streaming_client\SDL\src\hidapi\hidapi;$(IncludePath)
+
+
+ D:\dev\steam\rel\streaming_client\SDL\src\hidapi\hidapi;$(IncludePath)
+
+
+ D:\dev\steam\rel\streaming_client\SDL\src\hidapi\hidapi;$(IncludePath)
+
@@ -109,7 +121,7 @@
_DEBUG;%(PreprocessorDefinitions)
- winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)
+ setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)
true
true
Windows
@@ -140,7 +152,7 @@
_DEBUG;%(PreprocessorDefinitions)
- winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)
+ setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)
true
true
Windows
@@ -174,7 +186,7 @@
NDEBUG;%(PreprocessorDefinitions)
- winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)
+ setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)
true
true
Windows
@@ -206,7 +218,7 @@
NDEBUG;%(PreprocessorDefinitions)
- winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)
+ setupapi.lib;winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies)
true
true
Windows
@@ -318,6 +330,8 @@
+
+
@@ -411,6 +425,12 @@
+
+
+
+
+
+
@@ -523,4 +543,4 @@
-
+
\ No newline at end of file
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index e0ebb0811..56f88692d 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -1,461 +1,469 @@
-
-
-
-
- {395b3af0-33d0-411b-b153-de1676bf1ef8}
-
-
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
- API Headers
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+???
+
+
+
+ {395b3af0-33d0-411b-b153-de1676bf1ef8}
+
+
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+ API Headers
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj
old mode 100644
new mode 100755
index 0032309cb..cef9f4f24
--- a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj
+++ b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj
@@ -110,8 +110,8 @@
56F9D5601DF73BA400C15B5D /* SDL_dataqueue.c in Sources */ = {isa = PBXBuildFile; fileRef = 566726431DF72CF5001DD3DB /* SDL_dataqueue.c */; };
93CB792313FC5E5200BD3E05 /* SDL_uikitviewcontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = 93CB792213FC5E5200BD3E05 /* SDL_uikitviewcontroller.h */; };
93CB792613FC5F5300BD3E05 /* SDL_uikitviewcontroller.m in Sources */ = {isa = PBXBuildFile; fileRef = 93CB792513FC5F5300BD3E05 /* SDL_uikitviewcontroller.m */; };
- A7A9EEA91F702631002A5589 /* SDL_steamcontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = A7A9EEA71F702631002A5589 /* SDL_steamcontroller.c */; };
- A7A9EEAA1F702631002A5589 /* SDL_steamcontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = A7A9EEA81F702631002A5589 /* SDL_steamcontroller.h */; };
+ A704172E20F7E74800A82227 /* controller_type.h in Headers */ = {isa = PBXBuildFile; fileRef = A704172D20F7E74800A82227 /* controller_type.h */; };
+ A704172F20F7E76000A82227 /* SDL_gamecontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */; };
A7F629241FE06523002F9CC9 /* SDL_uikitmetalview.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D7516F81EE1C28A00820EEA /* SDL_uikitmetalview.m */; };
AA0AD06216647BBB00CE5896 /* SDL_gamecontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */; };
AA0AD06516647BD400CE5896 /* SDL_gamecontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = AA0AD06416647BD400CE5896 /* SDL_gamecontroller.h */; };
@@ -194,7 +194,18 @@
AADC5A631FDA10C800960936 /* SDL_shaders_metal_ios.h in Headers */ = {isa = PBXBuildFile; fileRef = AADC5A611FDA10C800960936 /* SDL_shaders_metal_ios.h */; };
AADC5A641FDA10C800960936 /* SDL_render_metal.m in Sources */ = {isa = PBXBuildFile; fileRef = AADC5A621FDA10C800960936 /* SDL_render_metal.m */; };
AADC5A651FDA10CB00960936 /* SDL_render_metal.m in Sources */ = {isa = PBXBuildFile; fileRef = AADC5A621FDA10C800960936 /* SDL_render_metal.m */; };
- AAE7A4222041CCA90096E65A /* SDL_steamcontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = A7A9EEA71F702631002A5589 /* SDL_steamcontroller.c */; };
+ F3BDD77620F51C3C004ECBF3 /* hid.mm in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD77520F51C3C004ECBF3 /* hid.mm */; };
+ F3BDD79220F51CB8004ECBF3 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78B20F51CB8004ECBF3 /* SDL_hidapi_xbox360.c */; };
+ F3BDD79320F51CB8004ECBF3 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78B20F51CB8004ECBF3 /* SDL_hidapi_xbox360.c */; };
+ F3BDD79420F51CB8004ECBF3 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78C20F51CB8004ECBF3 /* SDL_hidapi_switch.c */; };
+ F3BDD79520F51CB8004ECBF3 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78C20F51CB8004ECBF3 /* SDL_hidapi_switch.c */; };
+ F3BDD79620F51CB8004ECBF3 /* SDL_hidapi_xboxone.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78D20F51CB8004ECBF3 /* SDL_hidapi_xboxone.c */; };
+ F3BDD79720F51CB8004ECBF3 /* SDL_hidapi_xboxone.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78D20F51CB8004ECBF3 /* SDL_hidapi_xboxone.c */; };
+ F3BDD79820F51CB8004ECBF3 /* SDL_hidapi_ps4.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78E20F51CB8004ECBF3 /* SDL_hidapi_ps4.c */; };
+ F3BDD79920F51CB8004ECBF3 /* SDL_hidapi_ps4.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD78E20F51CB8004ECBF3 /* SDL_hidapi_ps4.c */; };
+ F3BDD79B20F51CB8004ECBF3 /* SDL_hidapijoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3BDD79020F51CB8004ECBF3 /* SDL_hidapijoystick_c.h */; };
+ F3BDD79C20F51CB8004ECBF3 /* SDL_hidapijoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD79120F51CB8004ECBF3 /* SDL_hidapijoystick.c */; };
+ F3BDD79D20F51CB8004ECBF3 /* SDL_hidapijoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = F3BDD79120F51CB8004ECBF3 /* SDL_hidapijoystick.c */; };
FA1DC2721C62BE65008F99A0 /* SDL_uikitclipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = FA1DC2701C62BE65008F99A0 /* SDL_uikitclipboard.h */; };
FA1DC2731C62BE65008F99A0 /* SDL_uikitclipboard.m in Sources */ = {isa = PBXBuildFile; fileRef = FA1DC2711C62BE65008F99A0 /* SDL_uikitclipboard.m */; };
FAB5981D1BB5C31500BE72C5 /* SDL_atomic.c in Sources */ = {isa = PBXBuildFile; fileRef = 04FFAB8912E23B8D00BA343D /* SDL_atomic.c */; };
@@ -223,7 +234,6 @@
FAB5984C1BB5C31600BE72C5 /* SDL_syshaptic.c in Sources */ = {isa = PBXBuildFile; fileRef = 047677B80EA76A31008ABAF1 /* SDL_syshaptic.c */; };
FAB5984D1BB5C31600BE72C5 /* SDL_haptic.c in Sources */ = {isa = PBXBuildFile; fileRef = 047677B90EA76A31008ABAF1 /* SDL_haptic.c */; };
FAB598501BB5C31600BE72C5 /* SDL_sysjoystick.m in Sources */ = {isa = PBXBuildFile; fileRef = FD689F000E26E5B600F90B21 /* SDL_sysjoystick.m */; };
- FAB598511BB5C31600BE72C5 /* SDL_gamecontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */; };
FAB598521BB5C31600BE72C5 /* SDL_joystick.c in Sources */ = {isa = PBXBuildFile; fileRef = FD5F9D1E0E0E08B3008E885B /* SDL_joystick.c */; };
FAB598551BB5C31600BE72C5 /* SDL_sysloadso.c in Sources */ = {isa = PBXBuildFile; fileRef = 047AF1B20EA98D6C00811173 /* SDL_sysloadso.c */; };
FAB598561BB5C31600BE72C5 /* SDL_sysloadso.c in Sources */ = {isa = PBXBuildFile; fileRef = FD8BD8190E27E25900B52CD5 /* SDL_sysloadso.c */; };
@@ -432,8 +442,7 @@
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; };
93CB792213FC5E5200BD3E05 /* SDL_uikitviewcontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_uikitviewcontroller.h; sourceTree = ""; };
93CB792513FC5F5300BD3E05 /* SDL_uikitviewcontroller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_uikitviewcontroller.m; sourceTree = ""; };
- A7A9EEA71F702631002A5589 /* SDL_steamcontroller.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_steamcontroller.c; sourceTree = ""; };
- A7A9EEA81F702631002A5589 /* SDL_steamcontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_steamcontroller.h; sourceTree = ""; };
+ A704172D20F7E74800A82227 /* controller_type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = controller_type.h; sourceTree = ""; };
AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_gamecontroller.c; sourceTree = ""; };
AA0AD06416647BD400CE5896 /* SDL_gamecontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gamecontroller.h; sourceTree = ""; };
AA0F8494178D5F1A00823F9D /* SDL_systls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_systls.c; sourceTree = ""; };
@@ -510,6 +519,13 @@
AADA5B8E16CCAB7C00107CF7 /* SDL_bits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_bits.h; sourceTree = ""; };
AADC5A611FDA10C800960936 /* SDL_shaders_metal_ios.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_shaders_metal_ios.h; sourceTree = ""; };
AADC5A621FDA10C800960936 /* SDL_render_metal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_render_metal.m; sourceTree = ""; };
+ F3BDD77520F51C3C004ECBF3 /* hid.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = hid.mm; sourceTree = ""; };
+ F3BDD78B20F51CB8004ECBF3 /* SDL_hidapi_xbox360.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_xbox360.c; sourceTree = ""; };
+ F3BDD78C20F51CB8004ECBF3 /* SDL_hidapi_switch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_switch.c; sourceTree = ""; };
+ F3BDD78D20F51CB8004ECBF3 /* SDL_hidapi_xboxone.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_xboxone.c; sourceTree = ""; };
+ F3BDD78E20F51CB8004ECBF3 /* SDL_hidapi_ps4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_ps4.c; sourceTree = ""; };
+ F3BDD79020F51CB8004ECBF3 /* SDL_hidapijoystick_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_hidapijoystick_c.h; sourceTree = ""; };
+ F3BDD79120F51CB8004ECBF3 /* SDL_hidapijoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapijoystick.c; sourceTree = ""; };
FA1DC2701C62BE65008F99A0 /* SDL_uikitclipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_uikitclipboard.h; sourceTree = ""; };
FA1DC2711C62BE65008F99A0 /* SDL_uikitclipboard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_uikitclipboard.m; sourceTree = ""; };
FAB598141BB5C1B100BE72C5 /* libSDL2.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSDL2.a; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -778,15 +794,6 @@
name = uikit;
sourceTree = "";
};
- A7A9EEA61F702607002A5589 /* steam */ = {
- isa = PBXGroup;
- children = (
- A7A9EEA71F702631002A5589 /* SDL_steamcontroller.c */,
- A7A9EEA81F702631002A5589 /* SDL_steamcontroller.h */,
- );
- path = steam;
- sourceTree = "";
- };
AA13B3521FB8B41700D9FEE6 /* yuv2rgb */ = {
isa = PBXGroup;
children = (
@@ -807,6 +814,36 @@
path = metal;
sourceTree = "";
};
+ F35CEA6E20F51B7F003ECE98 /* hidapi */ = {
+ isa = PBXGroup;
+ children = (
+ F3BDD77420F51C18004ECBF3 /* ios */,
+ );
+ name = hidapi;
+ path = ../../src/hidapi;
+ sourceTree = SOURCE_ROOT;
+ };
+ F3BDD77420F51C18004ECBF3 /* ios */ = {
+ isa = PBXGroup;
+ children = (
+ F3BDD77520F51C3C004ECBF3 /* hid.mm */,
+ );
+ path = ios;
+ sourceTree = "";
+ };
+ F3BDD78A20F51C8D004ECBF3 /* hidapi */ = {
+ isa = PBXGroup;
+ children = (
+ F3BDD78E20F51CB8004ECBF3 /* SDL_hidapi_ps4.c */,
+ F3BDD78C20F51CB8004ECBF3 /* SDL_hidapi_switch.c */,
+ F3BDD78B20F51CB8004ECBF3 /* SDL_hidapi_xbox360.c */,
+ F3BDD78D20F51CB8004ECBF3 /* SDL_hidapi_xboxone.c */,
+ F3BDD79020F51CB8004ECBF3 /* SDL_hidapijoystick_c.h */,
+ F3BDD79120F51CB8004ECBF3 /* SDL_hidapijoystick.c */,
+ );
+ path = hidapi;
+ sourceTree = "";
+ };
FD3F4A6F0DEA620800C5B771 /* stdlib */ = {
isa = PBXGroup;
children = (
@@ -824,8 +861,9 @@
FD5F9D080E0E08B3008E885B /* joystick */ = {
isa = PBXGroup;
children = (
- A7A9EEA61F702607002A5589 /* steam */,
+ F3BDD78A20F51C8D004ECBF3 /* hidapi */,
FD689EFF0E26E5B600F90B21 /* iphoneos */,
+ A704172D20F7E74800A82227 /* controller_type.h */,
AA0AD06116647BBB00CE5896 /* SDL_gamecontroller.c */,
FD5F9D1E0E0E08B3008E885B /* SDL_joystick.c */,
FD5F9D1F0E0E08B3008E885B /* SDL_joystick_c.h */,
@@ -970,6 +1008,7 @@
FD99B99D0DD52EDC00FB1D6B /* file */,
56C181E017C44D6900406AE3 /* filesystem */,
047677B60EA769DF008ABAF1 /* haptic */,
+ F35CEA6E20F51B7F003ECE98 /* hidapi */,
FD5F9D080E0E08B3008E885B /* joystick */,
FD8BD8150E27E25900B52CD5 /* loadso */,
56ED04DE118A8E9A00A56AA6 /* power */,
@@ -1213,13 +1252,13 @@
AA13B3591FB8B46400D9FEE6 /* yuv_rgb.h in Headers */,
04F7807712FB751400FC43C0 /* SDL_blendfillrect.h in Headers */,
04F7807912FB751400FC43C0 /* SDL_blendline.h in Headers */,
+ F3BDD79B20F51CB8004ECBF3 /* SDL_hidapijoystick_c.h in Headers */,
04F7807B12FB751400FC43C0 /* SDL_blendpoint.h in Headers */,
04F7807C12FB751400FC43C0 /* SDL_draw.h in Headers */,
04F7807E12FB751400FC43C0 /* SDL_drawline.h in Headers */,
AA13B34E1FB8B27800D9FEE6 /* SDL_yuv_c.h in Headers */,
04F7808012FB751400FC43C0 /* SDL_drawpoint.h in Headers */,
04F7808412FB753F00FC43C0 /* SDL_nullframebuffer_c.h in Headers */,
- A7A9EEAA1F702631002A5589 /* SDL_steamcontroller.h in Headers */,
0442EC5012FE1C1E004C9285 /* SDL_render_sw_c.h in Headers */,
FA1DC2721C62BE65008F99A0 /* SDL_uikitclipboard.h in Headers */,
0402A85A12FE70C600CECEE3 /* SDL_shaders_gles2.h in Headers */,
@@ -1250,6 +1289,7 @@
AA7558AA1595D55500BBD41B /* SDL_joystick.h in Headers */,
AA13B34B1FB8B27800D9FEE6 /* SDL_shape_internals.h in Headers */,
AA7558AB1595D55500BBD41B /* SDL_keyboard.h in Headers */,
+ A704172E20F7E74800A82227 /* controller_type.h in Headers */,
AA7558AC1595D55500BBD41B /* SDL_keycode.h in Headers */,
AA7558AD1595D55500BBD41B /* SDL_loadso.h in Headers */,
AA7558AE1595D55500BBD41B /* SDL_log.h in Headers */,
@@ -1432,6 +1472,7 @@
FAB598251BB5C31500BE72C5 /* SDL_audiocvt.c in Sources */,
FAB598271BB5C31500BE72C5 /* SDL_audiotypecvt.c in Sources */,
FAB598281BB5C31500BE72C5 /* SDL_mixer.c in Sources */,
+ F3BDD79720F51CB8004ECBF3 /* SDL_hidapi_xboxone.c in Sources */,
FAB5982A1BB5C31500BE72C5 /* SDL_wave.c in Sources */,
FAFDF8C61D88D4530083E6F2 /* SDL_uikitclipboard.m in Sources */,
FAB5982C1BB5C31500BE72C5 /* SDL_cpuinfo.c in Sources */,
@@ -1442,7 +1483,9 @@
A7F629241FE06523002F9CC9 /* SDL_uikitmetalview.m in Sources */,
FAB5983C1BB5C31500BE72C5 /* SDL_gesture.c in Sources */,
FAB5983E1BB5C31500BE72C5 /* SDL_keyboard.c in Sources */,
+ F3BDD79520F51CB8004ECBF3 /* SDL_hidapi_switch.c in Sources */,
FAB598401BB5C31500BE72C5 /* SDL_mouse.c in Sources */,
+ A704172F20F7E76000A82227 /* SDL_gamecontroller.c in Sources */,
FAB598421BB5C31500BE72C5 /* SDL_quit.c in Sources */,
FAB598441BB5C31500BE72C5 /* SDL_touch.c in Sources */,
FAB598461BB5C31500BE72C5 /* SDL_windowevents.c in Sources */,
@@ -1454,8 +1497,8 @@
AADC5A5F1FDA105600960936 /* SDL_vulkan_utils.c in Sources */,
AADC5A5E1FDA105300960936 /* SDL_yuv.c in Sources */,
FAB5984D1BB5C31600BE72C5 /* SDL_haptic.c in Sources */,
+ F3BDD79320F51CB8004ECBF3 /* SDL_hidapi_xbox360.c in Sources */,
FAB598501BB5C31600BE72C5 /* SDL_sysjoystick.m in Sources */,
- FAB598511BB5C31600BE72C5 /* SDL_gamecontroller.c in Sources */,
FAB598521BB5C31600BE72C5 /* SDL_joystick.c in Sources */,
FAB598551BB5C31600BE72C5 /* SDL_sysloadso.c in Sources */,
AADC5A651FDA10CB00960936 /* SDL_render_metal.m in Sources */,
@@ -1482,6 +1525,7 @@
FAB598761BB5C31600BE72C5 /* SDL_stdlib.c in Sources */,
FAB598771BB5C31600BE72C5 /* SDL_string.c in Sources */,
FAB598781BB5C31600BE72C5 /* SDL_syscond.c in Sources */,
+ F3BDD79D20F51CB8004ECBF3 /* SDL_hidapijoystick.c in Sources */,
AADC5A601FDA10A400960936 /* SDL_uikitvulkan.m in Sources */,
FAB598791BB5C31600BE72C5 /* SDL_sysmutex.c in Sources */,
FAB5987B1BB5C31600BE72C5 /* SDL_syssem.c in Sources */,
@@ -1492,6 +1536,7 @@
FAB598821BB5C31600BE72C5 /* SDL_systimer.c in Sources */,
FAB598831BB5C31600BE72C5 /* SDL_timer.c in Sources */,
FAB598871BB5C31600BE72C5 /* SDL_uikitappdelegate.m in Sources */,
+ F3BDD79920F51CB8004ECBF3 /* SDL_hidapi_ps4.c in Sources */,
FAB598891BB5C31600BE72C5 /* SDL_uikitevents.m in Sources */,
FAB5988B1BB5C31600BE72C5 /* SDL_uikitmessagebox.m in Sources */,
FAB5988D1BB5C31600BE72C5 /* SDL_uikitmodes.m in Sources */,
@@ -1535,7 +1580,6 @@
files = (
FD6526810DE8FCDD002AD96B /* SDL_systimer.c in Sources */,
FD6526800DE8FCDD002AD96B /* SDL_timer.c in Sources */,
- A7A9EEA91F702631002A5589 /* SDL_steamcontroller.c in Sources */,
FD3F4A7B0DEA620800C5B771 /* SDL_string.c in Sources */,
FD6526660DE8FCDD002AD96B /* SDL_dummyaudio.c in Sources */,
FD6526670DE8FCDD002AD96B /* SDL_audio.c in Sources */,
@@ -1566,7 +1610,9 @@
FD3F4A760DEA620800C5B771 /* SDL_getenv.c in Sources */,
FD3F4A770DEA620800C5B771 /* SDL_iconv.c in Sources */,
FD3F4A780DEA620800C5B771 /* SDL_malloc.c in Sources */,
+ F3BDD79220F51CB8004ECBF3 /* SDL_hidapi_xbox360.c in Sources */,
FD3F4A790DEA620800C5B771 /* SDL_qsort.c in Sources */,
+ F3BDD79820F51CB8004ECBF3 /* SDL_hidapi_ps4.c in Sources */,
FD3F4A7A0DEA620800C5B771 /* SDL_stdlib.c in Sources */,
FDA6844D0DF2374E00F98A1A /* SDL_blit.c in Sources */,
FDA6844F0DF2374E00F98A1A /* SDL_blit_0.c in Sources */,
@@ -1599,11 +1645,13 @@
FD689F270E26E5D900F90B21 /* SDL_uikitopenglview.m in Sources */,
FD689FCE0E26E9D400F90B21 /* SDL_uikitappdelegate.m in Sources */,
FD8BD8250E27E25900B52CD5 /* SDL_sysloadso.c in Sources */,
+ F3BDD79C20F51CB8004ECBF3 /* SDL_hidapijoystick.c in Sources */,
047677BB0EA76A31008ABAF1 /* SDL_syshaptic.c in Sources */,
047677BC0EA76A31008ABAF1 /* SDL_haptic.c in Sources */,
047AF1B30EA98D6C00811173 /* SDL_sysloadso.c in Sources */,
046387460F0B5B7D0041FD65 /* SDL_fillrect.c in Sources */,
04F2AF561104ABD200D6DDF7 /* SDL_assert.c in Sources */,
+ F3BDD79620F51CB8004ECBF3 /* SDL_hidapi_xboxone.c in Sources */,
56ED04E1118A8EE200A56AA6 /* SDL_power.c in Sources */,
56ED04E3118A8EFD00A56AA6 /* SDL_syspower.m in Sources */,
006E9889119552DD001DE610 /* SDL_rwopsbundlesupport.m in Sources */,
@@ -1629,10 +1677,12 @@
0402A85912FE70C600CECEE3 /* SDL_shaders_gles2.c in Sources */,
04BAC09D1300C1290055DE28 /* SDL_log.c in Sources */,
56EA86FB13E9EC2B002E47EB /* SDL_coreaudio.m in Sources */,
+ F3BDD79420F51CB8004ECBF3 /* SDL_hidapi_switch.c in Sources */,
93CB792613FC5F5300BD3E05 /* SDL_uikitviewcontroller.m in Sources */,
AA628ADB159369E3005138DD /* SDL_rotate.c in Sources */,
AA126AD51617C5E7005ABC8F /* SDL_uikitmodes.m in Sources */,
AA704DD7162AA90A0076D1C1 /* SDL_dropevents.c in Sources */,
+ F3BDD77620F51C3C004ECBF3 /* hid.mm in Sources */,
AABCC3951640643D00AB8930 /* SDL_uikitmessagebox.m in Sources */,
AA0AD06216647BBB00CE5896 /* SDL_gamecontroller.c in Sources */,
AA0F8495178D5F1A00823F9D /* SDL_systls.c in Sources */,
diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
old mode 100644
new mode 100755
index a39186b19..252eceaa0
--- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj
+++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
@@ -462,6 +462,30 @@
5C2EF6FE1FC9EE65003F5197 /* SDL_egl.c in Sources */ = {isa = PBXBuildFile; fileRef = 5C2EF6F51FC9EE35003F5197 /* SDL_egl.c */; };
5C2EF6FF1FC9EE65003F5197 /* SDL_rect_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C2EF6F41FC9EE34003F5197 /* SDL_rect_c.h */; };
5C2EF7011FC9EF10003F5197 /* SDL_egl.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C2EF7001FC9EF0F003F5197 /* SDL_egl.h */; };
+ A704170920F09A9800A82227 /* hid.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170820F09A9800A82227 /* hid.c */; };
+ A704170A20F09A9800A82227 /* hid.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170820F09A9800A82227 /* hid.c */; };
+ A704170B20F09A9800A82227 /* hid.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170820F09A9800A82227 /* hid.c */; };
+ A704171420F09AC900A82227 /* SDL_hidapijoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170D20F09AC800A82227 /* SDL_hidapijoystick.c */; };
+ A704171520F09AC900A82227 /* SDL_hidapijoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170D20F09AC800A82227 /* SDL_hidapijoystick.c */; };
+ A704171620F09AC900A82227 /* SDL_hidapijoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170D20F09AC800A82227 /* SDL_hidapijoystick.c */; };
+ A704171720F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A704170E20F09AC800A82227 /* SDL_hidapijoystick_c.h */; };
+ A704171820F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A704170E20F09AC800A82227 /* SDL_hidapijoystick_c.h */; };
+ A704171920F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A704170E20F09AC800A82227 /* SDL_hidapijoystick_c.h */; };
+ A704171A20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170F20F09AC800A82227 /* SDL_hidapi_switch.c */; };
+ A704171B20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170F20F09AC800A82227 /* SDL_hidapi_switch.c */; };
+ A704171C20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */ = {isa = PBXBuildFile; fileRef = A704170F20F09AC800A82227 /* SDL_hidapi_switch.c */; };
+ A704171D20F09AC900A82227 /* controller_type.h in Headers */ = {isa = PBXBuildFile; fileRef = A704171020F09AC900A82227 /* controller_type.h */; };
+ A704171E20F09AC900A82227 /* controller_type.h in Headers */ = {isa = PBXBuildFile; fileRef = A704171020F09AC900A82227 /* controller_type.h */; };
+ A704171F20F09AC900A82227 /* controller_type.h in Headers */ = {isa = PBXBuildFile; fileRef = A704171020F09AC900A82227 /* controller_type.h */; };
+ A704172020F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171120F09AC900A82227 /* SDL_hidapi_ps4.c */; };
+ A704172120F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171120F09AC900A82227 /* SDL_hidapi_ps4.c */; };
+ A704172220F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171120F09AC900A82227 /* SDL_hidapi_ps4.c */; };
+ A704172320F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171220F09AC900A82227 /* SDL_hidapi_xboxone.c */; };
+ A704172420F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171220F09AC900A82227 /* SDL_hidapi_xboxone.c */; };
+ A704172520F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171220F09AC900A82227 /* SDL_hidapi_xboxone.c */; };
+ A704172620F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171320F09AC900A82227 /* SDL_hidapi_xbox360.c */; };
+ A704172720F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171320F09AC900A82227 /* SDL_hidapi_xbox360.c */; };
+ A704172820F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */ = {isa = PBXBuildFile; fileRef = A704171320F09AC900A82227 /* SDL_hidapi_xbox360.c */; };
A7381E961D8B69D600B177DD /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7381E951D8B69D600B177DD /* CoreAudio.framework */; };
A7381E971D8B6A0300B177DD /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7381E931D8B69C300B177DD /* AudioToolbox.framework */; };
A77E6EB4167AB0A90010E40B /* SDL_gamecontroller.h in Headers */ = {isa = PBXBuildFile; fileRef = A77E6EB3167AB0A90010E40B /* SDL_gamecontroller.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -1114,6 +1138,14 @@
5C2EF6F51FC9EE35003F5197 /* SDL_egl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_egl.c; sourceTree = ""; };
5C2EF6F61FC9EE35003F5197 /* SDL_egl_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_egl_c.h; sourceTree = ""; };
5C2EF7001FC9EF0F003F5197 /* SDL_egl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_egl.h; sourceTree = ""; };
+ A704170820F09A9800A82227 /* hid.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hid.c; sourceTree = ""; };
+ A704170D20F09AC800A82227 /* SDL_hidapijoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapijoystick.c; sourceTree = ""; };
+ A704170E20F09AC800A82227 /* SDL_hidapijoystick_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_hidapijoystick_c.h; sourceTree = ""; };
+ A704170F20F09AC800A82227 /* SDL_hidapi_switch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_switch.c; sourceTree = ""; };
+ A704171020F09AC900A82227 /* controller_type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = controller_type.h; sourceTree = ""; };
+ A704171120F09AC900A82227 /* SDL_hidapi_ps4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_ps4.c; sourceTree = ""; };
+ A704171220F09AC900A82227 /* SDL_hidapi_xboxone.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_xboxone.c; sourceTree = ""; };
+ A704171320F09AC900A82227 /* SDL_hidapi_xbox360.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_xbox360.c; sourceTree = ""; };
A7381E931D8B69C300B177DD /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
A7381E951D8B69D600B177DD /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
A77E6EB3167AB0A90010E40B /* SDL_gamecontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_gamecontroller.h; sourceTree = ""; };
@@ -1536,6 +1568,7 @@
04BDFDFF12E6671700899322 /* joystick */ = {
isa = PBXGroup;
children = (
+ A704170C20F09AA600A82227 /* hidapi */,
04BDFE0612E6671700899322 /* darwin */,
04BDFE1612E6671700899322 /* SDL_joystick.c */,
04BDFE1712E6671700899322 /* SDL_joystick_c.h */,
@@ -1818,6 +1851,7 @@
567E2F1F17C44BBB005F1892 /* filesystem */,
04BDFDEC12E6671700899322 /* file */,
04BDFDF112E6671700899322 /* haptic */,
+ A73EBCD520F099C10043B449 /* hidapi */,
04BDFDFF12E6671700899322 /* joystick */,
04BDFE2F12E6671700899322 /* loadso */,
04BDFE4512E6671700899322 /* power */,
@@ -1879,6 +1913,37 @@
path = opengles2;
sourceTree = "";
};
+ A704170720F09A6700A82227 /* mac */ = {
+ isa = PBXGroup;
+ children = (
+ A704170820F09A9800A82227 /* hid.c */,
+ );
+ path = mac;
+ sourceTree = "";
+ };
+ A704170C20F09AA600A82227 /* hidapi */ = {
+ isa = PBXGroup;
+ children = (
+ A704171020F09AC900A82227 /* controller_type.h */,
+ A704171120F09AC900A82227 /* SDL_hidapi_ps4.c */,
+ A704170F20F09AC800A82227 /* SDL_hidapi_switch.c */,
+ A704171320F09AC900A82227 /* SDL_hidapi_xbox360.c */,
+ A704171220F09AC900A82227 /* SDL_hidapi_xboxone.c */,
+ A704170E20F09AC800A82227 /* SDL_hidapijoystick_c.h */,
+ A704170D20F09AC800A82227 /* SDL_hidapijoystick.c */,
+ );
+ path = hidapi;
+ sourceTree = "";
+ };
+ A73EBCD520F099C10043B449 /* hidapi */ = {
+ isa = PBXGroup;
+ children = (
+ A704170720F09A6700A82227 /* mac */,
+ );
+ name = hidapi;
+ path = ../../src/hidapi;
+ sourceTree = SOURCE_ROOT;
+ };
AA9A7F0E1FB0200B00FED37F /* yuv2rgb */ = {
isa = PBXGroup;
children = (
@@ -1993,6 +2058,7 @@
AA7558421595D4D800BBD41B /* SDL_revision.h in Headers */,
AA7558441595D4D800BBD41B /* SDL_rwops.h in Headers */,
AA7558461595D4D800BBD41B /* SDL_scancode.h in Headers */,
+ A704171720F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */,
AA7558481595D4D800BBD41B /* SDL_shape.h in Headers */,
AA75584A1595D4D800BBD41B /* SDL_stdinc.h in Headers */,
AA75584C1595D4D800BBD41B /* SDL_surface.h in Headers */,
@@ -2083,6 +2149,7 @@
04BD01F912E6671800899322 /* SDL_x11window.h in Headers */,
041B2CA612FA0D680087D585 /* SDL_sysrender.h in Headers */,
AA9A7F161FB0209D00FED37F /* SDL_yuv_c.h in Headers */,
+ A704171D20F09AC900A82227 /* controller_type.h in Headers */,
04409B9312FA97ED00FB9AA8 /* SDL_yuv_sw_c.h in Headers */,
04F7803912FB748500FC43C0 /* SDL_nullframebuffer_c.h in Headers */,
04F7804A12FB74A200FC43C0 /* SDL_blendfillrect.h in Headers */,
@@ -2132,6 +2199,7 @@
AA75581F1595D4D800BBD41B /* SDL_joystick.h in Headers */,
AA7558211595D4D800BBD41B /* SDL_keyboard.h in Headers */,
AA7558231595D4D800BBD41B /* SDL_keycode.h in Headers */,
+ A704171E20F09AC900A82227 /* controller_type.h in Headers */,
AA7558251595D4D800BBD41B /* SDL_loadso.h in Headers */,
AA7558271595D4D800BBD41B /* SDL_log.h in Headers */,
AA7558291595D4D800BBD41B /* SDL_main.h in Headers */,
@@ -2207,6 +2275,7 @@
04BD02DC12E6671800899322 /* SDL_systhread_c.h in Headers */,
04BD02E312E6671800899322 /* SDL_systhread.h in Headers */,
04BD02E512E6671800899322 /* SDL_thread_c.h in Headers */,
+ A704171820F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */,
04BD02F212E6671800899322 /* SDL_timer_c.h in Headers */,
04BD030D12E6671800899322 /* SDL_cocoaclipboard.h in Headers */,
04BD030F12E6671800899322 /* SDL_cocoaevents.h in Headers */,
@@ -2296,6 +2365,7 @@
DB313FD917554B71006C0E22 /* SDL_joystick.h in Headers */,
DB313FDA17554B71006C0E22 /* SDL_keyboard.h in Headers */,
DB313FDB17554B71006C0E22 /* SDL_keycode.h in Headers */,
+ A704171F20F09AC900A82227 /* controller_type.h in Headers */,
DB313FDC17554B71006C0E22 /* SDL_loadso.h in Headers */,
DB313FDD17554B71006C0E22 /* SDL_log.h in Headers */,
DB313FDE17554B71006C0E22 /* SDL_main.h in Headers */,
@@ -2371,6 +2441,7 @@
DB313F9317554B71006C0E22 /* SDL_systhread_c.h in Headers */,
DB313F9417554B71006C0E22 /* SDL_systhread.h in Headers */,
DB313F9517554B71006C0E22 /* SDL_thread_c.h in Headers */,
+ A704171920F09AC900A82227 /* SDL_hidapijoystick_c.h in Headers */,
DB313F9617554B71006C0E22 /* SDL_timer_c.h in Headers */,
DB313F9717554B71006C0E22 /* SDL_cocoaclipboard.h in Headers */,
DB313F9817554B71006C0E22 /* SDL_cocoaevents.h in Headers */,
@@ -2602,6 +2673,7 @@
04BD004112E6671800899322 /* SDL_cpuinfo.c in Sources */,
04BD004812E6671800899322 /* SDL_clipboardevents.c in Sources */,
04BD004A12E6671800899322 /* SDL_events.c in Sources */,
+ A704172620F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */,
04BD004C12E6671800899322 /* SDL_gesture.c in Sources */,
04BD004E12E6671800899322 /* SDL_keyboard.c in Sources */,
04BD005012E6671800899322 /* SDL_mouse.c in Sources */,
@@ -2640,6 +2712,7 @@
04BD00F612E6671800899322 /* SDL_cocoaevents.m in Sources */,
04BD00F812E6671800899322 /* SDL_cocoakeyboard.m in Sources */,
AA9A7F151FB0209D00FED37F /* SDL_yuv.c in Sources */,
+ A704171A20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */,
04BD00FA12E6671800899322 /* SDL_cocoamodes.m in Sources */,
4D16644F1EDD6023003DE88E /* SDL_vulkan_utils.c in Sources */,
04BD00FC12E6671800899322 /* SDL_cocoamouse.m in Sources */,
@@ -2648,6 +2721,7 @@
04BD010212E6671800899322 /* SDL_cocoavideo.m in Sources */,
04BD010412E6671800899322 /* SDL_cocoawindow.m in Sources */,
04BD011712E6671800899322 /* SDL_nullevents.c in Sources */,
+ A704172320F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */,
04BD011B12E6671800899322 /* SDL_nullvideo.c in Sources */,
04BD017512E6671800899322 /* SDL_blit.c in Sources */,
04BD017712E6671800899322 /* SDL_blit_0.c in Sources */,
@@ -2656,6 +2730,8 @@
04BD017912E6671800899322 /* SDL_blit_A.c in Sources */,
04BD017A12E6671800899322 /* SDL_blit_auto.c in Sources */,
04BD017C12E6671800899322 /* SDL_blit_copy.c in Sources */,
+ A704172020F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */,
+ A704170920F09A9800A82227 /* hid.c in Sources */,
04BD017E12E6671800899322 /* SDL_blit_N.c in Sources */,
04BD017F12E6671800899322 /* SDL_blit_slow.c in Sources */,
04BD018112E6671800899322 /* SDL_bmp.c in Sources */,
@@ -2664,6 +2740,7 @@
04BD018C12E6671800899322 /* SDL_pixels.c in Sources */,
04BD018E12E6671800899322 /* SDL_rect.c in Sources */,
04BD019612E6671800899322 /* SDL_RLEaccel.c in Sources */,
+ A704171420F09AC900A82227 /* SDL_hidapijoystick.c in Sources */,
04BD019812E6671800899322 /* SDL_shape.c in Sources */,
04BD019A12E6671800899322 /* SDL_stretch.c in Sources */,
04BD019B12E6671800899322 /* SDL_surface.c in Sources */,
@@ -2731,6 +2808,7 @@
04BD024812E6671800899322 /* SDL_audiotypecvt.c in Sources */,
04BD024912E6671800899322 /* SDL_mixer.c in Sources */,
04BD025112E6671800899322 /* SDL_wave.c in Sources */,
+ A704172720F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */,
04BD025C12E6671800899322 /* SDL_cpuinfo.c in Sources */,
04BD026312E6671800899322 /* SDL_clipboardevents.c in Sources */,
04BD026512E6671800899322 /* SDL_events.c in Sources */,
@@ -2769,6 +2847,7 @@
04BD02E412E6671800899322 /* SDL_thread.c in Sources */,
04BD02F112E6671800899322 /* SDL_timer.c in Sources */,
04BD02F312E6671800899322 /* SDL_systimer.c in Sources */,
+ A704171B20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */,
04BD030E12E6671800899322 /* SDL_cocoaclipboard.m in Sources */,
04BD031012E6671800899322 /* SDL_cocoaevents.m in Sources */,
04BD031212E6671800899322 /* SDL_cocoakeyboard.m in Sources */,
@@ -2777,6 +2856,7 @@
5C2EF6A31FC98B38003F5197 /* SDL_yuv.c in Sources */,
5C2EF6F11FC9D181003F5197 /* SDL_cocoaopengles.m in Sources */,
04BD031812E6671800899322 /* SDL_cocoaopengl.m in Sources */,
+ A704172420F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */,
04BD031A12E6671800899322 /* SDL_cocoashape.m in Sources */,
04BD031C12E6671800899322 /* SDL_cocoavideo.m in Sources */,
04BD031E12E6671800899322 /* SDL_cocoawindow.m in Sources */,
@@ -2785,6 +2865,8 @@
5C2EF6A51FC98B6B003F5197 /* yuv_rgb.c in Sources */,
04BD038F12E6671800899322 /* SDL_blit.c in Sources */,
04BD039112E6671800899322 /* SDL_blit_0.c in Sources */,
+ A704172120F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */,
+ A704170A20F09A9800A82227 /* hid.c in Sources */,
04BD039212E6671800899322 /* SDL_blit_1.c in Sources */,
04BD039312E6671800899322 /* SDL_blit_A.c in Sources */,
04BD039412E6671800899322 /* SDL_blit_auto.c in Sources */,
@@ -2793,6 +2875,7 @@
04BD039912E6671800899322 /* SDL_blit_slow.c in Sources */,
04BD039B12E6671800899322 /* SDL_bmp.c in Sources */,
04BD039C12E6671800899322 /* SDL_clipboard.c in Sources */,
+ A704171520F09AC900A82227 /* SDL_hidapijoystick.c in Sources */,
04BD03A112E6671800899322 /* SDL_fillrect.c in Sources */,
04BD03A612E6671800899322 /* SDL_pixels.c in Sources */,
04BD03A812E6671800899322 /* SDL_rect.c in Sources */,
@@ -2860,6 +2943,7 @@
DB31400617554B71006C0E22 /* SDL_audiotypecvt.c in Sources */,
DB31400717554B71006C0E22 /* SDL_mixer.c in Sources */,
DB31400817554B71006C0E22 /* SDL_wave.c in Sources */,
+ A704172820F09AC900A82227 /* SDL_hidapi_xbox360.c in Sources */,
DB31400917554B71006C0E22 /* SDL_cpuinfo.c in Sources */,
DB31400A17554B71006C0E22 /* SDL_clipboardevents.c in Sources */,
DB31400B17554B71006C0E22 /* SDL_events.c in Sources */,
@@ -2898,6 +2982,7 @@
DB31402B17554B71006C0E22 /* SDL_thread.c in Sources */,
DB31402C17554B71006C0E22 /* SDL_timer.c in Sources */,
DB31402D17554B71006C0E22 /* SDL_systimer.c in Sources */,
+ A704171C20F09AC900A82227 /* SDL_hidapi_switch.c in Sources */,
DB31402E17554B71006C0E22 /* SDL_cocoaclipboard.m in Sources */,
DB31402F17554B71006C0E22 /* SDL_cocoaevents.m in Sources */,
DB31403017554B71006C0E22 /* SDL_cocoakeyboard.m in Sources */,
@@ -2906,6 +2991,7 @@
5C2EF6A41FC98B39003F5197 /* SDL_yuv.c in Sources */,
5C2EF6F31FC9D182003F5197 /* SDL_cocoaopengles.m in Sources */,
DB31403317554B71006C0E22 /* SDL_cocoaopengl.m in Sources */,
+ A704172520F09AC900A82227 /* SDL_hidapi_xboxone.c in Sources */,
DB31403417554B71006C0E22 /* SDL_cocoashape.m in Sources */,
DB31403517554B71006C0E22 /* SDL_cocoavideo.m in Sources */,
DB31403617554B71006C0E22 /* SDL_cocoawindow.m in Sources */,
@@ -2914,6 +3000,8 @@
5C2EF6A61FC98B6C003F5197 /* yuv_rgb.c in Sources */,
DB31403917554B71006C0E22 /* SDL_blit.c in Sources */,
DB31403A17554B71006C0E22 /* SDL_blit_0.c in Sources */,
+ A704172220F09AC900A82227 /* SDL_hidapi_ps4.c in Sources */,
+ A704170B20F09A9800A82227 /* hid.c in Sources */,
DB31403B17554B71006C0E22 /* SDL_blit_1.c in Sources */,
DB31403C17554B71006C0E22 /* SDL_blit_A.c in Sources */,
DB31403D17554B71006C0E22 /* SDL_blit_auto.c in Sources */,
@@ -2922,6 +3010,7 @@
DB31404017554B71006C0E22 /* SDL_blit_slow.c in Sources */,
DB31404117554B71006C0E22 /* SDL_bmp.c in Sources */,
DB31404217554B71006C0E22 /* SDL_clipboard.c in Sources */,
+ A704171620F09AC900A82227 /* SDL_hidapijoystick.c in Sources */,
DB31404317554B71006C0E22 /* SDL_fillrect.c in Sources */,
DB31404417554B71006C0E22 /* SDL_pixels.c in Sources */,
DB31404517554B71006C0E22 /* SDL_rect.c in Sources */,
@@ -3019,6 +3108,7 @@
/usr/X11R6/include,
"$(VULKAN_SDK)/include",
../../src/video/khronos,
+ ../../src/hidapi/hidapi,
);
MACOSX_DEPLOYMENT_TARGET = 10.6;
SDKROOT = macosx;
@@ -3114,6 +3204,7 @@
/usr/X11R6/include,
"$(VULKAN_SDK)/include",
../../src/video/khronos,
+ ../../src/hidapi/hidapi,
);
MACOSX_DEPLOYMENT_TARGET = 10.6;
ONLY_ACTIVE_ARCH = YES;
diff --git a/android-project/app/src/main/java/org/libsdl/app/HIDDevice.java b/android-project/app/src/main/java/org/libsdl/app/HIDDevice.java
new file mode 100644
index 000000000..aa358d1fc
--- /dev/null
+++ b/android-project/app/src/main/java/org/libsdl/app/HIDDevice.java
@@ -0,0 +1,19 @@
+package org.libsdl.app;
+
+interface HIDDevice
+{
+ public int getId();
+ public int getVendorId();
+ public int getProductId();
+ public String getSerialNumber();
+ public int getVersion();
+ public String getManufacturerName();
+ public String getProductName();
+ public boolean open();
+ public int sendFeatureReport(byte[] report);
+ public int sendOutputReport(byte[] report);
+ public boolean getFeatureReport(byte[] report);
+ public void setFrozen(boolean frozen);
+ public void close();
+ public void shutdown();
+}
diff --git a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java
new file mode 100644
index 000000000..e8616c0b2
--- /dev/null
+++ b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java
@@ -0,0 +1,640 @@
+package org.libsdl.app;
+
+import android.content.Context;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothGattService;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+
+import java.lang.Runnable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.UUID;
+
+class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
+
+ private static final String TAG = "hidapi";
+ private HIDDeviceManager mManager;
+ private BluetoothDevice mDevice;
+ private int mDeviceId;
+ private BluetoothGatt mGatt;
+ private boolean mIsRegistered = false;
+ private boolean mIsConnected = false;
+ private boolean mIsChromebook = false;
+ private boolean mIsReconnecting = false;
+ private boolean mFrozen = false;
+ private LinkedList mOperations;
+ GattOperation mCurrentOperation = null;
+ private Handler mHandler;
+
+ private static final int TRANSPORT_AUTO = 0;
+ private static final int TRANSPORT_BREDR = 1;
+ private static final int TRANSPORT_LE = 2;
+
+ private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
+
+ static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
+ static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
+ static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
+ static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
+
+ static class GattOperation {
+ private enum Operation {
+ CHR_READ,
+ CHR_WRITE,
+ ENABLE_NOTIFICATION
+ }
+
+ Operation mOp;
+ UUID mUuid;
+ byte[] mValue;
+ BluetoothGatt mGatt;
+ boolean mResult = true;
+
+ private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
+ mGatt = gatt;
+ mOp = operation;
+ mUuid = uuid;
+ }
+
+ private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
+ mGatt = gatt;
+ mOp = operation;
+ mUuid = uuid;
+ mValue = value;
+ }
+
+ public void run() {
+ // This is executed in main thread
+ BluetoothGattCharacteristic chr;
+
+ switch (mOp) {
+ case CHR_READ:
+ chr = getCharacteristic(mUuid);
+ //Log.v(TAG, "Reading characteristic " + chr.getUuid());
+ if (!mGatt.readCharacteristic(chr)) {
+ Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
+ mResult = false;
+ break;
+ }
+ mResult = true;
+ break;
+ case CHR_WRITE:
+ chr = getCharacteristic(mUuid);
+ //Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
+ chr.setValue(mValue);
+ if (!mGatt.writeCharacteristic(chr)) {
+ Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
+ mResult = false;
+ break;
+ }
+ mResult = true;
+ break;
+ case ENABLE_NOTIFICATION:
+ chr = getCharacteristic(mUuid);
+ //Log.v(TAG, "Writing descriptor of " + chr.getUuid());
+ if (chr != null) {
+ BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
+ if (cccd != null) {
+ int properties = chr.getProperties();
+ byte[] value;
+ if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
+ value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
+ } else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
+ value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
+ } else {
+ Log.e(TAG, "Unable to start notifications on input characteristic");
+ mResult = false;
+ return;
+ }
+
+ mGatt.setCharacteristicNotification(chr, true);
+ cccd.setValue(value);
+ if (!mGatt.writeDescriptor(cccd)) {
+ Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
+ mResult = false;
+ return;
+ }
+ mResult = true;
+ }
+ }
+ }
+ }
+
+ public boolean finish() {
+ return mResult;
+ }
+
+ private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
+ BluetoothGattService valveService = mGatt.getService(steamControllerService);
+ if (valveService == null)
+ return null;
+ return valveService.getCharacteristic(uuid);
+ }
+
+ static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
+ return new GattOperation(gatt, Operation.CHR_READ, uuid);
+ }
+
+ static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
+ return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
+ }
+
+ static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
+ return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
+ }
+ }
+
+ public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
+ mManager = manager;
+ mDevice = device;
+ mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
+ mIsRegistered = false;
+ mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
+ mOperations = new LinkedList();
+ mHandler = new Handler(Looper.getMainLooper());
+
+ mGatt = connectGatt();
+ final HIDDeviceBLESteamController finalThis = this;
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ finalThis.checkConnectionForChromebookIssue();
+ }
+ }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
+ }
+
+ public String getIdentifier() {
+ return String.format("SteamController.%s", mDevice.getAddress());
+ }
+
+ public BluetoothGatt getGatt() {
+ return mGatt;
+ }
+
+ // Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
+ // of TRANSPORT_LE. Let's force ourselves to connect low energy.
+ private BluetoothGatt connectGatt(boolean managed) {
+ try {
+ Method m = mDevice.getClass().getDeclaredMethod("connectGatt", Context.class, boolean.class, BluetoothGattCallback.class, int.class);
+ return (BluetoothGatt) m.invoke(mDevice, mManager.getContext(), managed, this, TRANSPORT_LE);
+ } catch (Exception e) {
+ return mDevice.connectGatt(mManager.getContext(), managed, this);
+ }
+ }
+
+ private BluetoothGatt connectGatt() {
+ return connectGatt(false);
+ }
+
+ protected int getConnectionState() {
+
+ Context context = mManager.getContext();
+ if (context == null) {
+ // We are lacking any context to get our Bluetooth information. We'll just assume disconnected.
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
+ if (btManager == null) {
+ // This device doesn't support Bluetooth. We should never be here, because how did
+ // we instantiate a device to start with?
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
+ }
+
+ public void reconnect() {
+
+ if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
+ mGatt.disconnect();
+ mGatt = connectGatt();
+ }
+
+ }
+
+ protected void checkConnectionForChromebookIssue() {
+ if (!mIsChromebook) {
+ // We only do this on Chromebooks, because otherwise it's really annoying to just attempt
+ // over and over.
+ return;
+ }
+
+ int connectionState = getConnectionState();
+
+ switch (connectionState) {
+ case BluetoothProfile.STATE_CONNECTED:
+ if (!mIsConnected) {
+ // We are in the Bad Chromebook Place. We can force a disconnect
+ // to try to recover.
+ 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.");
+ mIsReconnecting = true;
+ mGatt.disconnect();
+ mGatt = connectGatt(false);
+ break;
+ }
+ else if (!isRegistered()) {
+ if (mGatt.getServices().size() > 0) {
+ Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover.");
+ probeService(this);
+ }
+ else {
+ Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover.");
+ mIsReconnecting = true;
+ mGatt.disconnect();
+ mGatt = connectGatt(false);
+ break;
+ }
+ }
+ else {
+ Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!");
+ return;
+ }
+ break;
+
+ case BluetoothProfile.STATE_DISCONNECTED:
+ 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.");
+
+ mIsReconnecting = true;
+ mGatt.disconnect();
+ mGatt = connectGatt(false);
+ break;
+
+ case BluetoothProfile.STATE_CONNECTING:
+ Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer.");
+ break;
+ }
+
+ final HIDDeviceBLESteamController finalThis = this;
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ finalThis.checkConnectionForChromebookIssue();
+ }
+ }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
+ }
+
+ private boolean isRegistered() {
+ return mIsRegistered;
+ }
+
+ private void setRegistered() {
+ mIsRegistered = true;
+ }
+
+ private boolean probeService(HIDDeviceBLESteamController controller) {
+
+ if (isRegistered()) {
+ return true;
+ }
+
+ if (!mIsConnected) {
+ return false;
+ }
+
+ Log.v(TAG, "probeService controller=" + controller);
+
+ for (BluetoothGattService service : mGatt.getServices()) {
+ if (service.getUuid().equals(steamControllerService)) {
+ Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
+
+ for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
+ if (chr.getUuid().equals(inputCharacteristic)) {
+ Log.v(TAG, "Found input characteristic");
+ // Start notifications
+ BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
+ if (cccd != null) {
+ enableNotification(chr.getUuid());
+ }
+ }
+ }
+ return true;
+ }
+ }
+
+ if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
+ Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
+ mIsConnected = false;
+ mIsReconnecting = true;
+ mGatt.disconnect();
+ mGatt = connectGatt(false);
+ }
+
+ return false;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ private void finishCurrentGattOperation() {
+ GattOperation op = null;
+ synchronized (mOperations) {
+ if (mCurrentOperation != null) {
+ op = mCurrentOperation;
+ mCurrentOperation = null;
+ }
+ }
+ if (op != null) {
+ boolean result = op.finish(); // TODO: Maybe in main thread as well?
+
+ // Our operation failed, let's add it back to the beginning of our queue.
+ if (!result) {
+ mOperations.addFirst(op);
+ }
+ }
+ executeNextGattOperation();
+ }
+
+ private void executeNextGattOperation() {
+ synchronized (mOperations) {
+ if (mCurrentOperation != null)
+ return;
+
+ if (mOperations.isEmpty())
+ return;
+
+ mCurrentOperation = mOperations.removeFirst();
+ }
+
+ // Run in main thread
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mOperations) {
+ if (mCurrentOperation == null) {
+ Log.e(TAG, "Current operation null in executor?");
+ return;
+ }
+
+ mCurrentOperation.run();
+ // now wait for the GATT callback and when it comes, finish this operation
+ }
+ }
+ });
+ }
+
+ private void queueGattOperation(GattOperation op) {
+ synchronized (mOperations) {
+ mOperations.add(op);
+ }
+ executeNextGattOperation();
+ }
+
+ private void enableNotification(UUID chrUuid) {
+ GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
+ queueGattOperation(op);
+ }
+
+ public void writeCharacteristic(UUID uuid, byte[] value) {
+ GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
+ queueGattOperation(op);
+ }
+
+ public void readCharacteristic(UUID uuid) {
+ GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
+ queueGattOperation(op);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////// BluetoothGattCallback overridden methods
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
+ //Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
+ mIsReconnecting = false;
+ if (newState == 2) {
+ mIsConnected = true;
+ // Run directly, without GattOperation
+ if (!isRegistered()) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mGatt.discoverServices();
+ }
+ });
+ }
+ }
+ else if (newState == 0) {
+ mIsConnected = false;
+ }
+
+ // Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
+ }
+
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ //Log.v(TAG, "onServicesDiscovered status=" + status);
+ if (status == 0) {
+ if (gatt.getServices().size() == 0) {
+ Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
+ mIsReconnecting = true;
+ mIsConnected = false;
+ gatt.disconnect();
+ mGatt = connectGatt(false);
+ }
+ else {
+ probeService(this);
+ }
+ }
+ }
+
+ public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+ //Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
+
+ if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
+ mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue());
+ }
+
+ finishCurrentGattOperation();
+ }
+
+ public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+ //Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
+
+ if (characteristic.getUuid().equals(reportCharacteristic)) {
+ // Only register controller with the native side once it has been fully configured
+ if (!isRegistered()) {
+ Log.v(TAG, "Registering Steam Controller with ID: " + getId());
+ mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0);
+ setRegistered();
+ }
+ }
+
+ finishCurrentGattOperation();
+ }
+
+ public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ // Enable this for verbose logging of controller input reports
+ //Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
+
+ if (characteristic.getUuid().equals(inputCharacteristic)) {
+ mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
+ }
+ }
+
+ public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+ //Log.v(TAG, "onDescriptorRead status=" + status);
+ }
+
+ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+ BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
+ //Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
+
+ if (chr.getUuid().equals(inputCharacteristic)) {
+ boolean hasWrittenInputDescriptor = true;
+ BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
+ if (reportChr != null) {
+ Log.v(TAG, "Writing report characteristic to enter valve mode");
+ reportChr.setValue(enterValveMode);
+ gatt.writeCharacteristic(reportChr);
+ }
+ }
+
+ finishCurrentGattOperation();
+ }
+
+ public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
+ //Log.v(TAG, "onReliableWriteCompleted status=" + status);
+ }
+
+ public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
+ //Log.v(TAG, "onReadRemoteRssi status=" + status);
+ }
+
+ public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
+ //Log.v(TAG, "onMtuChanged status=" + status);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+ //////// Public API
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public int getId() {
+ return mDeviceId;
+ }
+
+ @Override
+ public int getVendorId() {
+ // Valve Corporation
+ final int VALVE_USB_VID = 0x28DE;
+ return VALVE_USB_VID;
+ }
+
+ @Override
+ public int getProductId() {
+ // We don't have an easy way to query from the Bluetooth device, but we know what it is
+ final int D0G_BLE2_PID = 0x1106;
+ return D0G_BLE2_PID;
+ }
+
+ @Override
+ public String getSerialNumber() {
+ // This will be read later via feature report by Steam
+ return "12345";
+ }
+
+ @Override
+ public int getVersion() {
+ return 0;
+ }
+
+ @Override
+ public String getManufacturerName() {
+ return "Valve Corporation";
+ }
+
+ @Override
+ public String getProductName() {
+ return "Steam Controller";
+ }
+
+ @Override
+ public boolean open() {
+ return true;
+ }
+
+ @Override
+ public int sendFeatureReport(byte[] report) {
+ if (!isRegistered()) {
+ Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!");
+ if (mIsConnected) {
+ probeService(this);
+ }
+ return -1;
+ }
+
+ // We need to skip the first byte, as that doesn't go over the air
+ byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
+ //Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report));
+ writeCharacteristic(reportCharacteristic, actual_report);
+ return report.length;
+ }
+
+ @Override
+ public int sendOutputReport(byte[] report) {
+ if (!isRegistered()) {
+ Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!");
+ if (mIsConnected) {
+ probeService(this);
+ }
+ return -1;
+ }
+
+ //Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report));
+ writeCharacteristic(reportCharacteristic, report);
+ return report.length;
+ }
+
+ @Override
+ public boolean getFeatureReport(byte[] report) {
+ if (!isRegistered()) {
+ Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!");
+ if (mIsConnected) {
+ probeService(this);
+ }
+ return false;
+ }
+
+ //Log.v(TAG, "getFeatureReport");
+ readCharacteristic(reportCharacteristic);
+ return true;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public void setFrozen(boolean frozen) {
+ mFrozen = frozen;
+ }
+
+ @Override
+ public void shutdown() {
+ BluetoothGatt g = mGatt;
+ if (g != null) {
+ g.disconnect();
+ g.close();
+ mGatt = null;
+ }
+ mManager = null;
+ mIsRegistered = false;
+ mIsConnected = false;
+ mOperations.clear();
+ }
+
+}
+
diff --git a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
new file mode 100644
index 000000000..ffe6dc080
--- /dev/null
+++ b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
@@ -0,0 +1,614 @@
+package org.libsdl.app;
+
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.util.Log;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.hardware.usb.*;
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.List;
+
+public class HIDDeviceManager {
+ private static final String TAG = "hidapi";
+ private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
+
+ protected Context mContext;
+ private HashMap mDevicesById = new HashMap();
+ private HashMap mUSBDevices = new HashMap();
+ private HashMap mBluetoothDevices = new HashMap();
+ private int mNextDeviceId = 0;
+ private SharedPreferences mSharedPreferences = null;
+ private boolean mIsChromebook = false;
+ private UsbManager mUsbManager;
+ private Handler mHandler;
+ private BluetoothManager mBluetoothManager;
+ private List mLastBluetoothDevices;
+
+ private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
+ UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ handleUsbDeviceAttached(usbDevice);
+ } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
+ UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ handleUsbDeviceDetached(usbDevice);
+ } else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
+ UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
+ }
+ }
+ };
+
+ private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ // Bluetooth device was connected. If it was a Steam Controller, handle it
+ if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
+ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ Log.d(TAG, "Bluetooth device connected: " + device);
+
+ if (isSteamController(device)) {
+ connectBluetoothDevice(device);
+ }
+ }
+
+ // Bluetooth device was disconnected, remove from controller manager (if any)
+ if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
+ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ Log.d(TAG, "Bluetooth device disconnected: " + device);
+
+ disconnectBluetoothDevice(device);
+ }
+ }
+ };
+
+ public HIDDeviceManager(Context context) {
+ mContext = context;
+
+ HIDDeviceRegisterCallback(this);
+
+ mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
+ mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
+
+// if (shouldClear) {
+// SharedPreferences.Editor spedit = mSharedPreferences.edit();
+// spedit.clear();
+// spedit.commit();
+// }
+// else
+ {
+ mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
+ }
+
+ initializeUSB();
+ initializeBluetooth();
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public int getDeviceIDForIdentifier(String identifier) {
+ SharedPreferences.Editor spedit = mSharedPreferences.edit();
+
+ int result = mSharedPreferences.getInt(identifier, 0);
+ if (result == 0) {
+ result = mNextDeviceId++;
+ spedit.putInt("next_device_id", mNextDeviceId);
+ }
+
+ spedit.putInt(identifier, result);
+ spedit.commit();
+ return result;
+ }
+
+ protected void initializeUSB() {
+ mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
+
+ /*
+ // Logging
+ for (UsbDevice device : mUsbManager.getDeviceList().values()) {
+ Log.i(TAG,"Path: " + device.getDeviceName());
+ Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
+ Log.i(TAG,"Product: " + device.getProductName());
+ Log.i(TAG,"ID: " + device.getDeviceId());
+ Log.i(TAG,"Class: " + device.getDeviceClass());
+ Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
+ Log.i(TAG,"Vendor ID " + device.getVendorId());
+ Log.i(TAG,"Product ID: " + device.getProductId());
+ Log.i(TAG,"Interface count: " + device.getInterfaceCount());
+ Log.i(TAG,"---------------------------------------");
+
+ // Get interface details
+ for (int index = 0; index < device.getInterfaceCount(); index++) {
+ UsbInterface mUsbInterface = device.getInterface(index);
+ Log.i(TAG," ***** *****");
+ Log.i(TAG," Interface index: " + index);
+ Log.i(TAG," Interface ID: " + mUsbInterface.getId());
+ Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass());
+ Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass());
+ Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
+ Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
+
+ // Get endpoint details
+ for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
+ {
+ UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
+ Log.i(TAG," ++++ ++++ ++++");
+ Log.i(TAG," Endpoint index: " + epi);
+ Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
+ Log.i(TAG," Direction: " + mEndpoint.getDirection());
+ Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
+ Log.i(TAG," Interval: " + mEndpoint.getInterval());
+ Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
+ Log.i(TAG," Type: " + mEndpoint.getType());
+ }
+ }
+ }
+ Log.i(TAG," No more devices connected.");
+ */
+
+ // Register for USB broadcasts and permission completions
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
+ mContext.registerReceiver(mUsbBroadcast, filter);
+
+ for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
+ handleUsbDeviceAttached(usbDevice);
+ }
+ }
+
+ UsbManager getUSBManager() {
+ return mUsbManager;
+ }
+
+ protected void shutdownUSB() {
+ mContext.unregisterReceiver(mUsbBroadcast);
+ }
+
+ protected boolean isHIDDeviceUSB(UsbDevice usbDevice) {
+ for (int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); ++interface_number) {
+ if (isHIDDeviceInterface(usbDevice, interface_number)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected boolean isHIDDeviceInterface(UsbDevice usbDevice, int interface_number) {
+ UsbInterface usbInterface = usbDevice.getInterface(interface_number);
+ if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
+ return true;
+ }
+ if (interface_number == 0) {
+ if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
+ final int XB360_IFACE_SUBCLASS = 93;
+ final int XB360_IFACE_PROTOCOL = 1; // Wired only
+ final int[] SUPPORTED_VENDORS = {
+ 0x0079, // GPD Win 2
+ 0x044f, // Thrustmaster
+ 0x045e, // Microsoft
+ 0x046d, // Logitech
+ 0x056e, // Elecom
+ 0x06a3, // Saitek
+ 0x0738, // Mad Catz
+ 0x07ff, // Mad Catz
+ 0x0e6f, // Unknown
+ 0x0f0d, // Hori
+ 0x11c9, // Nacon
+ 0x12ab, // Unknown
+ 0x1430, // RedOctane
+ 0x146b, // BigBen
+ 0x1532, // Razer Sabertooth
+ 0x15e4, // Numark
+ 0x162e, // Joytech
+ 0x1689, // Razer Onza
+ 0x1bad, // Harmonix
+ 0x24c6, // PowerA
+ };
+
+ if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
+ usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
+ usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL) {
+ int vendor_id = usbDevice.getVendorId();
+ for (int supportedVid : SUPPORTED_VENDORS) {
+ if (vendor_id == supportedVid) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ protected boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
+ final int XB1_IFACE_SUBCLASS = 71;
+ final int XB1_IFACE_PROTOCOL = 208;
+ final int[] SUPPORTED_VENDORS = {
+ 0x045e, // Microsoft
+ 0x0738, // Mad Catz
+ 0x0e6f, // Unknown
+ 0x0f0d, // Hori
+ 0x1532, // Razer Wildcat
+ 0x24c6, // PowerA
+ };
+
+ if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
+ usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
+ usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
+ int vendor_id = usbDevice.getVendorId();
+ for (int supportedVid : SUPPORTED_VENDORS) {
+ if (vendor_id == supportedVid) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ protected void handleUsbDeviceAttached(UsbDevice usbDevice) {
+ if (isHIDDeviceUSB(usbDevice)) {
+ connectHIDDeviceUSB(usbDevice);
+ }
+ }
+
+ protected void handleUsbDeviceDetached(UsbDevice usbDevice) {
+ HIDDeviceUSB device = mUSBDevices.get(usbDevice);
+ if (device == null)
+ return;
+
+ int id = device.getId();
+ mUSBDevices.remove(usbDevice);
+ mDevicesById.remove(id);
+ device.shutdown();
+ HIDDeviceDisconnected(id);
+ }
+
+ protected void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
+ HIDDeviceUSB device = mUSBDevices.get(usbDevice);
+ if (device == null)
+ return;
+
+ boolean opened = false;
+ if (permission_granted) {
+ opened = device.open();
+ }
+ HIDDeviceOpenResult(device.getId(), opened);
+ }
+
+ protected void connectHIDDeviceUSB(UsbDevice usbDevice) {
+ synchronized (this) {
+ for (int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); interface_number++) {
+ if (isHIDDeviceInterface(usbDevice, interface_number)) {
+ HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_number);
+ int id = device.getId();
+ mUSBDevices.put(usbDevice, device);
+ mDevicesById.put(id, device);
+ HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), interface_number);
+ break;
+ }
+ }
+ }
+ }
+
+ protected void initializeBluetooth() {
+ Log.d(TAG, "Initializing Bluetooth");
+
+ // Find bonded bluetooth controllers and create SteamControllers for them
+ mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
+ if (mBluetoothManager == null) {
+ // This device doesn't support Bluetooth.
+ return;
+ }
+
+ BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
+ if (btAdapter == null) {
+ // This device has Bluetooth support in the codebase, but has no available adapters.
+ return;
+ }
+
+ // Get our bonded devices.
+ for (BluetoothDevice device : btAdapter.getBondedDevices()) {
+
+ Log.d(TAG, "Bluetooth device available: " + device);
+ if (isSteamController(device)) {
+ connectBluetoothDevice(device);
+ }
+
+ }
+
+ // NOTE: These don't work on Chromebooks, to my undying dismay.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+ filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ mContext.registerReceiver(mBluetoothBroadcast, filter);
+
+ if (mIsChromebook) {
+ mHandler = new Handler(Looper.getMainLooper());
+ mLastBluetoothDevices = new ArrayList<>();
+
+ // final HIDDeviceManager finalThis = this;
+ // mHandler.postDelayed(new Runnable() {
+ // @Override
+ // public void run() {
+ // finalThis.chromebookConnectionHandler();
+ // }
+ // }, 5000);
+ }
+ }
+
+ protected void shutdownBluetooth() {
+ mContext.unregisterReceiver(mBluetoothBroadcast);
+ }
+
+ // Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
+ // This function provides a sort of dummy version of that, watching for changes in the
+ // connected devices and attempting to add controllers as things change.
+ public void chromebookConnectionHandler() {
+ if (!mIsChromebook) {
+ return;
+ }
+
+ ArrayList disconnected = new ArrayList<>();
+ ArrayList connected = new ArrayList<>();
+
+ List currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
+
+ for (BluetoothDevice bluetoothDevice : currentConnected) {
+ if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
+ connected.add(bluetoothDevice);
+ }
+ }
+ for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
+ if (!currentConnected.contains(bluetoothDevice)) {
+ disconnected.add(bluetoothDevice);
+ }
+ }
+
+ mLastBluetoothDevices = currentConnected;
+
+ for (BluetoothDevice bluetoothDevice : disconnected) {
+ disconnectBluetoothDevice(bluetoothDevice);
+ }
+ for (BluetoothDevice bluetoothDevice : connected) {
+ connectBluetoothDevice(bluetoothDevice);
+ }
+
+ final HIDDeviceManager finalThis = this;
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ finalThis.chromebookConnectionHandler();
+ }
+ }, 10000);
+ }
+
+ public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
+ Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
+ synchronized (this) {
+ if (mBluetoothDevices.containsKey(bluetoothDevice)) {
+ Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
+
+ HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
+ device.reconnect();
+
+ return false;
+ }
+ HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
+ int id = device.getId();
+ mBluetoothDevices.put(bluetoothDevice, device);
+ mDevicesById.put(id, device);
+
+ // The Steam Controller will mark itself connected once initialization is complete
+ }
+ return true;
+ }
+
+ public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
+ synchronized (this) {
+ HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
+ if (device == null)
+ return;
+
+ int id = device.getId();
+ mBluetoothDevices.remove(bluetoothDevice);
+ mDevicesById.remove(id);
+ device.shutdown();
+ HIDDeviceDisconnected(id);
+ }
+ }
+
+ public boolean isSteamController(BluetoothDevice bluetoothDevice) {
+ // Sanity check. If you pass in a null device, by definition it is never a Steam Controller.
+ if (bluetoothDevice == null) {
+ return false;
+ }
+
+ // If the device has no local name, we really don't want to try an equality check against it.
+ if (bluetoothDevice.getName() == null) {
+ return false;
+ }
+
+ return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
+ }
+
+ public void close() {
+ shutdownUSB();
+ shutdownBluetooth();
+ synchronized (this) {
+ for (HIDDevice device : mDevicesById.values()) {
+ device.shutdown();
+ }
+ mDevicesById.clear();
+ mBluetoothDevices.clear();
+ HIDDeviceReleaseCallback();
+ }
+ }
+
+ public void setFrozen(boolean frozen) {
+ synchronized (this) {
+ for (HIDDevice device : mDevicesById.values()) {
+ device.setFrozen(frozen);
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ private HIDDevice getDevice(int id) {
+ synchronized (this) {
+ HIDDevice result = mDevicesById.get(id);
+ if (result == null) {
+ Log.v(TAG, "No device for id: " + id);
+ Log.v(TAG, "Available devices: " + mDevicesById.keySet());
+ }
+ return result;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////// JNI interface functions
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ boolean openDevice(int deviceID) {
+ // Look to see if this is a USB device and we have permission to access it
+ for (HIDDeviceUSB device : mUSBDevices.values()) {
+ if (deviceID == device.getId()) {
+ UsbDevice usbDevice = device.getDevice();
+ if (!mUsbManager.hasPermission(usbDevice)) {
+ HIDDeviceOpenPending(deviceID);
+ try {
+ mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), 0));
+ } catch (Exception e) {
+ Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
+ HIDDeviceOpenResult(deviceID, false);
+ }
+ return false;
+ }
+ break;
+ }
+ }
+
+ try {
+ Log.v(TAG, "openDevice deviceID=" + deviceID);
+ HIDDevice device;
+ device = getDevice(deviceID);
+ if (device == null) {
+ HIDDeviceDisconnected(deviceID);
+ return false;
+ }
+
+ return device.open();
+ } catch (Exception e) {
+ Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
+ }
+ return false;
+ }
+
+ int sendOutputReport(int deviceID, byte[] report) {
+ try {
+ Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
+ HIDDevice device;
+ device = getDevice(deviceID);
+ if (device == null) {
+ HIDDeviceDisconnected(deviceID);
+ return -1;
+ }
+
+ return device.sendOutputReport(report);
+ } catch (Exception e) {
+ Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
+ }
+ return -1;
+ }
+
+ int sendFeatureReport(int deviceID, byte[] report) {
+ try {
+ Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
+ HIDDevice device;
+ device = getDevice(deviceID);
+ if (device == null) {
+ HIDDeviceDisconnected(deviceID);
+ return -1;
+ }
+
+ return device.sendFeatureReport(report);
+ } catch (Exception e) {
+ Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
+ }
+ return -1;
+ }
+
+ boolean getFeatureReport(int deviceID, byte[] report) {
+ try {
+ Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
+ HIDDevice device;
+ device = getDevice(deviceID);
+ if (device == null) {
+ HIDDeviceDisconnected(deviceID);
+ return false;
+ }
+
+ return device.getFeatureReport(report);
+ } catch (Exception e) {
+ Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
+ }
+ return false;
+ }
+
+ void closeDevice(int deviceID) {
+ try {
+ Log.v(TAG, "closeDevice deviceID=" + deviceID);
+ HIDDevice device;
+ device = getDevice(deviceID);
+ if (device == null) {
+ HIDDeviceDisconnected(deviceID);
+ return;
+ }
+
+ device.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+ /////////////// Native methods
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ private native void HIDDeviceRegisterCallback(Object callbackHandler);
+ private native void HIDDeviceReleaseCallback();
+
+ 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);
+ native void HIDDeviceOpenPending(int deviceID);
+ native void HIDDeviceOpenResult(int deviceID, boolean opened);
+ native void HIDDeviceDisconnected(int deviceID);
+
+ native void HIDDeviceInputReport(int deviceID, byte[] report);
+ native void HIDDeviceFeatureReport(int deviceID, byte[] report);
+}
diff --git a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java
new file mode 100644
index 000000000..48a87e24d
--- /dev/null
+++ b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java
@@ -0,0 +1,298 @@
+package org.libsdl.app;
+
+import android.hardware.usb.*;
+import android.os.Build;
+import android.util.Log;
+import java.util.Arrays;
+
+class HIDDeviceUSB implements HIDDevice {
+
+ private static final String TAG = "hidapi";
+
+ protected HIDDeviceManager mManager;
+ protected UsbDevice mDevice;
+ protected int mInterface;
+ protected int mDeviceId;
+ protected UsbDeviceConnection mConnection;
+ protected UsbEndpoint mInputEndpoint;
+ protected UsbEndpoint mOutputEndpoint;
+ protected InputThread mInputThread;
+ protected boolean mRunning;
+ protected boolean mFrozen;
+
+ public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_number) {
+ mManager = manager;
+ mDevice = usbDevice;
+ mInterface = interface_number;
+ mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
+ mRunning = false;
+ }
+
+ public String getIdentifier() {
+ return String.format("%s/%x/%x", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId());
+ }
+
+ @Override
+ public int getId() {
+ return mDeviceId;
+ }
+
+ @Override
+ public int getVendorId() {
+ return mDevice.getVendorId();
+ }
+
+ @Override
+ public int getProductId() {
+ return mDevice.getProductId();
+ }
+
+ @Override
+ public String getSerialNumber() {
+ String result = null;
+ if (Build.VERSION.SDK_INT >= 21) {
+ result = mDevice.getSerialNumber();
+ }
+ if (result == null) {
+ result = "";
+ }
+ return result;
+ }
+
+ @Override
+ public int getVersion() {
+ return 0;
+ }
+
+ @Override
+ public String getManufacturerName() {
+ String result = null;
+ if (Build.VERSION.SDK_INT >= 21) {
+ result = mDevice.getManufacturerName();
+ }
+ if (result == null) {
+ result = String.format("%x", getVendorId());
+ }
+ return result;
+ }
+
+ @Override
+ public String getProductName() {
+ String result = null;
+ if (Build.VERSION.SDK_INT >= 21) {
+ result = mDevice.getProductName();
+ }
+ if (result == null) {
+ result = String.format("%x", getProductId());
+ }
+ return result;
+ }
+
+ public UsbDevice getDevice() {
+ return mDevice;
+ }
+
+ public String getDeviceName() {
+ return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
+ }
+
+ @Override
+ public boolean open() {
+ mConnection = mManager.getUSBManager().openDevice(mDevice);
+ if (mConnection == null) {
+ Log.w(TAG, "Unable to open USB device " + getDeviceName());
+ return false;
+ }
+
+ // Force claim all interfaces
+ for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
+ UsbInterface iface = mDevice.getInterface(i);
+
+ if (!mConnection.claimInterface(iface, true)) {
+ Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
+ close();
+ return false;
+ }
+ }
+
+ // Find the endpoints
+ UsbInterface iface = mDevice.getInterface(mInterface);
+ for (int j = 0; j < iface.getEndpointCount(); j++) {
+ UsbEndpoint endpt = iface.getEndpoint(j);
+ switch (endpt.getDirection()) {
+ case UsbConstants.USB_DIR_IN:
+ if (mInputEndpoint == null) {
+ mInputEndpoint = endpt;
+ }
+ break;
+ case UsbConstants.USB_DIR_OUT:
+ if (mOutputEndpoint == null) {
+ mOutputEndpoint = endpt;
+ }
+ break;
+ }
+ }
+
+ // Make sure the required endpoints were present
+ if (mInputEndpoint == null || mOutputEndpoint == null) {
+ Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
+ close();
+ return false;
+ }
+
+ // Start listening for input
+ mRunning = true;
+ mInputThread = new InputThread();
+ mInputThread.start();
+
+ return true;
+ }
+
+ @Override
+ public int sendFeatureReport(byte[] report) {
+ int res = -1;
+ int offset = 0;
+ int length = report.length;
+ boolean skipped_report_id = false;
+ byte report_number = report[0];
+
+ if (report_number == 0x0) {
+ ++offset;
+ --length;
+ skipped_report_id = true;
+ }
+
+ res = mConnection.controlTransfer(
+ UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
+ 0x09/*HID set_report*/,
+ (3/*HID feature*/ << 8) | report_number,
+ 0,
+ report, offset, length,
+ 1000/*timeout millis*/);
+
+ if (res < 0) {
+ Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
+ return -1;
+ }
+
+ if (skipped_report_id) {
+ ++length;
+ }
+ return length;
+ }
+
+ @Override
+ public int sendOutputReport(byte[] report) {
+ int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
+ if (r != report.length) {
+ Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
+ }
+ return r;
+ }
+
+ @Override
+ public boolean getFeatureReport(byte[] report) {
+ int res = -1;
+ int offset = 0;
+ int length = report.length;
+ boolean skipped_report_id = false;
+ byte report_number = report[0];
+
+ if (report_number == 0x0) {
+ /* Offset the return buffer by 1, so that the report ID
+ will remain in byte 0. */
+ ++offset;
+ --length;
+ skipped_report_id = true;
+ }
+
+ res = mConnection.controlTransfer(
+ UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
+ 0x01/*HID get_report*/,
+ (3/*HID feature*/ << 8) | report_number,
+ 0,
+ report, offset, length,
+ 1000/*timeout millis*/);
+
+ if (res < 0) {
+ Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
+ return false;
+ }
+
+ if (skipped_report_id) {
+ ++res;
+ ++length;
+ }
+
+ byte[] data;
+ if (res == length) {
+ data = report;
+ } else {
+ data = Arrays.copyOfRange(report, 0, res);
+ }
+ mManager.HIDDeviceFeatureReport(mDeviceId, data);
+
+ return true;
+ }
+
+ @Override
+ public void close() {
+ mRunning = false;
+ if (mInputThread != null) {
+ while (mInputThread.isAlive()) {
+ mInputThread.interrupt();
+ try {
+ mInputThread.join();
+ } catch (InterruptedException e) {
+ // Keep trying until we're done
+ }
+ }
+ mInputThread = null;
+ }
+ if (mConnection != null) {
+ for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
+ UsbInterface iface = mDevice.getInterface(i);
+ mConnection.releaseInterface(iface);
+ }
+ mConnection.close();
+ mConnection = null;
+ }
+ }
+
+ @Override
+ public void shutdown() {
+ close();
+ mManager = null;
+ }
+
+ @Override
+ public void setFrozen(boolean frozen) {
+ mFrozen = frozen;
+ }
+
+ protected class InputThread extends Thread {
+ @Override
+ public void run() {
+ int packetSize = mInputEndpoint.getMaxPacketSize();
+ byte[] packet = new byte[packetSize];
+ while (mRunning) {
+ int r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
+ if (r < 0) {
+ // Could be a timeout or an I/O error
+ }
+ if (r > 0) {
+ byte[] data;
+ if (r == packetSize) {
+ data = packet;
+ } else {
+ data = Arrays.copyOfRange(packet, 0, r);
+ }
+
+ if (!mFrozen) {
+ mManager.HIDDeviceInputReport(mDeviceId, data);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
index fd95b0f83..ebd52abf7 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
@@ -80,7 +80,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
protected static Hashtable mCursors;
protected static int mLastCursorID;
protected static SDLGenericMotionListener_API12 mMotionListener;
-
+ protected static HIDDeviceManager mHIDDeviceManager;
// This is what SDL runs in. It invokes SDL_main(), eventually
protected static Thread mSDLThread;
@@ -241,6 +241,8 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
mClipboardHandler = new SDLClipboardHandler_Old();
}
+ mHIDDeviceManager = new HIDDeviceManager(this);
+
// Set up the surface
mSurface = new SDLSurface(getApplication());
@@ -276,6 +278,10 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
return;
}
+ if (mHIDDeviceManager != null) {
+ mHIDDeviceManager.setFrozen(true);
+ }
+
SDLActivity.handleNativeState();
}
@@ -290,6 +296,10 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
return;
}
+ if (mHIDDeviceManager != null) {
+ mHIDDeviceManager.setFrozen(false);
+ }
+
SDLActivity.handleNativeState();
}
@@ -330,6 +340,11 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
protected void onDestroy() {
Log.v(TAG, "onDestroy()");
+ if (mHIDDeviceManager != null) {
+ mHIDDeviceManager.close();
+ mHIDDeviceManager = null;
+ }
+
if (SDLActivity.mBrokenLibraries) {
super.onDestroy();
// Reset everything in case the user re opens the app
@@ -466,10 +481,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
/* The native thread has finished */
public static void handleNativeExit() {
SDLActivity.mSDLThread = null;
-
- // Make sure we currently have a singleton before we try to call it.
- if (mSingleton != null)
- mSingleton.finish();
+ mSingleton.finish();
}
diff --git a/configure b/configure
index 6c9cde86e..e06743de4 100755
--- a/configure
+++ b/configure
@@ -868,6 +868,7 @@ enable_pthread_sem
enable_directx
enable_wasapi
enable_sdl_dlopen
+enable_hidapi
enable_clock_gettime
enable_rpath
enable_render_d3d
@@ -1623,6 +1624,8 @@ Optional Features:
--enable-directx use DirectX for Windows audio/video [[default=yes]]
--enable-wasapi use the Windows WASAPI audio driver [[default=yes]]
--enable-sdl-dlopen use dlopen for shared object loading [[default=yes]]
+ --enable-hidapi use HIDAPI for low level joystick drivers
+ [[default=no]]
--enable-clock_gettime use clock_gettime() instead of gettimeofday() on
UNIX [[default=yes]]
--enable-rpath use an rpath when linking SDL [[default=yes]]
@@ -16773,7 +16776,7 @@ $as_echo "#define HAVE_SA_SIGACTION 1" >>confdefs.h
fi
- for ac_header in libunwind.h
+ for ac_header in libunwind.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "libunwind.h" "ac_cv_header_libunwind_h" "$ac_includes_default"
if test "x$ac_cv_header_libunwind_h" = xyes; then :
@@ -18377,7 +18380,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PulseAudio $PULSEAUDIO_REQUIRED_VERSION support" >&5
$as_echo_n "checking for PulseAudio $PULSEAUDIO_REQUIRED_VERSION support... " >&6; }
if test x$PKG_CONFIG != xno; then
- if $PKG_CONFIG --atleast-pkgconfig-version 0.7 && $PKG_CONFIG --atleast-version $PULSEAUDIO_REQUIRED_VERSION libpulse-simple; then
+ if $PKG_CONFIG --atleast-pkgconfig-version 0.7 && $PKG_CONFIG --atleast-version $PULSEAUDIO_REQUIRED_VERSION libpulse-simple; then
PULSEAUDIO_CFLAGS=`$PKG_CONFIG --cflags libpulse-simple`
PULSEAUDIO_LIBS=`$PKG_CONFIG --libs libpulse-simple`
audio_pulseaudio=yes
@@ -20463,7 +20466,7 @@ $as_echo_n "checking for XGenericEvent... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
- #include
+ #include
int
main ()
@@ -20807,13 +20810,13 @@ $as_echo "#define SDL_VIDEO_DRIVER_X11_XINPUT2 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for xinput2 multitouch" >&5
$as_echo_n "checking for xinput2 multitouch... " >&6; }
- have_xinput2_multitouch=no
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+ have_xinput2_multitouch=no
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
- #include
- #include
- #include
+ #include
+ #include
+ #include
int
main ()
@@ -20828,14 +20831,14 @@ XITouchClassInfo *t;
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
- have_xinput2_multitouch=yes
- $as_echo "#define SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH 1" >>confdefs.h
+ have_xinput2_multitouch=yes
+ $as_echo "#define SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH 1" >>confdefs.h
- SUMMARY_video_x11="${SUMMARY_video_x11} xinput2_multitouch"
+ SUMMARY_video_x11="${SUMMARY_video_x11} xinput2_multitouch"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_xinput2_multitouch" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_xinput2_multitouch" >&5
$as_echo "$have_xinput2_multitouch" >&6; }
fi
# Check whether --enable-video-x11-xrandr was given.
@@ -23728,6 +23731,93 @@ $as_echo "#define SDL_JOYSTICK_USBHID 1" >>confdefs.h
esac
}
+CheckHIDAPI()
+{
+ # The hidraw support doesn't catch Xbox, PS4 and Nintendo controllers,
+ # so we'll just use libusb when it's available.
+ #
+ # Except that libusb requires root permissions to open devices, so that's not generally useful, and we'll disable this by default.
+ # Check whether --enable-hidapi was given.
+if test "${enable_hidapi+set}" = set; then :
+ enableval=$enable_hidapi;
+else
+ enable_hidapi=no
+fi
+
+ if test x$enable_joystick = xyes -a x$enable_hidapi = xyes; then
+ hidapi_support=no
+ # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PKG_CONFIG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PKG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_PKG_CONFIG" && ac_cv_path_PKG_CONFIG="no"
+ ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test x$PKG_CONFIG != xno; then
+ LIBUSB_CFLAGS=`$PKG_CONFIG --cflags libusb-1.0`
+ LIBUSB_LDFLAGS=`$PKG_CONFIG --libs libusb-1.0`
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$save_CFLAGS $LIBUSB_CFLAGS"
+ ac_fn_c_check_header_mongrel "$LINENO" "libusb.h" "ac_cv_header_libusb_h" "$ac_includes_default"
+if test "x$ac_cv_header_libusb_h" = xyes; then :
+ have_libusb_h=yes
+fi
+
+
+ CFLAGS="$save_CFLAGS"
+ fi
+ if test x$have_libusb_h = xyes; then
+ hidapi_support=yes
+
+$as_echo "#define SDL_JOYSTICK_HIDAPI 1" >>confdefs.h
+
+ EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
+ SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
+ SOURCES="$SOURCES $srcdir/src/hidapi/libusb/hid.c"
+ EXTRA_CFLAGS="$EXTRA_CFLAGS $LIBUSB_CFLAGS"
+ EXTRA_LDFLAGS="$EXTRA_LDFLAGS $LIBUSB_LDFLAGS"
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for hidapi support" >&5
+$as_echo_n "checking for hidapi support... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hidapi_support" >&5
+$as_echo "$hidapi_support" >&6; }
+ fi
+}
+
CheckClockGettime()
{
# Check whether --enable-clock_gettime was given.
@@ -23939,6 +24029,7 @@ case "$host" in
esac
CheckTslib
CheckUSBHID
+ CheckHIDAPI
CheckPTHREAD
CheckClockGettime
CheckLinuxVersion
@@ -24514,7 +24605,13 @@ $as_echo "#define SDL_AUDIO_DRIVER_COREAUDIO 1" >>confdefs.h
$as_echo "#define SDL_JOYSTICK_IOKIT 1" >>confdefs.h
+
+$as_echo "#define SDL_JOYSTICK_HIDAPI 1" >>confdefs.h
+
SOURCES="$SOURCES $srcdir/src/joystick/darwin/*.c"
+ SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
+ SOURCES="$SOURCES $srcdir/src/hidapi/mac/hid.c"
+ EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
have_joystick=yes
fi
# Set up files for the haptic library
diff --git a/configure.in b/configure.in
index a8f89b5ee..6f8103f5d 100644
--- a/configure.in
+++ b/configure.in
@@ -285,7 +285,7 @@ if test x$enable_libc = xyes; then
AC_CHECK_MEMBER(struct sigaction.sa_sigaction,[AC_DEFINE([HAVE_SA_SIGACTION], 1, [ ])], ,[#include ])
- dnl Check for additional non-standard headers
+ dnl Check for additional non-standard headers
AC_CHECK_HEADERS(libunwind.h)
fi
@@ -963,7 +963,7 @@ AC_HELP_STRING([--enable-pulseaudio], [use PulseAudio [[default=yes]]]),
AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
AC_MSG_CHECKING(for PulseAudio $PULSEAUDIO_REQUIRED_VERSION support)
if test x$PKG_CONFIG != xno; then
- if $PKG_CONFIG --atleast-pkgconfig-version 0.7 && $PKG_CONFIG --atleast-version $PULSEAUDIO_REQUIRED_VERSION libpulse-simple; then
+ if $PKG_CONFIG --atleast-pkgconfig-version 0.7 && $PKG_CONFIG --atleast-version $PULSEAUDIO_REQUIRED_VERSION libpulse-simple; then
PULSEAUDIO_CFLAGS=`$PKG_CONFIG --cflags libpulse-simple`
PULSEAUDIO_LIBS=`$PKG_CONFIG --libs libpulse-simple`
audio_pulseaudio=yes
@@ -1748,7 +1748,7 @@ AC_HELP_STRING([--enable-x11-shared], [dynamically load X11 support [[default=ma
AC_MSG_CHECKING([for XGenericEvent])
have_XGenericEvent=no
AC_TRY_COMPILE([
- #include
+ #include
],[
Display *display;
XEvent event;
@@ -1862,20 +1862,20 @@ AC_HELP_STRING([--enable-video-x11-xinput], [enable X11 XInput extension for man
SUMMARY_video_x11="${SUMMARY_video_x11} xinput2"
AC_DEFINE(SDL_VIDEO_DRIVER_X11_XINPUT2, 1, [ ])
AC_MSG_CHECKING(for xinput2 multitouch)
- have_xinput2_multitouch=no
- AC_TRY_COMPILE([
- #include
- #include
- #include
- ],[
+ have_xinput2_multitouch=no
+ AC_TRY_COMPILE([
+ #include
+ #include
+ #include
+ ],[
int event_type = XI_TouchBegin;
XITouchClassInfo *t;
- ],[
- have_xinput2_multitouch=yes
- AC_DEFINE([SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH], 1, [])
- SUMMARY_video_x11="${SUMMARY_video_x11} xinput2_multitouch"
- ])
- AC_MSG_RESULT($have_xinput2_multitouch)
+ ],[
+ have_xinput2_multitouch=yes
+ AC_DEFINE([SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH], 1, [])
+ SUMMARY_video_x11="${SUMMARY_video_x11} xinput2_multitouch"
+ ])
+ AC_MSG_RESULT($have_xinput2_multitouch)
fi
AC_ARG_ENABLE(video-x11-xrandr,
AC_HELP_STRING([--enable-video-x11-xrandr], [enable X11 Xrandr extension for fullscreen [[default=yes]]]),
@@ -3265,6 +3265,41 @@ CheckUSBHID()
esac
}
+dnl Check for HIDAPI joystick drivers
+CheckHIDAPI()
+{
+ # The hidraw support doesn't catch Xbox, PS4 and Nintendo controllers,
+ # so we'll just use libusb when it's available.
+ #
+ # Except that libusb requires root permissions to open devices, so that's not generally useful, and we'll disable this by default.
+ AC_ARG_ENABLE(hidapi,
+AC_HELP_STRING([--enable-hidapi], [use HIDAPI for low level joystick drivers [[default=no]]]),
+ , enable_hidapi=no)
+ if test x$enable_joystick = xyes -a x$enable_hidapi = xyes; then
+ hidapi_support=no
+ AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+ if test x$PKG_CONFIG != xno; then
+ LIBUSB_CFLAGS=`$PKG_CONFIG --cflags libusb-1.0`
+ LIBUSB_LDFLAGS=`$PKG_CONFIG --libs libusb-1.0`
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$save_CFLAGS $LIBUSB_CFLAGS"
+ AC_CHECK_HEADER(libusb.h, have_libusb_h=yes)
+ CFLAGS="$save_CFLAGS"
+ fi
+ if test x$have_libusb_h = xyes; then
+ hidapi_support=yes
+ AC_DEFINE(SDL_JOYSTICK_HIDAPI, 1, [ ])
+ EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
+ SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
+ SOURCES="$SOURCES $srcdir/src/hidapi/libusb/hid.c"
+ EXTRA_CFLAGS="$EXTRA_CFLAGS $LIBUSB_CFLAGS"
+ EXTRA_LDFLAGS="$EXTRA_LDFLAGS $LIBUSB_LDFLAGS"
+ fi
+ AC_MSG_CHECKING(for hidapi support)
+ AC_MSG_RESULT($hidapi_support)
+ fi
+}
+
dnl Check for clock_gettime()
CheckClockGettime()
{
@@ -3386,6 +3421,7 @@ case "$host" in
esac
CheckTslib
CheckUSBHID
+ CheckHIDAPI
CheckPTHREAD
CheckClockGettime
CheckLinuxVersion
@@ -3811,7 +3847,11 @@ AC_HELP_STRING([--enable-render-d3d], [enable the Direct3D render driver [[defau
# Set up files for the joystick library
if test x$enable_joystick = xyes; then
AC_DEFINE(SDL_JOYSTICK_IOKIT, 1, [ ])
+ AC_DEFINE(SDL_JOYSTICK_HIDAPI, 1, [ ])
SOURCES="$SOURCES $srcdir/src/joystick/darwin/*.c"
+ SOURCES="$SOURCES $srcdir/src/joystick/hidapi/*.c"
+ SOURCES="$SOURCES $srcdir/src/hidapi/mac/hid.c"
+ EXTRA_CFLAGS="$EXTRA_CFLAGS -I$srcdir/src/hidapi/hidapi"
have_joystick=yes
fi
# Set up files for the haptic library
diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in
index abcf2f2f3..069539e48 100644
--- a/include/SDL_config.h.in
+++ b/include/SDL_config.h.in
@@ -279,6 +279,7 @@
#undef SDL_JOYSTICK_WINMM
#undef SDL_JOYSTICK_USBHID
#undef SDL_JOYSTICK_USBHID_MACHINE_JOYSTICK_H
+#undef SDL_JOYSTICK_HIDAPI
#undef SDL_JOYSTICK_EMSCRIPTEN
#undef SDL_HAPTIC_DUMMY
#undef SDL_HAPTIC_ANDROID
diff --git a/include/SDL_config_android.h b/include/SDL_config_android.h
index efe520312..2aa17deab 100644
--- a/include/SDL_config_android.h
+++ b/include/SDL_config_android.h
@@ -134,6 +134,7 @@
/* Enable various input drivers */
#define SDL_JOYSTICK_ANDROID 1
+#define SDL_JOYSTICK_HIDAPI 1
#define SDL_HAPTIC_ANDROID 1
/* Enable various shared object loading systems */
diff --git a/include/SDL_config_iphoneos.h b/include/SDL_config_iphoneos.h
index bc628ed30..080fd85bf 100644
--- a/include/SDL_config_iphoneos.h
+++ b/include/SDL_config_iphoneos.h
@@ -137,6 +137,7 @@
/* Enable MFi joystick support */
#define SDL_JOYSTICK_MFI 1
+#define SDL_JOYSTICK_HIDAPI 1
/* Enable Unix style SO loading */
#define SDL_LOADSO_DLOPEN 1
diff --git a/include/SDL_config_macosx.h b/include/SDL_config_macosx.h
index cb3d42f94..5e0f836dc 100644
--- a/include/SDL_config_macosx.h
+++ b/include/SDL_config_macosx.h
@@ -137,6 +137,7 @@
/* Enable various input drivers */
#define SDL_JOYSTICK_IOKIT 1
+#define SDL_JOYSTICK_HIDAPI 1
#define SDL_HAPTIC_IOKIT 1
/* Enable various shared object loading systems */
diff --git a/include/SDL_config_windows.h b/include/SDL_config_windows.h
index 8631a2fe6..2a208f884 100644
--- a/include/SDL_config_windows.h
+++ b/include/SDL_config_windows.h
@@ -190,6 +190,7 @@ typedef unsigned int uintptr_t;
/* Enable various input drivers */
#define SDL_JOYSTICK_DINPUT 1
#define SDL_JOYSTICK_XINPUT 1
+#define SDL_JOYSTICK_HIDAPI 1
#define SDL_HAPTIC_DINPUT 1
#define SDL_HAPTIC_XINPUT 1
diff --git a/include/SDL_gamecontroller.h b/include/SDL_gamecontroller.h
index f7e03cdac..fb8a7a525 100644
--- a/include/SDL_gamecontroller.h
+++ b/include/SDL_gamecontroller.h
@@ -353,6 +353,19 @@ SDL_GameControllerGetBindForButton(SDL_GameController *gamecontroller,
extern DECLSPEC Uint8 SDLCALL SDL_GameControllerGetButton(SDL_GameController *gamecontroller,
SDL_GameControllerButton button);
+/**
+ * Trigger a rumble effect
+ * Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling.
+ *
+ * \param gamecontroller The controller to vibrate
+ * \param low_frequency_rumble The intensity of the low frequency (left) rumble motor, from 0 to 0xFFFF
+ * \param high_frequency_rumble The intensity of the high frequency (right) rumble motor, from 0 to 0xFFFF
+ * \param duration_ms The duration of the rumble effect, in milliseconds
+ *
+ * \return 0, or -1 if rumble isn't supported on this joystick
+ */
+extern DECLSPEC int SDLCALL SDL_GameControllerRumble(SDL_GameController *gamecontroller, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
+
/**
* Close a controller previously opened with SDL_GameControllerOpen().
*/
diff --git a/include/SDL_haptic.h b/include/SDL_haptic.h
index e3a2bca5f..cfb91c531 100644
--- a/include/SDL_haptic.h
+++ b/include/SDL_haptic.h
@@ -656,8 +656,8 @@ typedef struct SDL_HapticRamp
* This struct is exclusively for the ::SDL_HAPTIC_LEFTRIGHT effect.
*
* The Left/Right effect is used to explicitly control the large and small
- * motors, commonly found in modern game controllers. One motor is high
- * frequency, the other is low frequency.
+ * motors, commonly found in modern game controllers. The small (right) motor
+ * is high frequency, and the large (left) motor is low frequency.
*
* \sa SDL_HAPTIC_LEFTRIGHT
* \sa SDL_HapticEffect
@@ -668,7 +668,7 @@ typedef struct SDL_HapticLeftRight
Uint16 type; /**< ::SDL_HAPTIC_LEFTRIGHT */
/* Replay */
- Uint32 length; /**< Duration of the effect. */
+ Uint32 length; /**< Duration of the effect in milliseconds. */
/* Rumble */
Uint16 large_magnitude; /**< Control of the large controller motor. */
diff --git a/include/SDL_hints.h b/include/SDL_hints.h
index cb8bed2c0..fcfc5152c 100644
--- a/include/SDL_hints.h
+++ b/include/SDL_hints.h
@@ -465,6 +465,84 @@ extern "C" {
*/
#define SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS"
+/**
+ * \brief A variable controlling whether the HIDAPI joystick drivers should be used.
+ *
+ * This variable can be set to the following values:
+ * "0" - HIDAPI drivers are not used
+ * "1" - HIDAPI drivers are used (the default)
+ *
+ * This variable is the default for all drivers, but can be overridden by the hints for specific drivers below.
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI "SDL_JOYSTICK_HIDAPI"
+
+/**
+ * \brief A variable controlling whether the HIDAPI driver for PS4 controllers should be used.
+ *
+ * This variable can be set to the following values:
+ * "0" - HIDAPI driver is not used
+ * "1" - HIDAPI driver is used
+ *
+ * The default is the value of SDL_HINT_JOYSTICK_HIDAPI
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_PS4 "SDL_JOYSTICK_HIDAPI_PS4"
+
+/**
+ * \brief A variable controlling whether the HIDAPI driver for Steam Controllers should be used.
+ *
+ * This variable can be set to the following values:
+ * "0" - HIDAPI driver is not used
+ * "1" - HIDAPI driver is used
+ *
+ * The default is the value of SDL_HINT_JOYSTICK_HIDAPI
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_STEAM "SDL_JOYSTICK_HIDAPI_STEAM"
+
+/**
+ * \brief A variable controlling whether the HIDAPI driver for Nintendo Switch controllers should be used.
+ *
+ * This variable can be set to the following values:
+ * "0" - HIDAPI driver is not used
+ * "1" - HIDAPI driver is used
+ *
+ * The default is the value of SDL_HINT_JOYSTICK_HIDAPI
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_SWITCH "SDL_JOYSTICK_HIDAPI_SWITCH"
+
+/**
+ * \brief A variable controlling whether the HIDAPI driver for XBox 360 controllers should be used.
+ *
+ * This variable can be set to the following values:
+ * "0" - HIDAPI driver is not used
+ * "1" - HIDAPI driver is used
+ *
+ * The default is the value of SDL_HINT_JOYSTICK_HIDAPI
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_XBOX360 "SDL_JOYSTICK_HIDAPI_XBOX360"
+
+/**
+ * \brief A variable controlling whether the HIDAPI driver for XBox One controllers should be used.
+ *
+ * This variable can be set to the following values:
+ * "0" - HIDAPI driver is not used
+ * "1" - HIDAPI driver is used
+ *
+ * The default is the value of SDL_HINT_JOYSTICK_HIDAPI
+ */
+#define SDL_HINT_JOYSTICK_HIDAPI_XBOXONE "SDL_JOYSTICK_HIDAPI_XBOXONE"
+
+/**
+ * \brief A variable that controls whether Steam Controllers should be exposed using the SDL joystick and game controller APIs
+ *
+ * The variable can be set to the following values:
+ * "0" - Do not scan for Steam Controllers
+ * "1" - Scan for Steam Controllers (the default)
+ *
+ * The default value is "1". This hint must be set before initializing the joystick subsystem.
+ */
+#define SDL_HINT_ENABLE_STEAM_CONTROLLERS "SDL_ENABLE_STEAM_CONTROLLERS"
+
+
/**
* \brief If set to "0" then never set the top most bit on a SDL Window, even if the video mode expects it.
* This is a debugging aid for developers and not expected to be used by end users. The default is "1"
diff --git a/include/SDL_joystick.h b/include/SDL_joystick.h
index f67772d71..8303c5e86 100644
--- a/include/SDL_joystick.h
+++ b/include/SDL_joystick.h
@@ -97,10 +97,10 @@ typedef enum
typedef enum
{
SDL_JOYSTICK_POWER_UNKNOWN = -1,
- SDL_JOYSTICK_POWER_EMPTY,
- SDL_JOYSTICK_POWER_LOW,
- SDL_JOYSTICK_POWER_MEDIUM,
- SDL_JOYSTICK_POWER_FULL,
+ SDL_JOYSTICK_POWER_EMPTY, /* <= 5% */
+ SDL_JOYSTICK_POWER_LOW, /* <= 20% */
+ SDL_JOYSTICK_POWER_MEDIUM, /* <= 70% */
+ SDL_JOYSTICK_POWER_FULL, /* <= 100% */
SDL_JOYSTICK_POWER_WIRED,
SDL_JOYSTICK_POWER_MAX
} SDL_JoystickPowerLevel;
@@ -361,6 +361,19 @@ extern DECLSPEC int SDLCALL SDL_JoystickGetBall(SDL_Joystick * joystick,
extern DECLSPEC Uint8 SDLCALL SDL_JoystickGetButton(SDL_Joystick * joystick,
int button);
+/**
+ * Trigger a rumble effect
+ * Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling.
+ *
+ * \param joystick The joystick to vibrate
+ * \param low_frequency_rumble The intensity of the low frequency (left) rumble motor, from 0 to 0xFFFF
+ * \param high_frequency_rumble The intensity of the high frequency (right) rumble motor, from 0 to 0xFFFF
+ * \param duration_ms The duration of the rumble effect, in milliseconds
+ *
+ * \return 0, or -1 if rumble isn't supported on this joystick
+ */
+extern DECLSPEC int SDLCALL SDL_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
+
/**
* Close a joystick previously opened with SDL_JoystickOpen().
*/
diff --git a/include/SDL_stdinc.h b/include/SDL_stdinc.h
index b2ca7ae97..e373bc380 100644
--- a/include/SDL_stdinc.h
+++ b/include/SDL_stdinc.h
@@ -450,6 +450,7 @@ extern DECLSPEC void *SDLCALL SDL_memcpy(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_
extern DECLSPEC void *SDLCALL SDL_memmove(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len);
extern DECLSPEC int SDLCALL SDL_memcmp(const void *s1, const void *s2, size_t len);
+extern DECLSPEC wchar_t *SDLCALL SDL_wcsdup(const wchar_t *wstr);
extern DECLSPEC size_t SDLCALL SDL_wcslen(const wchar_t *wstr);
extern DECLSPEC size_t SDLCALL SDL_wcslcpy(SDL_OUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen);
extern DECLSPEC size_t SDLCALL SDL_wcslcat(SDL_INOUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen);
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 50a39be75..100b1d063 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -677,3 +677,6 @@
#define SDL_AndroidBackButton SDL_AndroidBackButton_REAL
#define SDL_exp SDL_exp_REAL
#define SDL_expf SDL_expf_REAL
+#define SDL_wcsdup SDL_wcsdup_REAL
+#define SDL_GameControllerRumble SDL_GameControllerRumble_REAL
+#define SDL_JoystickRumble SDL_JoystickRumble_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 97cdab1bd..c3983f89d 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -715,9 +715,10 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_HasAVX512F,(void),(),return)
#ifdef __ANDROID__
SDL_DYNAPI_PROC(SDL_bool,SDL_IsChromebook,(void),(),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_IsDeXMode,(void),(),return)
-#endif
-#ifdef __ANDROID__
SDL_DYNAPI_PROC(void,SDL_AndroidBackButton,(void),(),return)
#endif
SDL_DYNAPI_PROC(double,SDL_exp,(double a),(a),return)
SDL_DYNAPI_PROC(float,SDL_expf,(float a),(a),return)
+SDL_DYNAPI_PROC(wchar_t*,SDL_wcsdup,(const wchar_t *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_GameControllerRumble,(SDL_GameController *a, Uint16 b, Uint16 c, Uint32 d),(a,b,c,d),return)
+SDL_DYNAPI_PROC(int,SDL_JoystickRumble,(SDL_Joystick *a, Uint16 b, Uint16 c, Uint32 d),(a,b,c,d),return)
diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c
index 6af3220c1..6c85bb544 100644
--- a/src/joystick/SDL_gamecontroller.c
+++ b/src/joystick/SDL_gamecontroller.c
@@ -102,6 +102,7 @@ typedef struct _ControllerMapping_t
static SDL_JoystickGUID s_zeroGUID;
static ControllerMapping_t *s_pSupportedControllers = NULL;
static ControllerMapping_t *s_pDefaultMapping = NULL;
+static ControllerMapping_t *s_pHIDAPIMapping = NULL;
static ControllerMapping_t *s_pXInputMapping = NULL;
/* The SDL game controller structure */
@@ -430,6 +431,10 @@ static ControllerMapping_t *SDL_PrivateGetControllerMappingForGUID(SDL_JoystickG
}
pSupportedController = pSupportedController->next;
}
+ if (guid->data[14] == 'h') {
+ /* This is a HIDAPI device */
+ return s_pHIDAPIMapping;
+ }
#if SDL_JOYSTICK_XINPUT
if (guid->data[14] == 'x') {
/* This is an XInput device */
@@ -1130,6 +1135,7 @@ SDL_PrivateGameControllerAddMapping(const char *mappingString, SDL_ControllerMap
char *pchGUID;
SDL_JoystickGUID jGUID;
SDL_bool is_default_mapping = SDL_FALSE;
+ SDL_bool is_hidapi_mapping = SDL_FALSE;
SDL_bool is_xinput_mapping = SDL_FALSE;
SDL_bool existing = SDL_FALSE;
ControllerMapping_t *pControllerMapping;
@@ -1144,6 +1150,8 @@ SDL_PrivateGameControllerAddMapping(const char *mappingString, SDL_ControllerMap
}
if (!SDL_strcasecmp(pchGUID, "default")) {
is_default_mapping = SDL_TRUE;
+ } else if (!SDL_strcasecmp(pchGUID, "hidapi")) {
+ is_hidapi_mapping = SDL_TRUE;
} else if (!SDL_strcasecmp(pchGUID, "xinput")) {
is_xinput_mapping = SDL_TRUE;
}
@@ -1160,6 +1168,8 @@ SDL_PrivateGameControllerAddMapping(const char *mappingString, SDL_ControllerMap
} else {
if (is_default_mapping) {
s_pDefaultMapping = pControllerMapping;
+ } else if (is_hidapi_mapping) {
+ s_pHIDAPIMapping = pControllerMapping;
} else if (is_xinput_mapping) {
s_pXInputMapping = pControllerMapping;
}
@@ -1458,7 +1468,6 @@ SDL_bool SDL_ShouldIgnoreGameController(const char *name, SDL_JoystickGUID guid)
}
SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL);
- vidpid = MAKE_VIDPID(vendor, product);
if (SDL_GetHintBoolean("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD", SDL_FALSE)) {
/* 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 */
@@ -1476,6 +1485,8 @@ SDL_bool SDL_ShouldIgnoreGameController(const char *name, SDL_JoystickGUID guid)
}
}
+ vidpid = MAKE_VIDPID(vendor, product);
+
if (SDL_allowed_controllers.num_entries > 0) {
for (i = 0; i < SDL_allowed_controllers.num_entries; ++i) {
if (vidpid == SDL_allowed_controllers.entries[i]) {
@@ -1503,22 +1514,18 @@ SDL_bool SDL_ShouldIgnoreGameController(const char *name, SDL_JoystickGUID guid)
SDL_GameController *
SDL_GameControllerOpen(int device_index)
{
+ SDL_JoystickID instance_id;
SDL_GameController *gamecontroller;
SDL_GameController *gamecontrollerlist;
ControllerMapping_t *pSupportedController = NULL;
SDL_LockJoysticks();
- if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) {
- SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
- SDL_UnlockJoysticks();
- return (NULL);
- }
-
gamecontrollerlist = SDL_gamecontrollers;
/* If the controller is already open, return it */
+ instance_id = SDL_JoystickGetDeviceInstanceID(device_index);
while (gamecontrollerlist) {
- if (SDL_SYS_GetInstanceIdOfDeviceIndex(device_index) == gamecontrollerlist->joystick->instance_id) {
+ if (instance_id == gamecontrollerlist->joystick->instance_id) {
gamecontroller = gamecontrollerlist;
++gamecontroller->ref_count;
SDL_UnlockJoysticks();
@@ -1834,6 +1841,12 @@ SDL_GameControllerButtonBind SDL_GameControllerGetBindForButton(SDL_GameControll
}
+int
+SDL_GameControllerRumble(SDL_GameController *gamecontroller, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ return SDL_JoystickRumble(SDL_GameControllerGetJoystick(gamecontroller), low_frequency_rumble, high_frequency_rumble, duration_ms);
+}
+
void
SDL_GameControllerClose(SDL_GameController * gamecontroller)
{
diff --git a/src/joystick/SDL_gamecontrollerdb.h b/src/joystick/SDL_gamecontrollerdb.h
index 0302e085a..a33a8c983 100644
--- a/src/joystick/SDL_gamecontrollerdb.h
+++ b/src/joystick/SDL_gamecontrollerdb.h
@@ -555,6 +555,7 @@ static const char *s_ControllerMappings [] =
"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,",
"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,",
"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,",
+ "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 */
"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,",
"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,",
"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,",
@@ -575,6 +576,7 @@ static const char *s_ControllerMappings [] =
#if defined(SDL_JOYSTICK_EMSCRIPTEN)
"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,",
#endif
+ "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,",
NULL
};
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index ebbd2bf9c..f806387dd 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -23,6 +23,7 @@
/* This is the joystick API for Simple DirectMedia Layer */
#include "SDL.h"
+#include "SDL_atomic.h"
#include "SDL_events.h"
#include "SDL_sysjoystick.h"
#include "SDL_assert.h"
@@ -33,11 +34,45 @@
#endif
#include "../video/SDL_sysvideo.h"
+/* This is included in only one place because it has a large static list of controllers */
+#include "controller_type.h"
+#ifdef __WIN32__
+/* Needed for checking for input remapping programs */
+#include "../../core/windows/SDL_windows.h"
+
+#undef UNICODE /* We want ASCII functions */
+#include
+#endif
+
+static SDL_JoystickDriver *SDL_joystick_drivers[] = {
+#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
+ &SDL_WINDOWS_JoystickDriver,
+#endif
+#ifdef SDL_JOYSTICK_LINUX
+ &SDL_LINUX_JoystickDriver,
+#endif
+#ifdef SDL_JOYSTICK_IOKIT
+ &SDL_DARWIN_JoystickDriver,
+#endif
+#if defined(__IPHONEOS__) || defined(__TVOS__)
+ &SDL_IOS_JoystickDriver,
+#endif
+#ifdef SDL_JOYSTICK_ANDROID
+ &SDL_ANDROID_JoystickDriver,
+#endif
+#ifdef SDL_JOYSTICK_HIDAPI
+ &SDL_HIDAPI_JoystickDriver,
+#endif
+#if defined(SDL_JOYSTICK_DUMMY) || defined(SDL_JOYSTICK_DISABLED)
+ &SDL_DUMMY_JoystickDriver
+#endif
+};
static SDL_bool SDL_joystick_allows_background_events = SDL_FALSE;
static SDL_Joystick *SDL_joysticks = NULL;
static SDL_bool SDL_updating_joystick = SDL_FALSE;
static SDL_mutex *SDL_joystick_lock = NULL; /* This needs to support recursive locks */
+static SDL_atomic_t SDL_next_joystick_instance_id;
void
SDL_LockJoysticks(void)
@@ -69,7 +104,7 @@ SDL_JoystickAllowBackgroundEventsChanged(void *userdata, const char *name, const
int
SDL_JoystickInit(void)
{
- int status;
+ int i, status;
SDL_GameControllerInitMappings();
@@ -88,11 +123,13 @@ SDL_JoystickInit(void)
}
#endif /* !SDL_EVENTS_DISABLED */
- status = SDL_SYS_JoystickInit();
- if (status >= 0) {
- status = 0;
+ status = -1;
+ for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
+ if (SDL_joystick_drivers[i]->Init() >= 0) {
+ status = 0;
+ }
}
- return (status);
+ return status;
}
/*
@@ -101,7 +138,48 @@ SDL_JoystickInit(void)
int
SDL_NumJoysticks(void)
{
- return SDL_SYS_NumJoysticks();
+ int i, total_joysticks = 0;
+ SDL_LockJoysticks();
+ for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
+ total_joysticks += SDL_joystick_drivers[i]->GetCount();
+ }
+ SDL_UnlockJoysticks();
+ return total_joysticks;
+}
+
+/*
+ * Return the next available joystick instance ID
+ * This may be called by drivers from multiple threads, unprotected by any locks
+ */
+SDL_JoystickID SDL_GetNextJoystickInstanceID()
+{
+ return SDL_AtomicIncRef(&SDL_next_joystick_instance_id);
+}
+
+/*
+ * Get the driver and device index for an API device index
+ * This should be called while the joystick lock is held, to prevent another thread from updating the list
+ */
+SDL_bool
+SDL_GetDriverAndJoystickIndex(int device_index, SDL_JoystickDriver **driver, int *driver_index)
+{
+ int i, num_joysticks, total_joysticks = 0;
+
+ if (device_index >= 0) {
+ for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
+ num_joysticks = SDL_joystick_drivers[i]->GetCount();
+ if (device_index < num_joysticks) {
+ *driver = SDL_joystick_drivers[i];
+ *driver_index = device_index;
+ return SDL_TRUE;
+ }
+ device_index -= num_joysticks;
+ total_joysticks += num_joysticks;
+ }
+ }
+
+ SDL_SetError("There are %d joysticks available", total_joysticks);
+ return SDL_FALSE;
}
/*
@@ -127,11 +205,17 @@ SDL_FixupJoystickName(const char *name)
const char *
SDL_JoystickNameForIndex(int device_index)
{
- if (device_index < 0 || device_index >= SDL_NumJoysticks()) {
- SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
- return (NULL);
+ SDL_JoystickDriver *driver;
+ const char *name = NULL;
+
+ SDL_LockJoysticks();
+ if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
+ name = SDL_FixupJoystickName(driver->GetDeviceName(device_index));
}
- return SDL_FixupJoystickName(SDL_SYS_JoystickNameForDeviceIndex(device_index));
+ SDL_UnlockJoysticks();
+
+ /* FIXME: Really we should reference count this name so it doesn't go away after unlock */
+ return name;
}
/*
@@ -176,27 +260,30 @@ SDL_JoystickAxesCenteredAtZero(SDL_Joystick *joystick)
SDL_Joystick *
SDL_JoystickOpen(int device_index)
{
+ SDL_JoystickDriver *driver;
+ SDL_JoystickID instance_id;
SDL_Joystick *joystick;
SDL_Joystick *joysticklist;
const char *joystickname = NULL;
- if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) {
- SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
- return (NULL);
- }
-
SDL_LockJoysticks();
+ if (!SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
+ SDL_UnlockJoysticks();
+ return NULL;
+ }
+
joysticklist = SDL_joysticks;
/* If the joystick is already open, return it
* it is important that we have a single joystick * for each instance id
*/
+ instance_id = driver->GetDeviceInstanceID(device_index);
while (joysticklist) {
- if (SDL_JoystickGetDeviceInstanceID(device_index) == joysticklist->instance_id) {
+ if (instance_id == joysticklist->instance_id) {
joystick = joysticklist;
++joystick->ref_count;
SDL_UnlockJoysticks();
- return (joystick);
+ return joystick;
}
joysticklist = joysticklist->next;
}
@@ -208,18 +295,23 @@ SDL_JoystickOpen(int device_index)
SDL_UnlockJoysticks();
return NULL;
}
+ joystick->driver = driver;
+ joystick->instance_id = instance_id;
- if (SDL_SYS_JoystickOpen(joystick, device_index) < 0) {
+ if (driver->Open(joystick, device_index) < 0) {
SDL_free(joystick);
SDL_UnlockJoysticks();
return NULL;
}
- joystickname = SDL_SYS_JoystickNameForDeviceIndex(device_index);
- if (joystickname)
+ joystickname = driver->GetDeviceName(device_index);
+ if (joystickname) {
joystick->name = SDL_strdup(joystickname);
- else
+ } else {
joystick->name = NULL;
+ }
+
+ joystick->guid = driver->GetDeviceGUID(device_index);
if (joystick->naxes > 0) {
joystick->axes = (SDL_JoystickAxisInfo *) SDL_calloc(joystick->naxes, sizeof(SDL_JoystickAxisInfo));
@@ -263,9 +355,9 @@ SDL_JoystickOpen(int device_index)
SDL_UnlockJoysticks();
- SDL_SYS_JoystickUpdate(joystick);
+ driver->Update(joystick);
- return (joystick);
+ return joystick;
}
@@ -294,9 +386,9 @@ int
SDL_JoystickNumAxes(SDL_Joystick * joystick)
{
if (!SDL_PrivateJoystickValid(joystick)) {
- return (-1);
+ return -1;
}
- return (joystick->naxes);
+ return joystick->naxes;
}
/*
@@ -306,9 +398,9 @@ int
SDL_JoystickNumHats(SDL_Joystick * joystick)
{
if (!SDL_PrivateJoystickValid(joystick)) {
- return (-1);
+ return -1;
}
- return (joystick->nhats);
+ return joystick->nhats;
}
/*
@@ -318,9 +410,9 @@ int
SDL_JoystickNumBalls(SDL_Joystick * joystick)
{
if (!SDL_PrivateJoystickValid(joystick)) {
- return (-1);
+ return -1;
}
- return (joystick->nballs);
+ return joystick->nballs;
}
/*
@@ -330,9 +422,9 @@ int
SDL_JoystickNumButtons(SDL_Joystick * joystick)
{
if (!SDL_PrivateJoystickValid(joystick)) {
- return (-1);
+ return -1;
}
- return (joystick->nbuttons);
+ return joystick->nbuttons;
}
/*
@@ -344,7 +436,7 @@ SDL_JoystickGetAxis(SDL_Joystick * joystick, int axis)
Sint16 state;
if (!SDL_PrivateJoystickValid(joystick)) {
- return (0);
+ return 0;
}
if (axis < joystick->naxes) {
state = joystick->axes[axis].value;
@@ -352,7 +444,7 @@ SDL_JoystickGetAxis(SDL_Joystick * joystick, int axis)
SDL_SetError("Joystick only has %d axes", joystick->naxes);
state = 0;
}
- return (state);
+ return state;
}
/*
@@ -383,7 +475,7 @@ SDL_JoystickGetHat(SDL_Joystick * joystick, int hat)
Uint8 state;
if (!SDL_PrivateJoystickValid(joystick)) {
- return (0);
+ return 0;
}
if (hat < joystick->nhats) {
state = joystick->hats[hat];
@@ -391,7 +483,7 @@ SDL_JoystickGetHat(SDL_Joystick * joystick, int hat)
SDL_SetError("Joystick only has %d hats", joystick->nhats);
state = 0;
}
- return (state);
+ return state;
}
/*
@@ -403,7 +495,7 @@ SDL_JoystickGetBall(SDL_Joystick * joystick, int ball, int *dx, int *dy)
int retval;
if (!SDL_PrivateJoystickValid(joystick)) {
- return (-1);
+ return -1;
}
retval = 0;
@@ -419,7 +511,7 @@ SDL_JoystickGetBall(SDL_Joystick * joystick, int ball, int *dx, int *dy)
} else {
return SDL_SetError("Joystick only has %d balls", joystick->nballs);
}
- return (retval);
+ return retval;
}
/*
@@ -431,7 +523,7 @@ SDL_JoystickGetButton(SDL_Joystick * joystick, int button)
Uint8 state;
if (!SDL_PrivateJoystickValid(joystick)) {
- return (0);
+ return 0;
}
if (button < joystick->nbuttons) {
state = joystick->buttons[button];
@@ -439,7 +531,7 @@ SDL_JoystickGetButton(SDL_Joystick * joystick, int button)
SDL_SetError("Joystick only has %d buttons", joystick->nbuttons);
state = 0;
}
- return (state);
+ return state;
}
/*
@@ -453,7 +545,7 @@ SDL_JoystickGetAttached(SDL_Joystick * joystick)
return SDL_FALSE;
}
- return SDL_SYS_JoystickAttached(joystick);
+ return joystick->driver->IsAttached(joystick);
}
/*
@@ -463,10 +555,10 @@ SDL_JoystickID
SDL_JoystickInstanceID(SDL_Joystick * joystick)
{
if (!SDL_PrivateJoystickValid(joystick)) {
- return (-1);
+ return -1;
}
- return (joystick->instance_id);
+ return joystick->instance_id;
}
/*
@@ -495,12 +587,21 @@ const char *
SDL_JoystickName(SDL_Joystick * joystick)
{
if (!SDL_PrivateJoystickValid(joystick)) {
- return (NULL);
+ return NULL;
}
return SDL_FixupJoystickName(joystick->name);
}
+int
+SDL_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ if (!SDL_PrivateJoystickValid(joystick)) {
+ return -1;
+ }
+ return joystick->driver->Rumble(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms);
+}
+
/*
* Close a joystick previously opened with SDL_JoystickOpen()
*/
@@ -510,7 +611,7 @@ SDL_JoystickClose(SDL_Joystick * joystick)
SDL_Joystick *joysticklist;
SDL_Joystick *joysticklistprev;
- if (!joystick) {
+ if (!SDL_PrivateJoystickValid(joystick)) {
return;
}
@@ -527,7 +628,7 @@ SDL_JoystickClose(SDL_Joystick * joystick)
return;
}
- SDL_SYS_JoystickClose(joystick);
+ joystick->driver->Close(joystick);
joystick->hwdata = NULL;
joysticklist = SDL_joysticks;
@@ -561,6 +662,8 @@ SDL_JoystickClose(SDL_Joystick * joystick)
void
SDL_JoystickQuit(void)
{
+ int i;
+
/* Make sure we're not getting called in the middle of updating joysticks */
SDL_assert(!SDL_updating_joystick);
@@ -573,7 +676,9 @@ SDL_JoystickQuit(void)
}
/* Quit the joystick setup */
- SDL_SYS_JoystickQuit();
+ for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
+ SDL_joystick_drivers[i]->Quit();
+ }
SDL_UnlockJoysticks();
@@ -609,10 +714,16 @@ SDL_PrivateJoystickShouldIgnoreEvent()
/* These are global for SDL_sysjoystick.c and SDL_events.c */
-void SDL_PrivateJoystickAdded(int device_index)
+void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
{
#if !SDL_EVENTS_DISABLED
SDL_Event event;
+ int device_index;
+
+ device_index = SDL_JoystickGetDeviceIndexFromInstanceID(device_instance);
+ if (device_index < 0) {
+ return;
+ }
event.type = SDL_JOYDEVICEADDED;
@@ -722,7 +833,7 @@ SDL_PrivateJoystickAxis(SDL_Joystick * joystick, Uint8 axis, Sint16 value)
posted = SDL_PushEvent(&event) == 1;
}
#endif /* !SDL_EVENTS_DISABLED */
- return (posted);
+ return posted;
}
int
@@ -762,7 +873,7 @@ SDL_PrivateJoystickHat(SDL_Joystick * joystick, Uint8 hat, Uint8 value)
posted = SDL_PushEvent(&event) == 1;
}
#endif /* !SDL_EVENTS_DISABLED */
- return (posted);
+ return posted;
}
int
@@ -798,7 +909,7 @@ SDL_PrivateJoystickBall(SDL_Joystick * joystick, Uint8 ball,
posted = SDL_PushEvent(&event) == 1;
}
#endif /* !SDL_EVENTS_DISABLED */
- return (posted);
+ return posted;
}
int
@@ -817,7 +928,7 @@ SDL_PrivateJoystickButton(SDL_Joystick * joystick, Uint8 button, Uint8 state)
break;
default:
/* Invalid state -- bail */
- return (0);
+ return 0;
}
#endif /* !SDL_EVENTS_DISABLED */
@@ -850,12 +961,13 @@ SDL_PrivateJoystickButton(SDL_Joystick * joystick, Uint8 button, Uint8 state)
posted = SDL_PushEvent(&event) == 1;
}
#endif /* !SDL_EVENTS_DISABLED */
- return (posted);
+ return posted;
}
void
SDL_JoystickUpdate(void)
{
+ int i;
SDL_Joystick *joystick;
SDL_LockJoysticks();
@@ -872,15 +984,13 @@ SDL_JoystickUpdate(void)
SDL_UnlockJoysticks();
for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
- SDL_SYS_JoystickUpdate(joystick);
+ joystick->driver->Update(joystick);
if (joystick->delayed_guide_button) {
SDL_GameControllerHandleDelayedGuideButton(joystick);
}
if (joystick->force_recentering) {
- int i;
-
/* Tell the app that everything is centered/unpressed... */
for (i = 0; i < joystick->naxes; i++) {
if (joystick->axes[i].has_initial_value) {
@@ -914,7 +1024,9 @@ SDL_JoystickUpdate(void)
/* this needs to happen AFTER walking the joystick list above, so that any
dangling hardware data from removed devices can be free'd
*/
- SDL_SYS_JoystickDetect();
+ for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
+ SDL_joystick_drivers[i]->Detect();
+ }
SDL_UnlockJoysticks();
}
@@ -947,7 +1059,7 @@ SDL_JoystickEventState(int state)
}
break;
}
- return (state);
+ return state;
#endif /* SDL_EVENTS_DISABLED */
}
@@ -963,7 +1075,7 @@ void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *prod
/* guid16[4] is product ID */
guid16[5] == 0x0000
/* guid16[6] is product version */
- ) {
+ ) {
if (vendor) {
*vendor = guid16[2];
}
@@ -986,6 +1098,43 @@ void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *prod
}
}
+SDL_bool
+SDL_IsJoystickPS4(Uint16 vendor, Uint16 product)
+{
+ return (GuessControllerType(vendor, product) == k_eControllerType_PS4Controller);
+}
+
+SDL_bool
+SDL_IsJoystickNintendoSwitchPro(Uint16 vendor, Uint16 product)
+{
+ return (GuessControllerType(vendor, product) == k_eControllerType_SwitchProController);
+}
+
+SDL_bool
+SDL_IsJoystickSteamController(Uint16 vendor, Uint16 product)
+{
+ return BIsSteamController(GuessControllerType(vendor, product)) ? SDL_TRUE : SDL_FALSE;
+}
+
+SDL_bool
+SDL_IsJoystickXbox360(Uint16 vendor, Uint16 product)
+{
+ /* Filter out some bogus values here */
+ if (vendor == 0x0000 && product == 0x0000) {
+ return SDL_FALSE;
+ }
+ if (vendor == 0x0001 && product == 0x0001) {
+ return SDL_FALSE;
+ }
+ return (GuessControllerType(vendor, product) == k_eControllerType_XBox360Controller);
+}
+
+SDL_bool
+SDL_IsJoystickXboxOne(Uint16 vendor, Uint16 product)
+{
+ return (GuessControllerType(vendor, product) == k_eControllerType_XBoxOneController);
+}
+
static SDL_bool SDL_IsJoystickProductWheel(Uint32 vidpid)
{
static Uint32 wheel_joysticks[] = {
@@ -1092,19 +1241,80 @@ static SDL_JoystickType SDL_GetJoystickGUIDType(SDL_JoystickGUID guid)
return SDL_JOYSTICK_TYPE_THROTTLE;
}
+ if (GuessControllerType(vendor, product) != k_eControllerType_UnknownNonSteamController) {
+ return SDL_JOYSTICK_TYPE_GAMECONTROLLER;
+ }
+
return SDL_JOYSTICK_TYPE_UNKNOWN;
}
+static SDL_bool SDL_IsPS4RemapperRunning(void)
+{
+#ifdef __WIN32__
+ const char *mapper_processes[] = {
+ "DS4Windows.exe",
+ "InputMapper.exe",
+ };
+ int i;
+ PROCESSENTRY32 pe32;
+ SDL_bool found = SDL_FALSE;
+
+ /* Take a snapshot of all processes in the system */
+ HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (hProcessSnap != INVALID_HANDLE_VALUE) {
+ pe32.dwSize = sizeof(PROCESSENTRY32);
+ if (Process32First(hProcessSnap, &pe32)) {
+ do
+ {
+ for (i = 0; i < SDL_arraysize(mapper_processes); ++i) {
+ if (SDL_strcasecmp(pe32.szExeFile, mapper_processes[i]) == 0) {
+ found = SDL_TRUE;
+ }
+ }
+ } while (Process32Next(hProcessSnap, &pe32) && !found);
+ }
+ CloseHandle(hProcessSnap);
+ }
+ return found;
+#else
+ return SDL_FALSE;
+#endif
+}
+
+SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid)
+{
+ Uint16 vendor;
+ Uint16 product;
+
+ SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL);
+
+ if (SDL_IsJoystickPS4(vendor, product) && SDL_IsPS4RemapperRunning()) {
+ return SDL_TRUE;
+ }
+
+ if (SDL_IsGameControllerNameAndGUID(name, guid) &&
+ SDL_ShouldIgnoreGameController(name, guid)) {
+ return SDL_TRUE;
+ }
+
+ return SDL_FALSE;
+}
+
/* return the guid for this index */
SDL_JoystickGUID SDL_JoystickGetDeviceGUID(int device_index)
{
- if (device_index < 0 || device_index >= SDL_NumJoysticks()) {
- SDL_JoystickGUID emptyGUID;
- SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
- SDL_zero(emptyGUID);
- return emptyGUID;
+ SDL_JoystickDriver *driver;
+ SDL_JoystickGUID guid;
+
+ SDL_LockJoysticks();
+ if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
+ guid = driver->GetDeviceGUID(device_index);
+ } else {
+ SDL_zero(guid);
}
- return SDL_SYS_JoystickGetDeviceGUID(device_index);
+ SDL_UnlockJoysticks();
+
+ return guid;
}
Uint16 SDL_JoystickGetDeviceVendor(int device_index)
@@ -1150,11 +1360,33 @@ SDL_JoystickType SDL_JoystickGetDeviceType(int device_index)
SDL_JoystickID SDL_JoystickGetDeviceInstanceID(int device_index)
{
- if (device_index < 0 || device_index >= SDL_NumJoysticks()) {
- SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
- return -1;
+ SDL_JoystickDriver *driver;
+ SDL_JoystickID instance_id = -1;
+
+ SDL_LockJoysticks();
+ if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
+ instance_id = driver->GetDeviceInstanceID(device_index);
}
- return SDL_SYS_GetInstanceIdOfDeviceIndex(device_index);
+ SDL_UnlockJoysticks();
+
+ return instance_id;
+}
+
+int SDL_JoystickGetDeviceIndexFromInstanceID(SDL_JoystickID instance_id)
+{
+ int i, num_joysticks, device_index = -1;
+
+ SDL_LockJoysticks();
+ num_joysticks = SDL_NumJoysticks();
+ for (i = 0; i < num_joysticks; ++i) {
+ if (SDL_JoystickGetDeviceInstanceID(i) == instance_id) {
+ device_index = i;
+ break;
+ }
+ }
+ SDL_UnlockJoysticks();
+
+ return device_index;
}
SDL_JoystickGUID SDL_JoystickGetGUID(SDL_Joystick * joystick)
@@ -1164,7 +1396,7 @@ SDL_JoystickGUID SDL_JoystickGetGUID(SDL_Joystick * joystick)
SDL_zero(emptyGUID);
return emptyGUID;
}
- return SDL_SYS_JoystickGetGUID(joystick);
+ return joystick->guid;
}
Uint16 SDL_JoystickGetVendor(SDL_Joystick * joystick)
@@ -1229,7 +1461,6 @@ void SDL_JoystickGetGUIDString(SDL_JoystickGUID guid, char *pszGUID, int cbGUID)
*pszGUID = '\0';
}
-
/*-----------------------------------------------------------------------------
* Purpose: Returns the 4 bit nibble for a hex character
* Input : c -
@@ -1254,7 +1485,6 @@ static unsigned char nibble(char c)
return 0;
}
-
/* convert the string version of a joystick guid to the struct */
SDL_JoystickGUID SDL_JoystickGetGUIDFromString(const char *pchGUID)
{
@@ -1277,19 +1507,17 @@ SDL_JoystickGUID SDL_JoystickGetGUIDFromString(const char *pchGUID)
return guid;
}
-
/* update the power level for this joystick */
void SDL_PrivateJoystickBatteryLevel(SDL_Joystick * joystick, SDL_JoystickPowerLevel ePowerLevel)
{
joystick->epowerlevel = ePowerLevel;
}
-
/* return its power level */
SDL_JoystickPowerLevel SDL_JoystickCurrentPowerLevel(SDL_Joystick * joystick)
{
if (!SDL_PrivateJoystickValid(joystick)) {
- return (SDL_JOYSTICK_POWER_UNKNOWN);
+ return SDL_JOYSTICK_POWER_UNKNOWN;
}
return joystick->epowerlevel;
}
diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h
index 2ce622eae..b45761714 100644
--- a/src/joystick/SDL_joystick_c.h
+++ b/src/joystick/SDL_joystick_c.h
@@ -23,19 +23,48 @@
/* Useful functions and variables from SDL_joystick.c */
#include "SDL_joystick.h"
+struct _SDL_JoystickDriver;
+
/* Initialization and shutdown functions */
extern int SDL_JoystickInit(void);
extern void SDL_JoystickQuit(void);
+/* Function to get the next available joystick instance ID */
+extern SDL_JoystickID SDL_GetNextJoystickInstanceID(void);
+
/* Initialization and shutdown functions */
extern int SDL_GameControllerInitMappings(void);
extern void SDL_GameControllerQuitMappings(void);
extern int SDL_GameControllerInit(void);
extern void SDL_GameControllerQuit(void);
+/* Function to get the joystick driver and device index for an API device index */
+extern SDL_bool SDL_GetDriverAndJoystickIndex(int device_index, struct _SDL_JoystickDriver **driver, int *driver_index);
+
+/* Function to return the device index for a joystick ID, or -1 if not found */
+extern int SDL_JoystickGetDeviceIndexFromInstanceID(SDL_JoystickID instance_id);
+
/* Function to extract information from an SDL joystick GUID */
extern void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version);
+/* Function to return whether a joystick is a PS4 controller */
+extern SDL_bool SDL_IsJoystickPS4(Uint16 vendor_id, Uint16 product_id);
+
+/* Function to return whether a joystick is a Nintendo Switch Pro controller */
+extern SDL_bool SDL_IsJoystickNintendoSwitchPro(Uint16 vendor_id, Uint16 product_id);
+
+/* Function to return whether a joystick is a Steam Controller */
+extern SDL_bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id);
+
+/* Function to return whether a joystick is an Xbox 360 controller */
+extern SDL_bool SDL_IsJoystickXbox360(Uint16 vendor_id, Uint16 product_id);
+
+/* Function to return whether a joystick is an Xbox One controller */
+extern SDL_bool SDL_IsJoystickXboxOne(Uint16 vendor_id, Uint16 product_id);
+
+/* Function to return whether a joystick should be ignored */
+extern SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid);
+
/* Function to return whether a joystick name and GUID is a game controller */
extern SDL_bool SDL_IsGameControllerNameAndGUID(const char *name, SDL_JoystickGUID guid);
@@ -46,7 +75,7 @@ extern SDL_bool SDL_ShouldIgnoreGameController(const char *name, SDL_JoystickGUI
extern void SDL_GameControllerHandleDelayedGuideButton(SDL_Joystick *joystick);
/* Internal event queueing functions */
-extern void SDL_PrivateJoystickAdded(int device_index);
+extern void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance);
extern void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance);
extern int SDL_PrivateJoystickAxis(SDL_Joystick * joystick,
Uint8 axis, Sint16 value);
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
index efcc2d7b2..99ed52b4e 100644
--- a/src/joystick/SDL_sysjoystick.h
+++ b/src/joystick/SDL_sysjoystick.h
@@ -42,6 +42,7 @@ struct _SDL_Joystick
{
SDL_JoystickID instance_id; /* Device instance, monotonically increasing from 0 */
char *name; /* Joystick name - system dependent */
+ SDL_JoystickGUID guid; /* Joystick guid */
int naxes; /* Number of axis controls on the joystick */
SDL_JoystickAxisInfo *axes;
@@ -58,69 +59,95 @@ struct _SDL_Joystick
int nbuttons; /* Number of buttons on the joystick */
Uint8 *buttons; /* Current button states */
- struct joystick_hwdata *hwdata; /* Driver dependent information */
-
- int ref_count; /* Reference count for multiple opens */
-
SDL_bool is_game_controller;
SDL_bool delayed_guide_button; /* SDL_TRUE if this device has the guide button event delayed */
SDL_bool force_recentering; /* SDL_TRUE if this device needs to have its state reset to 0 */
SDL_JoystickPowerLevel epowerlevel; /* power level of this joystick, SDL_JOYSTICK_POWER_UNKNOWN if not supported */
+ struct _SDL_JoystickDriver *driver;
+
+ struct joystick_hwdata *hwdata; /* Driver dependent information */
+
+ int ref_count; /* Reference count for multiple opens */
+
struct _SDL_Joystick *next; /* pointer to next joystick we have allocated */
};
+#if defined(__IPHONEOS__) || defined(__ANDROID__)
+#define HAVE_STEAMCONTROLLERS
+#define USE_STEAMCONTROLLER_HIDAPI
+#elif defined(__LINUX__)
+#define HAVE_STEAMCONTROLLERS
+#define USE_STEAMCONTROLLER_LINUX
+#endif
+
+/* Device bus definitions */
+#define SDL_HARDWARE_BUS_USB 0x03
+#define SDL_HARDWARE_BUS_BLUETOOTH 0x05
+
/* Macro to combine a USB vendor ID and product ID into a single Uint32 value */
#define MAKE_VIDPID(VID, PID) (((Uint32)(VID))<<16|(PID))
-/* Function to scan the system for joysticks.
- * Joystick 0 should be the system default joystick.
- * This function should return the number of available joysticks, or -1
- * on an unrecoverable fatal error.
- */
-extern int SDL_SYS_JoystickInit(void);
+typedef struct _SDL_JoystickDriver
+{
+ /* Function to scan the system for joysticks.
+ * Joystick 0 should be the system default joystick.
+ * This function should return 0, or -1 on an unrecoverable error.
+ */
+ int (*Init)(void);
-/* Function to return the number of joystick devices plugged in right now */
-extern int SDL_SYS_NumJoysticks(void);
+ /* Function to return the number of joystick devices plugged in right now */
+ int (*GetCount)(void);
-/* Function to cause any queued joystick insertions to be processed */
-extern void SDL_SYS_JoystickDetect(void);
+ /* Function to cause any queued joystick insertions to be processed */
+ void (*Detect)(void);
-/* Function to get the device-dependent name of a joystick */
-extern const char *SDL_SYS_JoystickNameForDeviceIndex(int device_index);
+ /* Function to get the device-dependent name of a joystick */
+ const char *(*GetDeviceName)(int device_index);
-/* Function to get the current instance id of the joystick located at device_index */
-extern SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index);
+ /* Function to return the stable GUID for a plugged in device */
+ SDL_JoystickGUID (*GetDeviceGUID)(int device_index);
-/* Function to open a joystick for use.
- The joystick to open is specified by the device index.
- This should fill the nbuttons and naxes fields of the joystick structure.
- It returns 0, or -1 if there is an error.
- */
-extern int SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index);
+ /* Function to get the current instance id of the joystick located at device_index */
+ SDL_JoystickID (*GetDeviceInstanceID)(int device_index);
-/* Function to query if the joystick is currently attached
- * It returns SDL_TRUE if attached, SDL_FALSE otherwise.
- */
-extern SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick * joystick);
+ /* Function to open a joystick for use.
+ The joystick to open is specified by the device index.
+ This should fill the nbuttons and naxes fields of the joystick structure.
+ It returns 0, or -1 if there is an error.
+ */
+ int (*Open)(SDL_Joystick * joystick, int device_index);
-/* Function to update the state of a joystick - called as a device poll.
- * This function shouldn't update the joystick structure directly,
- * but instead should call SDL_PrivateJoystick*() to deliver events
- * and update joystick device state.
- */
-extern void SDL_SYS_JoystickUpdate(SDL_Joystick * joystick);
+ /* Function to query if the joystick is currently attached
+ * It returns SDL_TRUE if attached, SDL_FALSE otherwise.
+ */
+ SDL_bool (*IsAttached)(SDL_Joystick * joystick);
-/* Function to close a joystick after use */
-extern void SDL_SYS_JoystickClose(SDL_Joystick * joystick);
+ /* Rumble functionality */
+ int (*Rumble)(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
-/* Function to perform any system-specific joystick related cleanup */
-extern void SDL_SYS_JoystickQuit(void);
+ /* Function to update the state of a joystick - called as a device poll.
+ * This function shouldn't update the joystick structure directly,
+ * but instead should call SDL_PrivateJoystick*() to deliver events
+ * and update joystick device state.
+ */
+ void (*Update)(SDL_Joystick * joystick);
-/* Function to return the stable GUID for a plugged in device */
-extern SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index);
+ /* Function to close a joystick after use */
+ void (*Close)(SDL_Joystick * joystick);
-/* Function to return the stable GUID for a opened joystick */
-extern SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick);
+ /* Function to perform any system-specific joystick related cleanup */
+ void (*Quit)(void);
+
+} SDL_JoystickDriver;
+
+/* The available joystick drivers */
+extern SDL_JoystickDriver SDL_ANDROID_JoystickDriver;
+extern SDL_JoystickDriver SDL_DARWIN_JoystickDriver;
+extern SDL_JoystickDriver SDL_DUMMY_JoystickDriver;
+extern SDL_JoystickDriver SDL_HIDAPI_JoystickDriver;
+extern SDL_JoystickDriver SDL_IOS_JoystickDriver;
+extern SDL_JoystickDriver SDL_LINUX_JoystickDriver;
+extern SDL_JoystickDriver SDL_WINDOWS_JoystickDriver;
#endif /* SDL_sysjoystick_h_ */
diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c
index 7132a5581..6b12c511b 100644
--- a/src/joystick/android/SDL_sysjoystick.c
+++ b/src/joystick/android/SDL_sysjoystick.c
@@ -36,7 +36,7 @@
#include "../SDL_joystick_c.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../core/android/SDL_android.h"
-#include "../steam/SDL_steamcontroller.h"
+#include "../hidapi/SDL_hidapijoystick_c.h"
#include "android/keycodes.h"
@@ -69,7 +69,6 @@ static SDL_joylist_item * JoystickByDeviceId(int device_id);
static SDL_joylist_item *SDL_joylist = NULL;
static SDL_joylist_item *SDL_joylist_tail = NULL;
static int numjoysticks = 0;
-static int instance_counter = 0;
/* Public domain CRC implementation adapted from:
@@ -326,7 +325,6 @@ Android_OnHat(int device_id, int hat_id, int x, int y)
int
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)
{
- const Uint16 BUS_BLUETOOTH = 0x05;
SDL_joylist_item *item;
SDL_JoystickGUID guid;
Uint16 *guid16 = (Uint16 *)guid.data;
@@ -344,7 +342,14 @@ Android_AddJoystick(int device_id, const char *name, const char *desc, int vendo
if (JoystickByDeviceId(device_id) != NULL || name == NULL) {
return -1;
}
-
+
+#ifdef SDL_JOYSTICK_HIDAPI
+ if (HIDAPI_IsDevicePresent(vendor_id, product_id)) {
+ /* The HIDAPI driver is taking care of this device */
+ return -1;
+ }
+#endif
+
#ifdef DEBUG_JOYSTICK
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);
#endif
@@ -378,7 +383,7 @@ Android_AddJoystick(int device_id, const char *name, const char *desc, int vendo
/* We only need 16 bits for each of these; space them out to fill 128. */
/* Byteswap so devices get same GUID on little/big endian platforms. */
- *guid16++ = SDL_SwapLE16(BUS_BLUETOOTH);
+ *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
*guid16++ = 0;
if (vendor_id && product_id) {
@@ -424,7 +429,7 @@ Android_AddJoystick(int device_id, const char *name, const char *desc, int vendo
item->naxes = naxes;
item->nhats = nhats;
item->nballs = nballs;
- item->device_instance = instance_counter++;
+ item->device_instance = SDL_GetNextJoystickInstanceID();
if (SDL_joylist_tail == NULL) {
SDL_joylist = SDL_joylist_tail = item;
} else {
@@ -435,7 +440,7 @@ Android_AddJoystick(int device_id, const char *name, const char *desc, int vendo
/* Need to increment the joystick count before we post the event */
++numjoysticks;
- SDL_PrivateJoystickAdded(numjoysticks - 1);
+ SDL_PrivateJoystickAdded(item->device_instance);
#ifdef DEBUG_JOYSTICK
SDL_Log("Added joystick %s with device_id %d", name, device_id);
@@ -492,104 +497,29 @@ Android_RemoveJoystick(int device_id)
}
-static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickGUID guid, int *device_instance)
+static void ANDROID_JoystickDetect();
+
+static int
+ANDROID_JoystickInit(void)
{
- SDL_joylist_item *item;
-
- item = (SDL_joylist_item *)SDL_calloc(1, sizeof (SDL_joylist_item));
- if (item == NULL) {
- return SDL_FALSE;
- }
-
- *device_instance = item->device_instance = instance_counter++;
- item->device_id = -1;
- item->name = SDL_strdup(name);
- item->guid = guid;
- SDL_GetSteamControllerInputs(&item->nbuttons,
- &item->naxes,
- &item->nhats);
- item->m_bSteamController = SDL_TRUE;
-
- if (SDL_joylist_tail == NULL) {
- SDL_joylist = SDL_joylist_tail = item;
- } else {
- SDL_joylist_tail->next = item;
- SDL_joylist_tail = item;
- }
-
- /* Need to increment the joystick count before we post the event */
- ++numjoysticks;
-
- SDL_PrivateJoystickAdded(numjoysticks - 1);
-
- return SDL_TRUE;
-}
-
-static void SteamControllerDisconnectedCallback(int device_instance)
-{
- SDL_joylist_item *item = SDL_joylist;
- SDL_joylist_item *prev = NULL;
-
- while (item != NULL) {
- if (item->device_instance == device_instance) {
- break;
- }
- prev = item;
- item = item->next;
- }
-
- if (item == NULL) {
- return;
- }
-
- if (item->joystick) {
- item->joystick->hwdata = NULL;
- }
-
- if (prev != NULL) {
- prev->next = item->next;
- } else {
- SDL_assert(SDL_joylist == item);
- SDL_joylist = item->next;
- }
- if (item == SDL_joylist_tail) {
- SDL_joylist_tail = prev;
- }
-
- /* Need to decrement the joystick count before we post the event */
- --numjoysticks;
-
- SDL_PrivateJoystickRemoved(item->device_instance);
-
- SDL_free(item->name);
- SDL_free(item);
-}
-
-int
-SDL_SYS_JoystickInit(void)
-{
- SDL_SYS_JoystickDetect();
+ ANDROID_JoystickDetect();
if (SDL_GetHintBoolean(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, SDL_TRUE)) {
/* Default behavior, accelerometer as joystick */
Android_AddJoystick(ANDROID_ACCELEROMETER_DEVICE_ID, ANDROID_ACCELEROMETER_NAME, ANDROID_ACCELEROMETER_NAME, 0, 0, SDL_TRUE, 0, 3, 0, 0);
}
-
- SDL_InitSteamControllers(SteamControllerConnectedCallback,
- SteamControllerDisconnectedCallback);
-
- return (numjoysticks);
+ return 0;
}
-int
-SDL_SYS_NumJoysticks(void)
+static int
+ANDROID_JoystickGetCount(void)
{
return numjoysticks;
}
-void
-SDL_SYS_JoystickDetect(void)
+static void
+ANDROID_JoystickDetect(void)
{
/* Support for device connect/disconnect is API >= 16 only,
* so we poll every three seconds
@@ -600,8 +530,6 @@ SDL_SYS_JoystickDetect(void)
timeout = SDL_GetTicks() + 3000;
Android_JNI_PollInputDevices();
}
-
- SDL_UpdateSteamControllers();
}
static SDL_joylist_item *
@@ -635,7 +563,7 @@ JoystickByDeviceId(int device_id)
}
/* Joystick not found, try adding it */
- SDL_SYS_JoystickDetect();
+ ANDROID_JoystickDetect();
while (item != NULL) {
if (item->device_id == device_id) {
@@ -647,26 +575,26 @@ JoystickByDeviceId(int device_id)
return NULL;
}
-/* Function to get the device-dependent name of a joystick */
-const char *
-SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+static const char *
+ANDROID_JoystickGetDeviceName(int device_index)
{
return JoystickByDevIndex(device_index)->name;
}
-/* Function to perform the mapping from device index to the instance id for this index */
-SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+static SDL_JoystickGUID
+ANDROID_JoystickGetDeviceGUID(int device_index)
+{
+ return JoystickByDevIndex(device_index)->guid;
+}
+
+static SDL_JoystickID
+ANDROID_JoystickGetDeviceInstanceID(int device_index)
{
return JoystickByDevIndex(device_index)->device_instance;
}
-/* Function to open a joystick for use.
- The joystick to open is specified by the device index.
- This should fill the nbuttons and naxes fields of the joystick structure.
- It returns 0, or -1 if there is an error.
- */
-int
-SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
+static int
+ANDROID_JoystickOpen(SDL_Joystick * joystick, int device_index)
{
SDL_joylist_item *item = JoystickByDevIndex(device_index);
@@ -689,14 +617,20 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
return (0);
}
-/* Function to determine if this joystick is attached to the system right now */
-SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
+static SDL_bool
+ANDROID_JoystickIsAttached(SDL_Joystick *joystick)
{
return joystick->hwdata != NULL;
}
-void
-SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+static int
+ANDROID_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ return SDL_Unsupported();
+}
+
+static void
+ANDROID_JoystickUpdate(SDL_Joystick * joystick)
{
SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
@@ -704,11 +638,6 @@ SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
return;
}
- if (item->m_bSteamController) {
- SDL_UpdateSteamController(joystick);
- return;
- }
-
if (item->is_accelerometer) {
int i;
Sint16 value;
@@ -729,9 +658,8 @@ SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
}
}
-/* Function to close a joystick after use */
-void
-SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+static void
+ANDROID_JoystickClose(SDL_Joystick * joystick)
{
SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
if (item) {
@@ -739,9 +667,8 @@ SDL_SYS_JoystickClose(SDL_Joystick * joystick)
}
}
-/* Function to perform any system-specific joystick related cleanup */
-void
-SDL_SYS_JoystickQuit(void)
+static void
+ANDROID_JoystickQuit(void)
{
/* We don't have any way to scan for joysticks at init, so don't wipe the list
* of joysticks here in case this is a reinit.
@@ -759,28 +686,24 @@ SDL_SYS_JoystickQuit(void)
SDL_joylist = SDL_joylist_tail = NULL;
numjoysticks = 0;
- instance_counter = 0;
#endif /* 0 */
-
- SDL_QuitSteamControllers();
}
-SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index)
+SDL_JoystickDriver SDL_ANDROID_JoystickDriver =
{
- return JoystickByDevIndex(device_index)->guid;
-}
-
-SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
-{
- SDL_JoystickGUID guid;
-
- if (joystick->hwdata != NULL) {
- return ((SDL_joylist_item*)joystick->hwdata)->guid;
- }
-
- SDL_zero(guid);
- return guid;
-}
+ ANDROID_JoystickInit,
+ ANDROID_JoystickGetCount,
+ ANDROID_JoystickDetect,
+ ANDROID_JoystickGetDeviceName,
+ ANDROID_JoystickGetDeviceGUID,
+ ANDROID_JoystickGetDeviceInstanceID,
+ ANDROID_JoystickOpen,
+ ANDROID_JoystickIsAttached,
+ ANDROID_JoystickRumble,
+ ANDROID_JoystickUpdate,
+ ANDROID_JoystickClose,
+ ANDROID_JoystickQuit,
+};
#endif /* SDL_JOYSTICK_ANDROID */
diff --git a/src/joystick/android/SDL_sysjoystick_c.h b/src/joystick/android/SDL_sysjoystick_c.h
index 0cbf537b5..20d73810d 100644
--- a/src/joystick/android/SDL_sysjoystick_c.h
+++ b/src/joystick/android/SDL_sysjoystick_c.h
@@ -47,9 +47,6 @@ typedef struct SDL_joylist_item
int nbuttons, naxes, nhats, nballs;
int dpad_state;
- /* Steam Controller support */
- SDL_bool m_bSteamController;
-
struct SDL_joylist_item *next;
} SDL_joylist_item;
diff --git a/src/joystick/darwin/SDL_sysjoystick.c b/src/joystick/darwin/SDL_sysjoystick.c
index abfb1c62b..0b72d052c 100644
--- a/src/joystick/darwin/SDL_sysjoystick.c
+++ b/src/joystick/darwin/SDL_sysjoystick.c
@@ -22,29 +22,79 @@
#ifdef SDL_JOYSTICK_IOKIT
-#include
-
-/* For force feedback testing. */
-#include
-#include
-
+#include "SDL_events.h"
#include "SDL_joystick.h"
#include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.h"
#include "SDL_sysjoystick_c.h"
-#include "SDL_events.h"
+#include "../hidapi/SDL_hidapijoystick_c.h"
#include "../../haptic/darwin/SDL_syshaptic_c.h" /* For haptic hot plugging */
+
#define SDL_JOYSTICK_RUNLOOP_MODE CFSTR("SDLJoystick")
+#define CONVERT_MAGNITUDE(x) (((x)*10000) / 0x7FFF)
+
/* The base object of the HID Manager API */
static IOHIDManagerRef hidman = NULL;
/* Linked list of all available devices */
static recDevice *gpDeviceList = NULL;
-/* static incrementing counter for new joystick devices seen on the system. Devices should start with index 0 */
-static int s_joystick_instance_id = -1;
+void FreeRumbleEffectData(FFEFFECT *effect)
+{
+ if (!effect) {
+ return;
+ }
+ SDL_free(effect->rgdwAxes);
+ SDL_free(effect->rglDirection);
+ SDL_free(effect->lpvTypeSpecificParams);
+ SDL_free(effect);
+}
+
+FFEFFECT *CreateRumbleEffectData(Sint16 magnitude, Uint32 duration_ms)
+{
+ FFEFFECT *effect;
+ FFPERIODIC *periodic;
+
+ /* Create the effect */
+ effect = (FFEFFECT *)SDL_calloc(1, sizeof(*effect));
+ if (!effect) {
+ return NULL;
+ }
+ effect->dwSize = sizeof(*effect);
+ effect->dwGain = 10000;
+ effect->dwFlags = FFEFF_OBJECTOFFSETS;
+ effect->dwDuration = duration_ms * 1000; /* In microseconds. */
+ effect->dwTriggerButton = FFEB_NOTRIGGER;
+
+ effect->cAxes = 2;
+ effect->rgdwAxes = (DWORD *)SDL_calloc(effect->cAxes, sizeof(DWORD));
+ if (!effect->rgdwAxes) {
+ FreeRumbleEffectData(effect);
+ return NULL;
+ }
+
+ effect->rglDirection = (LONG *)SDL_calloc(effect->cAxes, sizeof(LONG));
+ if (!effect->rglDirection) {
+ FreeRumbleEffectData(effect);
+ return NULL;
+ }
+ effect->dwFlags |= FFEFF_CARTESIAN;
+
+ periodic = (FFPERIODIC *)SDL_calloc(1, sizeof(*periodic));
+ if (!periodic) {
+ FreeRumbleEffectData(effect);
+ return NULL;
+ }
+ periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
+ periodic->dwPeriod = 1000000;
+
+ effect->cbTypeSpecificParams = sizeof(*periodic);
+ effect->lpvTypeSpecificParams = periodic;
+
+ return effect;
+}
static recDevice *GetDeviceForIndex(int device_index)
{
@@ -157,6 +207,19 @@ JoystickDeviceWasRemovedCallback(void *ctx, IOReturn result, void *sender)
recDevice *device = (recDevice *) ctx;
device->removed = SDL_TRUE;
device->deviceRef = NULL; // deviceRef was invalidated due to the remove
+ if (device->ffeffect_ref) {
+ FFDeviceReleaseEffect(device->ffdevice, device->ffeffect_ref);
+ device->ffeffect_ref = NULL;
+ }
+ if (device->ffeffect) {
+ FreeRumbleEffectData(device->ffeffect);
+ device->ffeffect = NULL;
+ }
+ if (device->ffdevice) {
+ FFReleaseDevice(device->ffdevice);
+ device->ffdevice = NULL;
+ device->ff_initialized = SDL_FALSE;
+ }
#if SDL_HAPTIC_IOKIT
MacHaptic_MaybeRemoveDevice(device->ffservice);
#endif
@@ -333,8 +396,6 @@ AddHIDElement(const void *value, void *parameter)
static SDL_bool
GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
{
- const Uint16 BUS_USB = 0x03;
- const Uint16 BUS_BLUETOOTH = 0x05;
Sint32 vendor = 0;
Sint32 product = 0;
Sint32 version = 0;
@@ -389,10 +450,17 @@ GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
CFNumberGetValue(refCF, kCFNumberSInt32Type, &version);
}
+#ifdef SDL_JOYSTICK_HIDAPI
+ if (HIDAPI_IsDevicePresent(vendor, product)) {
+ /* The HIDAPI driver is taking care of this device */
+ return 0;
+ }
+#endif
+
SDL_memset(pDevice->guid.data, 0, sizeof(pDevice->guid.data));
if (vendor && product) {
- *guid16++ = SDL_SwapLE16(BUS_USB);
+ *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
*guid16++ = 0;
*guid16++ = SDL_SwapLE16((Uint16)vendor);
*guid16++ = 0;
@@ -401,7 +469,7 @@ GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
*guid16++ = SDL_SwapLE16((Uint16)version);
*guid16++ = 0;
} else {
- *guid16++ = SDL_SwapLE16(BUS_BLUETOOTH);
+ *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
*guid16++ = 0;
SDL_strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4);
}
@@ -444,7 +512,6 @@ JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDevic
}
device = (recDevice *) SDL_calloc(1, sizeof(recDevice));
-
if (!device) {
SDL_OutOfMemory();
return;
@@ -455,8 +522,7 @@ JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDevic
return; /* not a device we care about, probably. */
}
- if (SDL_IsGameControllerNameAndGUID(device->product, device->guid) &&
- SDL_ShouldIgnoreGameController(device->product, device->guid)) {
+ if (SDL_ShouldIgnoreJoystick(device->product, device->guid)) {
SDL_free(device);
return;
}
@@ -466,16 +532,16 @@ JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDevic
IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
/* Allocate an instance ID for this device */
- device->instance_id = ++s_joystick_instance_id;
+ device->instance_id = SDL_GetNextJoystickInstanceID();
/* We have to do some storage of the io_service_t for SDL_HapticOpenFromJoystick */
ioservice = IOHIDDeviceGetService(ioHIDDeviceObject);
-#if SDL_HAPTIC_IOKIT
if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK)) {
device->ffservice = ioservice;
+#if SDL_HAPTIC_IOKIT
MacHaptic_MaybeAddDevice(ioservice);
- }
#endif
+ }
/* Add device to the end of the list */
if ( !gpDeviceList ) {
@@ -492,7 +558,7 @@ JoystickDeviceWasAddedCallback(void *ctx, IOReturn res, void *sender, IOHIDDevic
++device_index; /* bump by one since we counted by pNext. */
}
- SDL_PrivateJoystickAdded(device_index);
+ SDL_PrivateJoystickAdded(device->instance_id);
}
static SDL_bool
@@ -577,13 +643,8 @@ CreateHIDManager(void)
}
-/* Function to scan the system for joysticks.
- * Joystick 0 should be the system default joystick.
- * This function should return the number of available joysticks, or -1
- * on an unrecoverable fatal error.
- */
-int
-SDL_SYS_JoystickInit(void)
+static int
+DARWIN_JoystickInit(void)
{
if (gpDeviceList) {
return SDL_SetError("Joystick: Device list already inited.");
@@ -593,12 +654,11 @@ SDL_SYS_JoystickInit(void)
return SDL_SetError("Joystick: Couldn't initialize HID Manager");
}
- return SDL_SYS_NumJoysticks();
+ return 0;
}
-/* Function to return the number of joystick devices plugged in right now */
-int
-SDL_SYS_NumJoysticks(void)
+static int
+DARWIN_JoystickGetCount(void)
{
recDevice *device = gpDeviceList;
int nJoySticks = 0;
@@ -613,10 +673,8 @@ SDL_SYS_NumJoysticks(void)
return nJoySticks;
}
-/* Function to cause any queued joystick insertions to be processed
- */
-void
-SDL_SYS_JoystickDetect(void)
+static void
+DARWIN_JoystickDetect(void)
{
recDevice *device = gpDeviceList;
while (device) {
@@ -627,37 +685,43 @@ SDL_SYS_JoystickDetect(void)
}
}
- /* run this after the checks above so we don't set device->removed and delete the device before
- SDL_SYS_JoystickUpdate can run to clean up the SDL_Joystick object that owns this device */
- while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
- /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
- }
+ /* run this after the checks above so we don't set device->removed and delete the device before
+ DARWIN_JoystickUpdate can run to clean up the SDL_Joystick object that owns this device */
+ while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
+ /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
+ }
}
/* Function to get the device-dependent name of a joystick */
const char *
-SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+DARWIN_JoystickGetDeviceName(int device_index)
{
recDevice *device = GetDeviceForIndex(device_index);
return device ? device->product : "UNKNOWN";
}
-/* Function to return the instance id of the joystick at device_index
- */
-SDL_JoystickID
-SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+static SDL_JoystickGUID
+DARWIN_JoystickGetDeviceGUID( int device_index )
+{
+ recDevice *device = GetDeviceForIndex(device_index);
+ SDL_JoystickGUID guid;
+ if (device) {
+ guid = device->guid;
+ } else {
+ SDL_zero(guid);
+ }
+ return guid;
+}
+
+static SDL_JoystickID
+DARWIN_JoystickGetDeviceInstanceID(int device_index)
{
recDevice *device = GetDeviceForIndex(device_index);
return device ? device->instance_id : 0;
}
-/* Function to open a joystick for use.
- * The joystick to open is specified by the device index.
- * This should fill the nbuttons and naxes fields of the joystick structure.
- * It returns 0, or -1 if there is an error.
- */
-int
-SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
+static int
+DARWIN_JoystickOpen(SDL_Joystick * joystick, int device_index)
{
recDevice *device = GetDeviceForIndex(device_index);
@@ -672,22 +736,144 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
return 0;
}
-/* Function to query if the joystick is currently attached
- * It returns SDL_TRUE if attached, SDL_FALSE otherwise.
- */
-SDL_bool
-SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
+static SDL_bool
+DARWIN_JoystickIsAttached(SDL_Joystick * joystick)
{
return joystick->hwdata != NULL;
}
-/* Function to update the state of a joystick - called as a device poll.
- * This function shouldn't update the joystick structure directly,
- * but instead should call SDL_PrivateJoystick*() to deliver events
- * and update joystick device state.
+/*
+ * Like strerror but for force feedback errors.
*/
-void
-SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+static const char *
+FFStrError(unsigned int err)
+{
+ switch (err) {
+ case FFERR_DEVICEFULL:
+ return "device full";
+ /* This should be valid, but for some reason isn't defined... */
+ /* case FFERR_DEVICENOTREG:
+ return "device not registered"; */
+ case FFERR_DEVICEPAUSED:
+ return "device paused";
+ case FFERR_DEVICERELEASED:
+ return "device released";
+ case FFERR_EFFECTPLAYING:
+ return "effect playing";
+ case FFERR_EFFECTTYPEMISMATCH:
+ return "effect type mismatch";
+ case FFERR_EFFECTTYPENOTSUPPORTED:
+ return "effect type not supported";
+ case FFERR_GENERIC:
+ return "undetermined error";
+ case FFERR_HASEFFECTS:
+ return "device has effects";
+ case FFERR_INCOMPLETEEFFECT:
+ return "incomplete effect";
+ case FFERR_INTERNAL:
+ return "internal fault";
+ case FFERR_INVALIDDOWNLOADID:
+ return "invalid download id";
+ case FFERR_INVALIDPARAM:
+ return "invalid parameter";
+ case FFERR_MOREDATA:
+ return "more data";
+ case FFERR_NOINTERFACE:
+ return "interface not supported";
+ case FFERR_NOTDOWNLOADED:
+ return "effect is not downloaded";
+ case FFERR_NOTINITIALIZED:
+ return "object has not been initialized";
+ case FFERR_OUTOFMEMORY:
+ return "out of memory";
+ case FFERR_UNPLUGGED:
+ return "device is unplugged";
+ case FFERR_UNSUPPORTED:
+ return "function call unsupported";
+ case FFERR_UNSUPPORTEDAXIS:
+ return "axis unsupported";
+
+ default:
+ return "unknown error";
+ }
+}
+
+static int
+DARWIN_JoystickInitRumble(recDevice *device, Sint16 magnitude, Uint32 duration_ms)
+{
+ HRESULT result;
+
+ if (!device->ffdevice) {
+ result = FFCreateDevice(device->ffservice, &device->ffdevice);
+ if (result != FF_OK) {
+ return SDL_SetError("Unable to create force feedback device from service: %s", FFStrError(result));
+ }
+ }
+
+ /* Reset and then enable actuators */
+ result = FFDeviceSendForceFeedbackCommand(device->ffdevice, FFSFFC_RESET);
+ if (result != FF_OK) {
+ return SDL_SetError("Unable to reset force feedback device: %s", FFStrError(result));
+ }
+
+ result = FFDeviceSendForceFeedbackCommand(device->ffdevice, FFSFFC_SETACTUATORSON);
+ if (result != FF_OK) {
+ return SDL_SetError("Unable to enable force feedback actuators: %s", FFStrError(result));
+ }
+
+ /* Create the effect */
+ device->ffeffect = CreateRumbleEffectData(magnitude, duration_ms);
+ if (!device->ffeffect) {
+ return SDL_OutOfMemory();
+ }
+
+ result = FFDeviceCreateEffect(device->ffdevice, kFFEffectType_Sine_ID,
+ device->ffeffect, &device->ffeffect_ref);
+ if (result != FF_OK) {
+ return SDL_SetError("Haptic: Unable to create effect: %s", FFStrError(result));
+ }
+ return 0;
+}
+
+static int
+DARWIN_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ HRESULT result;
+ recDevice *device = joystick->hwdata;
+
+ /* Scale and average the two rumble strengths */
+ Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
+
+ if (!device->ffservice) {
+ return SDL_Unsupported();
+ }
+
+ if (device->ff_initialized) {
+ FFPERIODIC *periodic = ((FFPERIODIC *)device->ffeffect->lpvTypeSpecificParams);
+ device->ffeffect->dwDuration = duration_ms * 1000; /* In microseconds. */
+ periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
+
+ result = FFEffectSetParameters(device->ffeffect_ref, device->ffeffect,
+ (FFEP_DURATION | FFEP_TYPESPECIFICPARAMS));
+ if (result != FF_OK) {
+ return SDL_SetError("Unable to update rumble effect: %s", FFStrError(result));
+ }
+ } else {
+ if (DARWIN_JoystickInitRumble(device, magnitude, duration_ms) < 0) {
+ return -1;
+ }
+ device->ff_initialized = SDL_TRUE;
+ }
+
+ result = FFEffectStart(device->ffeffect_ref, 1, 0);
+ if (result != FF_OK) {
+ return SDL_SetError("Unable to run the rumble effect: %s", FFStrError(result));
+ }
+ return 0;
+}
+
+static void
+DARWIN_JoystickUpdate(SDL_Joystick * joystick)
{
recDevice *device = joystick->hwdata;
recElement *element;
@@ -792,15 +978,13 @@ SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
}
}
-/* Function to close a joystick after use */
-void
-SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+static void
+DARWIN_JoystickClose(SDL_Joystick * joystick)
{
}
-/* Function to perform any system-specific joystick related cleanup */
-void
-SDL_SYS_JoystickQuit(void)
+static void
+DARWIN_JoystickQuit(void)
{
while (FreeDevice(gpDeviceList)) {
/* spin */
@@ -814,23 +998,21 @@ SDL_SYS_JoystickQuit(void)
}
}
-
-SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
+SDL_JoystickDriver SDL_DARWIN_JoystickDriver =
{
- recDevice *device = GetDeviceForIndex(device_index);
- SDL_JoystickGUID guid;
- if (device) {
- guid = device->guid;
- } else {
- SDL_zero(guid);
- }
- return guid;
-}
-
-SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick *joystick)
-{
- return joystick->hwdata->guid;
-}
+ DARWIN_JoystickInit,
+ DARWIN_JoystickGetCount,
+ DARWIN_JoystickDetect,
+ DARWIN_JoystickGetDeviceName,
+ DARWIN_JoystickGetDeviceGUID,
+ DARWIN_JoystickGetDeviceInstanceID,
+ DARWIN_JoystickOpen,
+ DARWIN_JoystickIsAttached,
+ DARWIN_JoystickRumble,
+ DARWIN_JoystickUpdate,
+ DARWIN_JoystickClose,
+ DARWIN_JoystickQuit,
+};
#endif /* SDL_JOYSTICK_IOKIT */
diff --git a/src/joystick/darwin/SDL_sysjoystick_c.h b/src/joystick/darwin/SDL_sysjoystick_c.h
index cde6a5c21..2168f912e 100644
--- a/src/joystick/darwin/SDL_sysjoystick_c.h
+++ b/src/joystick/darwin/SDL_sysjoystick_c.h
@@ -24,6 +24,8 @@
#define SDL_JOYSTICK_IOKIT_H
#include
+#include
+#include
struct recElement
{
@@ -45,6 +47,10 @@ struct joystick_hwdata
{
IOHIDDeviceRef deviceRef; /* HIDManager device handle */
io_service_t ffservice; /* Interface for force feedback, 0 = no ff */
+ FFDeviceObjectReference ffdevice;
+ FFEFFECT *ffeffect;
+ FFEffectObjectReference ffeffect_ref;
+ SDL_bool ff_initialized;
char product[256]; /* name of product */
uint32_t usage; /* usage page from IOUSBHID Parser.h which defines general usage */
diff --git a/src/joystick/dummy/SDL_sysjoystick.c b/src/joystick/dummy/SDL_sysjoystick.c
index 3dd96a001..c491e13f7 100644
--- a/src/joystick/dummy/SDL_sysjoystick.c
+++ b/src/joystick/dummy/SDL_sysjoystick.c
@@ -28,99 +28,92 @@
#include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.h"
-/* Function to scan the system for joysticks.
- * It should return 0, or -1 on an unrecoverable fatal error.
- */
-int
-SDL_SYS_JoystickInit(void)
+
+static int
+DUMMY_JoystickInit(void)
{
return 0;
}
-int
-SDL_SYS_NumJoysticks(void)
+static int
+DUMMY_JoystickGetCount(void)
{
return 0;
}
-void
-SDL_SYS_JoystickDetect(void)
+static void
+DUMMY_JoystickDetect(void)
{
}
-/* Function to get the device-dependent name of a joystick */
-const char *
-SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+static const char *
+DUMMY_JoystickGetDeviceName(int device_index)
{
- SDL_SetError("Logic error: No joysticks available");
- return (NULL);
+ return NULL;
}
-/* Function to perform the mapping from device index to the instance id for this index */
-SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+static SDL_JoystickGUID
+DUMMY_JoystickGetDeviceGUID(int device_index)
{
- return device_index;
+ SDL_JoystickGUID guid;
+ SDL_zero(guid);
+ return guid;
}
-/* Function to open a joystick for use.
- The joystick to open is specified by the device index.
- This should fill the nbuttons and naxes fields of the joystick structure.
- It returns 0, or -1 if there is an error.
- */
-int
-SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
+static SDL_JoystickID
+DUMMY_JoystickGetDeviceInstanceID(int device_index)
+{
+ return -1;
+}
+
+static int
+DUMMY_JoystickOpen(SDL_Joystick * joystick, int device_index)
{
return SDL_SetError("Logic error: No joysticks available");
}
-/* Function to determine if this joystick is attached to the system right now */
-SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
+static SDL_bool
+DUMMY_JoystickIsAttached(SDL_Joystick *joystick)
{
- return SDL_TRUE;
+ return SDL_FALSE;
}
-/* Function to update the state of a joystick - called as a device poll.
- * This function shouldn't update the joystick structure directly,
- * but instead should call SDL_PrivateJoystick*() to deliver events
- * and update joystick device state.
- */
-void
-SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+static int
+DUMMY_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ return SDL_Unsupported();
+}
+
+static void
+DUMMY_JoystickUpdate(SDL_Joystick * joystick)
{
}
-/* Function to close a joystick after use */
-void
-SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+static void
+DUMMY_JoystickClose(SDL_Joystick * joystick)
{
}
-/* Function to perform any system-specific joystick related cleanup */
-void
-SDL_SYS_JoystickQuit(void)
+static void
+DUMMY_JoystickQuit(void)
{
}
-SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
+SDL_JoystickDriver SDL_DUMMY_JoystickDriver =
{
- SDL_JoystickGUID guid;
- /* the GUID is just the first 16 chars of the name for now */
- const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index );
- SDL_zero( guid );
- SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
- return guid;
-}
-
-
-SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
-{
- SDL_JoystickGUID guid;
- /* the GUID is just the first 16 chars of the name for now */
- const char *name = joystick->name;
- SDL_zero( guid );
- SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
- return guid;
-}
+ DUMMY_JoystickInit,
+ DUMMY_JoystickGetCount,
+ DUMMY_JoystickDetect,
+ DUMMY_JoystickGetDeviceName,
+ DUMMY_JoystickGetDeviceGUID,
+ DUMMY_JoystickGetDeviceInstanceID,
+ DUMMY_JoystickOpen,
+ DUMMY_JoystickIsAttached,
+ DUMMY_JoystickRumble,
+ DUMMY_JoystickUpdate,
+ DUMMY_JoystickClose,
+ DUMMY_JoystickQuit,
+};
#endif /* SDL_JOYSTICK_DUMMY || SDL_JOYSTICK_DISABLED */
diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c
new file mode 100644
index 000000000..a37fcf3b6
--- /dev/null
+++ b/src/joystick/hidapi/SDL_hidapi_ps4.c
@@ -0,0 +1,533 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2018 Sam Lantinga
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#include "SDL_hints.h"
+#include "SDL_log.h"
+#include "SDL_events.h"
+#include "SDL_timer.h"
+#include "SDL_joystick.h"
+#include "SDL_gamecontroller.h"
+#include "../SDL_sysjoystick.h"
+#include "SDL_hidapijoystick_c.h"
+
+
+#ifdef SDL_JOYSTICK_HIDAPI_PS4
+
+#define SONY_USB_VID 0x054C
+#define SONY_DS4_PID 0x05C4
+#define SONY_DS4_DONGLE_PID 0x0BA0
+#define SONY_DS4_SLIM_PID 0x09CC
+
+#define USB_PACKET_LENGTH 64
+
+#define VOLUME_CHECK_INTERVAL_MS (10 * 1000)
+
+typedef enum
+{
+ k_EPS4ReportIdUsbState = 1,
+ k_EPS4ReportIdUsbEffects = 5,
+ k_EPS4ReportIdBluetoothState = 17,
+ k_EPS4ReportIdBluetoothEffects = 17,
+ k_EPS4ReportIdDisconnectMessage = 226,
+} EPS4ReportId;
+
+typedef enum
+{
+ k_ePS4FeatureReportIdGyroCalibration_USB = 0x02,
+ k_ePS4FeatureReportIdGyroCalibration_BT = 0x05,
+ k_ePS4FeatureReportIdSerialNumber = 0x12,
+} EPS4FeatureReportID;
+
+typedef struct
+{
+ Uint8 ucLeftJoystickX;
+ Uint8 ucLeftJoystickY;
+ Uint8 ucRightJoystickX;
+ Uint8 ucRightJoystickY;
+ Uint8 rgucButtonsHatAndCounter[ 3 ];
+ Uint8 ucTriggerLeft;
+ Uint8 ucTriggerRight;
+ Uint8 _rgucPad0[ 3 ];
+ Sint16 sGyroX;
+ Sint16 sGyroY;
+ Sint16 sGyroZ;
+ Sint16 sAccelX;
+ Sint16 sAccelY;
+ Sint16 sAccelZ;
+ Uint8 _rgucPad1[ 5 ];
+ Uint8 ucBatteryLevel;
+ Uint8 _rgucPad2[ 4 ];
+ Uint8 ucTrackpadCounter1;
+ Uint8 rgucTrackpadData1[ 3 ];
+ Uint8 ucTrackpadCounter2;
+ Uint8 rgucTrackpadData2[ 3 ];
+} PS4StatePacket_t;
+
+typedef struct
+{
+ Uint8 ucRumbleRight;
+ Uint8 ucRumbleLeft;
+ Uint8 ucLedRed;
+ Uint8 ucLedGreen;
+ Uint8 ucLedBlue;
+ Uint8 ucLedDelayOn;
+ Uint8 ucLedDelayOff;
+ Uint8 _rgucPad0[ 8 ];
+ Uint8 ucVolumeLeft;
+ Uint8 ucVolumeRight;
+ Uint8 ucVolumeMic;
+ Uint8 ucVolumeSpeaker;
+} DS4EffectsState_t;
+
+typedef struct {
+ SDL_bool is_dongle;
+ SDL_bool is_bluetooth;
+ SDL_bool audio_supported;
+ Uint8 volume;
+ Uint32 last_volume_check;
+ Uint32 rumble_expiration;
+ PS4StatePacket_t last_state;
+} SDL_DriverPS4_Context;
+
+
+/* Public domain CRC implementation adapted from:
+ http://home.thep.lu.se/~bjorn/crc/crc32_simple.c
+*/
+static Uint32 crc32_for_byte(Uint32 r)
+{
+ int i;
+ for(i = 0; i < 8; ++i) {
+ r = (r & 1? 0: (Uint32)0xEDB88320L) ^ r >> 1;
+ }
+ return r ^ (Uint32)0xFF000000L;
+}
+
+static Uint32 crc32(Uint32 crc, const void *data, int count)
+{
+ int i;
+ for(i = 0; i < count; ++i) {
+ crc = crc32_for_byte((Uint8)crc ^ ((const Uint8*)data)[i]) ^ crc >> 8;
+ }
+ return crc;
+}
+
+#ifdef __WIN32__
+#include "../../core/windows/SDL_windows.h"
+
+/* Define Vista for the Audio related includes below to work */
+#undef NTDDI_VERSION
+#define NTDDI_VERSION NTDDI_VISTA
+#undef _WIN32_WINNT
+#define _WIN32_WINNT _WIN32_WINNT_VISTA
+#define COBJMACROS
+#include
+#include
+#include
+
+#undef DEFINE_GUID
+#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}}
+DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E);
+DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6);
+DEFINE_GUID(IID_IAudioEndpointVolume, 0x5CDF2C82, 0x841E, 0x4546, 0x97, 0x22, 0x0C, 0xF7, 0x40, 0x78, 0x22, 0x9A);
+#endif
+
+
+
+static float GetSystemVolume(void)
+{
+ float volume = -1.0f; /* Return this if we can't get system volume */
+
+#ifdef __WIN32__
+ HRESULT hr = WIN_CoInitialize();
+ if (SUCCEEDED(hr)) {
+ IMMDeviceEnumerator *pEnumerator;
+
+ /* This should gracefully fail on XP and succeed on everything Vista and above */
+ hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (LPVOID*)&pEnumerator);
+ if (SUCCEEDED(hr)) {
+ IMMDevice *pDevice;
+
+ hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eRender, eConsole, &pDevice);
+ if (SUCCEEDED(hr)) {
+ IAudioEndpointVolume *pEndpointVolume;
+
+ hr = IMMDevice_Activate(pDevice, &IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (LPVOID*)&pEndpointVolume);
+ if (SUCCEEDED(hr)) {
+ IAudioEndpointVolume_GetMasterVolumeLevelScalar(pEndpointVolume, &volume);
+ IUnknown_Release(pEndpointVolume);
+ }
+ IUnknown_Release(pDevice);
+ }
+ IUnknown_Release(pEnumerator);
+ }
+ WIN_CoUninitialize();
+ }
+#endif /* __WIN32__ */
+
+ return volume;
+}
+
+static uint8_t GetPlaystationVolumeFromFloat(float fVolume)
+{
+ const int k_nVolumeFitRatio = 15;
+ const int k_nVolumeFitOffset = 9;
+ float fVolLog;
+
+ if (fVolume > 1.0f || fVolume < 0.0f) {
+ fVolume = 0.30f;
+ }
+ fVolLog = SDL_logf(fVolume * 100);
+
+ return (Uint8)((fVolLog * k_nVolumeFitRatio) + k_nVolumeFitOffset);
+}
+
+static SDL_bool
+HIDAPI_DriverPS4_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, int interface_number, Uint16 usage_page, Uint16 usage)
+{
+ /* The Revolution Pro Controller exposes multiple interfaces on Windows */
+ const Uint16 NACON_USB_VID = 0x146b;
+ if (vendor_id == NACON_USB_VID && usage_page != 0 && usage_page != 1) {
+ return SDL_FALSE;
+ }
+
+ return SDL_IsJoystickPS4(vendor_id, product_id);
+}
+
+static const char *
+HIDAPI_DriverPS4_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
+{
+ if (vendor_id == SONY_USB_VID) {
+ return "PS4 Controller";
+ }
+ return NULL;
+}
+
+static SDL_bool ReadFeatureReport(hid_device *dev, Uint8 report_id, Uint8 *data, size_t size)
+{
+ Uint8 report[USB_PACKET_LENGTH + 1];
+
+ SDL_memset(report, 0, sizeof(report));
+ report[0] = report_id;
+ if (hid_get_feature_report(dev, report, sizeof(report)) < 0) {
+ return SDL_FALSE;
+ }
+ SDL_memcpy(data, report, SDL_min(size, sizeof(report)));
+ return SDL_TRUE;
+}
+
+static SDL_bool CheckUSBConnected(hid_device *dev)
+{
+ int i;
+ Uint8 data[16];
+
+ /* This will fail if we're on Bluetooth */
+ if (ReadFeatureReport(dev, k_ePS4FeatureReportIdSerialNumber, data, sizeof(data))) {
+ for (i = 0; i < sizeof(data); ++i) {
+ if (data[i] != 0x00) {
+ return SDL_TRUE;
+ }
+ }
+ /* Maybe the dongle without a connected controller? */
+ }
+ return SDL_FALSE;
+}
+
+static int HIDAPI_DriverPS4_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
+
+static SDL_bool
+HIDAPI_DriverPS4_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
+{
+ SDL_DriverPS4_Context *ctx;
+
+ ctx = (SDL_DriverPS4_Context *)SDL_calloc(1, sizeof(*ctx));
+ if (!ctx) {
+ SDL_OutOfMemory();
+ return SDL_FALSE;
+ }
+ *context = ctx;
+
+ /* Check for type of connection */
+ ctx->is_dongle = (vendor_id == SONY_USB_VID && product_id == SONY_DS4_DONGLE_PID);
+ if (ctx->is_dongle) {
+ ctx->is_bluetooth = SDL_FALSE;
+ } else if (vendor_id == SONY_USB_VID) {
+ ctx->is_bluetooth = !CheckUSBConnected(dev);
+ } else {
+ /* Third party controllers appear to all be wired */
+ ctx->is_bluetooth = SDL_FALSE;
+ }
+#ifdef DEBUG_PS4
+ SDL_Log("PS4 dongle = %s, bluetooth = %s\n", ctx->is_dongle ? "TRUE" : "FALSE", ctx->is_bluetooth ? "TRUE" : "FALSE");
+#endif
+
+ /* Check to see if audio is supported */
+ if (vendor_id == SONY_USB_VID &&
+ (product_id == SONY_DS4_SLIM_PID || product_id == SONY_DS4_DONGLE_PID )) {
+ ctx->audio_supported = SDL_TRUE;
+ }
+
+ /* Initialize LED and effect state */
+ HIDAPI_DriverPS4_Rumble(joystick, dev, ctx, 0, 0, 0);
+
+ /* Initialize the joystick capabilities */
+ joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
+ joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
+ joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
+
+ return SDL_TRUE;
+}
+
+static int
+HIDAPI_DriverPS4_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context;
+ DS4EffectsState_t *effects;
+ Uint8 data[78];
+ int report_size, offset;
+
+ /* In order to send rumble, we have to send a complete effect packet */
+ SDL_memset(data, 0, sizeof(data));
+
+ if (ctx->is_bluetooth) {
+ data[0] = k_EPS4ReportIdBluetoothEffects;
+ data[1] = 0xC0 | 0x04; /* Magic value HID + CRC, also sets interval to 4ms for samples */
+ data[3] = 0x03; /* 0x1 is rumble, 0x2 is lightbar, 0x4 is the blink interval */
+
+ report_size = 78;
+ offset = 6;
+ } else {
+ data[0] = k_EPS4ReportIdUsbEffects;
+ data[1] = 0x07; /* Magic value */
+
+ report_size = 32;
+ offset = 4;
+ }
+ effects = (DS4EffectsState_t *)&data[offset];
+
+ effects->ucRumbleLeft = (low_frequency_rumble >> 8);
+ effects->ucRumbleRight = (high_frequency_rumble >> 8);
+
+ effects->ucLedRed = 0;
+ effects->ucLedGreen = 0;
+ effects->ucLedBlue = 80;
+
+ if (ctx->audio_supported) {
+ Uint32 now = SDL_GetTicks();
+ if (!ctx->last_volume_check ||
+ SDL_TICKS_PASSED(now, ctx->last_volume_check + VOLUME_CHECK_INTERVAL_MS)) {
+ ctx->volume = GetPlaystationVolumeFromFloat(GetSystemVolume());
+ ctx->last_volume_check = now;
+ }
+
+ effects->ucVolumeRight = ctx->volume;
+ effects->ucVolumeLeft = ctx->volume;
+ effects->ucVolumeSpeaker = ctx->volume;
+ effects->ucVolumeMic = 0xFF;
+ }
+
+ if (ctx->is_bluetooth) {
+ /* Bluetooth reports need a CRC at the end of the packet (at least on Linux) */
+ Uint8 ubHdr = 0xA2; /* hidp header is part of the CRC calculation */
+ Uint32 unCRC;
+ unCRC = crc32(0, &ubHdr, 1);
+ unCRC = crc32(unCRC, data, (Uint32)(report_size - sizeof(unCRC)));
+ SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC));
+ }
+
+ if (hid_write(dev, data, report_size) != report_size) {
+ return SDL_SetError("Couldn't send rumble packet");
+ }
+
+ if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
+ ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
+ } else {
+ ctx->rumble_expiration = 0;
+ }
+ return 0;
+}
+
+static void
+HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS4_Context *ctx, PS4StatePacket_t *packet)
+{
+ Sint16 axis;
+
+ if (ctx->last_state.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) {
+ {
+ Uint8 data = (packet->rgucButtonsHatAndCounter[0] >> 4);
+
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+ }
+ {
+ Uint8 data = (packet->rgucButtonsHatAndCounter[0] & 0x0F);
+ SDL_bool dpad_up = SDL_FALSE;
+ SDL_bool dpad_down = SDL_FALSE;
+ SDL_bool dpad_left = SDL_FALSE;
+ SDL_bool dpad_right = SDL_FALSE;
+
+ switch (data) {
+ case 0:
+ dpad_up = SDL_TRUE;
+ break;
+ case 1:
+ dpad_up = SDL_TRUE;
+ dpad_right = SDL_TRUE;
+ break;
+ case 2:
+ dpad_right = SDL_TRUE;
+ break;
+ case 3:
+ dpad_right = SDL_TRUE;
+ dpad_down = SDL_TRUE;
+ break;
+ case 4:
+ dpad_down = SDL_TRUE;
+ break;
+ case 5:
+ dpad_left = SDL_TRUE;
+ dpad_down = SDL_TRUE;
+ break;
+ case 6:
+ dpad_left = SDL_TRUE;
+ break;
+ case 7:
+ dpad_up = SDL_TRUE;
+ dpad_left = SDL_TRUE;
+ break;
+ default:
+ break;
+ }
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
+ }
+ }
+
+ if (ctx->last_state.rgucButtonsHatAndCounter[1] != packet->rgucButtonsHatAndCounter[1]) {
+ Uint8 data = packet->rgucButtonsHatAndCounter[1];
+
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED);
+ }
+
+ if (ctx->last_state.rgucButtonsHatAndCounter[2] != packet->rgucButtonsHatAndCounter[2]) {
+ Uint8 data = (packet->rgucButtonsHatAndCounter[2] & 0x03);
+
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+ }
+
+ axis = ((int)packet->ucTriggerLeft * 257) - 32768;
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
+ axis = ((int)packet->ucTriggerRight * 257) - 32768;
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
+ axis = ((int)packet->ucLeftJoystickX * 257) - 32768;
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
+ axis = ((int)packet->ucLeftJoystickY * 257) - 32768;
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
+ axis = ((int)packet->ucRightJoystickX * 257) - 32768;
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
+ axis = ((int)packet->ucRightJoystickY * 257) - 32768;
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
+
+ if (packet->ucBatteryLevel & 0x10) {
+ joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
+ } else {
+ /* Battery level ranges from 0 to 10 */
+ int level = (packet->ucBatteryLevel & 0xF);
+ if (level == 0) {
+ joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
+ } else if (level <= 2) {
+ joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
+ } else if (level <= 7) {
+ joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
+ } else {
+ joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
+ }
+ }
+
+ SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state));
+}
+
+static void
+HIDAPI_DriverPS4_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+ SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context;
+ Uint8 data[USB_PACKET_LENGTH];
+ int size;
+
+ while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
+ switch (data[0]) {
+ case k_EPS4ReportIdUsbState:
+ HIDAPI_DriverPS4_HandleStatePacket(joystick, dev, ctx, (PS4StatePacket_t *)&data[1]);
+ break;
+ case k_EPS4ReportIdBluetoothState:
+ /* Bluetooth state packets have two additional bytes at the beginning */
+ HIDAPI_DriverPS4_HandleStatePacket(joystick, dev, ctx, (PS4StatePacket_t *)&data[3]);
+ break;
+ default:
+#ifdef DEBUG_JOYSTICK
+ SDL_Log("Unknown PS4 packet: 0x%.2x\n", data[0]);
+#endif
+ break;
+ }
+ }
+
+ if (ctx->rumble_expiration) {
+ Uint32 now = SDL_GetTicks();
+ if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
+ HIDAPI_DriverPS4_Rumble(joystick, dev, context, 0, 0, 0);
+ }
+ }
+}
+
+static void
+HIDAPI_DriverPS4_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+ SDL_free(context);
+}
+
+SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 =
+{
+ SDL_HINT_JOYSTICK_HIDAPI_PS4,
+ SDL_TRUE,
+ HIDAPI_DriverPS4_IsSupportedDevice,
+ HIDAPI_DriverPS4_GetDeviceName,
+ HIDAPI_DriverPS4_Init,
+ HIDAPI_DriverPS4_Rumble,
+ HIDAPI_DriverPS4_Update,
+ HIDAPI_DriverPS4_Quit
+};
+
+#endif /* SDL_JOYSTICK_HIDAPI_PS4 */
+
+#endif /* SDL_JOYSTICK_HIDAPI */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c
new file mode 100644
index 000000000..4726949df
--- /dev/null
+++ b/src/joystick/hidapi/SDL_hidapi_switch.c
@@ -0,0 +1,899 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2018 Sam Lantinga
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#include "SDL_hints.h"
+#include "SDL_log.h"
+#include "SDL_events.h"
+#include "SDL_timer.h"
+#include "SDL_joystick.h"
+#include "SDL_gamecontroller.h"
+#include "../SDL_sysjoystick.h"
+#include "SDL_hidapijoystick_c.h"
+
+
+#ifdef SDL_JOYSTICK_HIDAPI_SWITCH
+
+typedef enum {
+ k_eSwitchInputReportIDs_SubcommandReply = 0x21,
+ k_eSwitchInputReportIDs_FullControllerState = 0x30,
+ k_eSwitchInputReportIDs_SimpleControllerState = 0x3F,
+ k_eSwitchInputReportIDs_CommandAck = 0x81,
+} ESwitchInputReportIDs;
+
+typedef enum {
+ k_eSwitchOutputReportIDs_RumbleAndSubcommand = 0x01,
+ k_eSwitchOutputReportIDs_Rumble = 0x10,
+ k_eSwitchOutputReportIDs_Proprietary = 0x80,
+} ESwitchOutputReportIDs;
+
+typedef enum {
+ k_eSwitchSubcommandIDs_BluetoothManualPair = 0x01,
+ k_eSwitchSubcommandIDs_RequestDeviceInfo = 0x02,
+ k_eSwitchSubcommandIDs_SetInputReportMode = 0x03,
+ k_eSwitchSubcommandIDs_SetHCIState = 0x06,
+ k_eSwitchSubcommandIDs_SPIFlashRead = 0x10,
+ k_eSwitchSubcommandIDs_SetPlayerLights = 0x30,
+ k_eSwitchSubcommandIDs_SetHomeLight = 0x38,
+ k_eSwitchSubcommandIDs_EnableIMU = 0x40,
+ k_eSwitchSubcommandIDs_SetIMUSensitivity = 0x41,
+ k_eSwitchSubcommandIDs_EnableVibration = 0x48,
+} ESwitchSubcommandIDs;
+
+typedef enum {
+ k_eSwitchProprietaryCommandIDs_Handshake = 0x02,
+ k_eSwitchProprietaryCommandIDs_HighSpeed = 0x03,
+ k_eSwitchProprietaryCommandIDs_ForceUSB = 0x04,
+ k_eSwitchProprietaryCommandIDs_ClearUSB = 0x05,
+ k_eSwitchProprietaryCommandIDs_ResetMCU = 0x06,
+} ESwitchProprietaryCommandIDs;
+
+typedef enum {
+ k_eSwitchDeviceInfoControllerType_JoyConLeft = 0x1,
+ k_eSwitchDeviceInfoControllerType_JoyConRight = 0x2,
+ k_eSwitchDeviceInfoControllerType_ProController = 0x3,
+} ESwitchDeviceInfoControllerType;
+
+#define k_unSwitchOutputPacketDataLength 49
+#define k_unSwitchMaxOutputPacketLength 64
+#define k_unSwitchBluetoothPacketLength k_unSwitchOutputPacketDataLength
+#define k_unSwitchUSBPacketLength k_unSwitchMaxOutputPacketLength
+
+#define k_unSPIStickCalibrationStartOffset 0x603D
+#define k_unSPIStickCalibrationEndOffset 0x604E
+#define k_unSPIStickCalibrationLength (k_unSPIStickCalibrationEndOffset - k_unSPIStickCalibrationStartOffset + 1)
+
+#pragma pack(1)
+typedef struct
+{
+ Uint8 rgucButtons[2];
+ Uint8 ucStickHat;
+ Sint16 sJoystickLeft[2];
+ Sint16 sJoystickRight[2];
+} SwitchSimpleStatePacket_t;
+
+typedef struct
+{
+ Uint8 ucCounter;
+ Uint8 ucBatteryAndConnection;
+ Uint8 rgucButtons[3];
+ Uint8 rgucJoystickLeft[3];
+ Uint8 rgucJoystickRight[3];
+ Uint8 ucVibrationCode;
+} SwitchControllerStatePacket_t;
+
+typedef struct
+{
+ SwitchControllerStatePacket_t controllerState;
+
+ struct {
+ Sint16 sAccelX;
+ Sint16 sAccelY;
+ Sint16 sAccelZ;
+
+ Sint16 sGyroX;
+ Sint16 sGyroY;
+ Sint16 sGyroZ;
+ } imuState[3];
+} SwitchStatePacket_t;
+
+typedef struct
+{
+ Uint32 unAddress;
+ Uint8 ucLength;
+} SwitchSPIOpData_t;
+
+typedef struct
+{
+ SwitchControllerStatePacket_t m_controllerState;
+
+ Uint8 ucSubcommandAck;
+ Uint8 ucSubcommandID;
+
+ #define k_unSubcommandDataBytes 35
+ union {
+ Uint8 rgucSubcommandData[ k_unSubcommandDataBytes ];
+
+ struct {
+ SwitchSPIOpData_t opData;
+ Uint8 rgucReadData[ k_unSubcommandDataBytes - sizeof(SwitchSPIOpData_t) ];
+ } spiReadData;
+
+ struct {
+ Uint8 rgucFirmwareVersion[2];
+ Uint8 ucDeviceType;
+ Uint8 ucFiller1;
+ Uint8 rgucMACAddress[6];
+ Uint8 ucFiller2;
+ Uint8 ucColorLocation;
+ } deviceInfo;
+ };
+} SwitchSubcommandInputPacket_t;
+
+typedef struct
+{
+ Uint8 rgucData[4];
+} SwitchRumbleData_t;
+
+typedef struct
+{
+ Uint8 ucPacketType;
+ Uint8 ucPacketNumber;
+ SwitchRumbleData_t rumbleData[2];
+} SwitchCommonOutputPacket_t;
+
+typedef struct
+{
+ SwitchCommonOutputPacket_t commonData;
+
+ Uint8 ucSubcommandID;
+ Uint8 rgucSubcommandData[ k_unSwitchOutputPacketDataLength - sizeof(SwitchCommonOutputPacket_t) - 1 ];
+} SwitchSubcommandOutputPacket_t;
+
+typedef struct
+{
+ Uint8 ucPacketType;
+ Uint8 ucProprietaryID;
+
+ Uint8 rgucProprietaryData[ k_unSwitchOutputPacketDataLength - 1 - 1 ];
+} SwitchProprietaryOutputPacket_t;
+#pragma pack()
+
+typedef struct {
+ hid_device *dev;
+ SDL_bool m_bIsUsingBluetooth;
+ Uint8 m_nCommandNumber;
+ SwitchCommonOutputPacket_t m_RumblePacket;
+ Uint32 m_nRumbleExpiration;
+ Uint8 m_rgucReadBuffer[k_unSwitchMaxOutputPacketLength];
+ SwitchSimpleStatePacket_t m_lastSimpleState;
+ SwitchStatePacket_t m_lastFullState;
+
+ struct StickCalibrationData {
+ struct {
+ Sint16 sCenter;
+ Sint16 sMin;
+ Sint16 sMax;
+ } axis[2];
+ } m_StickCalData[2];
+
+ struct StickExtents {
+ struct {
+ Sint16 sMin;
+ Sint16 sMax;
+ } axis[2];
+ } m_StickExtents[2];
+} SDL_DriverSwitch_Context;
+
+
+static SDL_bool
+HIDAPI_DriverSwitch_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, int interface_number, Uint16 usage_page, Uint16 usage)
+{
+ return SDL_IsJoystickNintendoSwitchPro(vendor_id, product_id);
+}
+
+static const char *
+HIDAPI_DriverSwitch_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
+{
+ /* Give a user friendly name for this controller */
+ if (SDL_IsJoystickNintendoSwitchPro(vendor_id, product_id)) {
+ return "Nintendo Switch Pro Controller";
+ }
+ return NULL;
+}
+
+static int ReadInput(SDL_DriverSwitch_Context *ctx)
+{
+ return hid_read_timeout(ctx->dev, ctx->m_rgucReadBuffer, sizeof(ctx->m_rgucReadBuffer), 0);
+}
+
+static int WriteOutput(SDL_DriverSwitch_Context *ctx, Uint8 *data, int size)
+{
+ return hid_write(ctx->dev, data, size);
+}
+
+static SwitchSubcommandInputPacket_t *ReadSubcommandReply(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs expectedID)
+{
+ /* Average response time for messages is ~30ms */
+ Uint32 TimeoutMs = 100;
+ Uint32 startTicks = SDL_GetTicks();
+
+ int nRead = 0;
+ while ((nRead = ReadInput(ctx)) != -1) {
+ if (nRead > 0) {
+ if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) {
+ SwitchSubcommandInputPacket_t *reply = (SwitchSubcommandInputPacket_t *)&ctx->m_rgucReadBuffer[ 1 ];
+ if (reply->ucSubcommandID == expectedID && (reply->ucSubcommandAck & 0x80)) {
+ return reply;
+ }
+ }
+ } else {
+ SDL_Delay(1);
+ }
+
+ if (SDL_TICKS_PASSED(SDL_GetTicks(), startTicks + TimeoutMs)) {
+ break;
+ }
+ }
+ return NULL;
+}
+
+static SDL_bool ReadProprietaryReply(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs expectedID)
+{
+ /* Average response time for messages is ~30ms */
+ Uint32 TimeoutMs = 100;
+ Uint32 startTicks = SDL_GetTicks();
+
+ int nRead = 0;
+ while ((nRead = ReadInput(ctx)) != -1) {
+ if (nRead > 0) {
+ if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_CommandAck && ctx->m_rgucReadBuffer[ 1 ] == expectedID) {
+ return SDL_TRUE;
+ }
+ } else {
+ SDL_Delay(1);
+ }
+
+ if (SDL_TICKS_PASSED(SDL_GetTicks(), startTicks + TimeoutMs)) {
+ break;
+ }
+ }
+ return SDL_FALSE;
+}
+
+static void ConstructSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandOutputPacket_t *outPacket)
+{
+ SDL_memset(outPacket, 0, sizeof(*outPacket));
+
+ outPacket->commonData.ucPacketType = k_eSwitchOutputReportIDs_RumbleAndSubcommand;
+ outPacket->commonData.ucPacketNumber = ctx->m_nCommandNumber;
+
+ SDL_memcpy(&outPacket->commonData.rumbleData, &ctx->m_RumblePacket.rumbleData, sizeof(ctx->m_RumblePacket.rumbleData));
+
+ outPacket->ucSubcommandID = ucCommandID;
+ SDL_memcpy(outPacket->rgucSubcommandData, pBuf, ucLen);
+
+ ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
+}
+
+static SDL_bool WritePacket(SDL_DriverSwitch_Context *ctx, void *pBuf, Uint8 ucLen)
+{
+ Uint8 rgucBuf[k_unSwitchMaxOutputPacketLength];
+ const size_t unWriteSize = ctx->m_bIsUsingBluetooth ? k_unSwitchBluetoothPacketLength : k_unSwitchUSBPacketLength;
+
+ if (ucLen > k_unSwitchOutputPacketDataLength) {
+ return SDL_FALSE;
+ }
+
+ if (ucLen < unWriteSize) {
+ SDL_memcpy(rgucBuf, pBuf, ucLen);
+ SDL_memset(rgucBuf+ucLen, 0, unWriteSize-ucLen);
+ pBuf = rgucBuf;
+ ucLen = (Uint8)unWriteSize;
+ }
+ return (WriteOutput(ctx, (Uint8 *)pBuf, ucLen) >= 0);
+}
+
+static SDL_bool WriteSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandInputPacket_t **ppReply)
+{
+ int nRetries = 5;
+ SwitchSubcommandInputPacket_t *reply = NULL;
+
+ while (!reply && nRetries--) {
+ SwitchSubcommandOutputPacket_t commandPacket;
+ ConstructSubcommand(ctx, ucCommandID, pBuf, ucLen, &commandPacket);
+
+ if (!WritePacket(ctx, &commandPacket, sizeof(commandPacket))) {
+ continue;
+ }
+
+ reply = ReadSubcommandReply(ctx, ucCommandID);
+ }
+
+ if (ppReply) {
+ *ppReply = reply;
+ }
+ return reply != NULL;
+}
+
+static SDL_bool WriteProprietary(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs ucCommand, Uint8 *pBuf, Uint8 ucLen, SDL_bool waitForReply)
+{
+ int nRetries = 5;
+
+ while (nRetries--) {
+ SwitchProprietaryOutputPacket_t packet;
+
+ if ((!pBuf && ucLen > 0) || ucLen > sizeof(packet.rgucProprietaryData)) {
+ return SDL_FALSE;
+ }
+
+ packet.ucPacketType = k_eSwitchOutputReportIDs_Proprietary;
+ packet.ucProprietaryID = ucCommand;
+ SDL_memcpy(packet.rgucProprietaryData, pBuf, ucLen);
+
+ if (!WritePacket(ctx, &packet, sizeof(packet))) {
+ continue;
+ }
+
+ if (!waitForReply || ReadProprietaryReply(ctx, ucCommand)) {
+ return SDL_TRUE;
+ }
+ }
+ return SDL_FALSE;
+}
+
+static void SetNeutralRumble(SwitchRumbleData_t *pRumble)
+{
+ pRumble->rgucData[0] = 0x00;
+ pRumble->rgucData[1] = 0x01;
+ pRumble->rgucData[2] = 0x40;
+ pRumble->rgucData[3] = 0x40;
+}
+
+static void EncodeRumble(SwitchRumbleData_t *pRumble, Uint16 usHighFreq, Uint8 ucHighFreqAmp, Uint8 ucLowFreq, Uint16 usLowFreqAmp)
+{
+ if (ucHighFreqAmp > 0 || usLowFreqAmp > 0) {
+ // High-band frequency and low-band amplitude are actually nine-bits each so they
+ // take a bit from the high-band amplitude and low-band frequency bytes respectively
+ pRumble->rgucData[0] = usHighFreq & 0xFF;
+ pRumble->rgucData[1] = ucHighFreqAmp | ((usHighFreq >> 8) & 0x01);
+
+ pRumble->rgucData[2] = ucLowFreq | ((usLowFreqAmp >> 8) & 0x80);
+ pRumble->rgucData[3] = usLowFreqAmp & 0xFF;
+
+#ifdef DEBUG_RUMBLE
+ SDL_Log("Freq: %.2X %.2X %.2X, Amp: %.2X %.2X %.2X\n",
+ usHighFreq & 0xFF, ((usHighFreq >> 8) & 0x01), ucLowFreq,
+ ucHighFreqAmp, ((usLowFreqAmp >> 8) & 0x80), usLowFreqAmp & 0xFF);
+#endif
+ } else {
+ SetNeutralRumble(pRumble);
+ }
+}
+
+static SDL_bool WriteRumble(SDL_DriverSwitch_Context *ctx)
+{
+ /* Write into m_RumblePacket rather than a temporary buffer to allow the current rumble state
+ * to be retained for subsequent rumble or subcommand packets sent to the controller
+ */
+ ctx->m_RumblePacket.ucPacketType = k_eSwitchOutputReportIDs_Rumble;
+ ctx->m_RumblePacket.ucPacketNumber = ctx->m_nCommandNumber;
+ ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
+
+ return WritePacket(ctx, (Uint8 *)&ctx->m_RumblePacket, sizeof(ctx->m_RumblePacket));
+}
+
+static SDL_bool BTrySetupUSB(SDL_DriverSwitch_Context *ctx)
+{
+ /* We have to send a connection handshake to the controller when communicating over USB
+ * before we're able to send it other commands. Luckily this command is not supported
+ * over Bluetooth, so we can use the controller's lack of response as a way to
+ * determine if the connection is over USB or Bluetooth
+ */
+ if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, SDL_TRUE)) {
+ return SDL_FALSE;
+ }
+ if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_HighSpeed, NULL, 0, SDL_TRUE)) {
+ return SDL_FALSE;
+ }
+ if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, SDL_TRUE)) {
+ return SDL_FALSE;
+ }
+ return SDL_TRUE;
+}
+
+static SDL_bool SetVibrationEnabled(SDL_DriverSwitch_Context *ctx, Uint8 enabled)
+{
+ return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_EnableVibration, &enabled, sizeof(enabled), NULL);
+
+}
+static SDL_bool SetInputMode(SDL_DriverSwitch_Context *ctx, Uint8 input_mode)
+{
+ return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetInputReportMode, &input_mode, 1, NULL);
+}
+
+static SDL_bool SetHomeLED(SDL_DriverSwitch_Context *ctx, Uint8 brightness)
+{
+ Uint8 ucLedIntensity = 0;
+ Uint8 rgucBuffer[4];
+
+ if (brightness > 0) {
+ if (brightness < 65) {
+ ucLedIntensity = (brightness + 5) / 10;
+ } else {
+ ucLedIntensity = (Uint8)SDL_ceilf(0xF * SDL_powf((float)brightness / 100.f, 2.13f));
+ }
+ }
+
+ rgucBuffer[0] = (0x0 << 4) | 0x1; /* 0 mini cycles (besides first), cycle duration 8ms */
+ rgucBuffer[1] = ((ucLedIntensity & 0xF) << 4) | 0x0; /* LED start intensity (0x0-0xF), 0 cycles (LED stays on at start intensity after first cycle) */
+ rgucBuffer[2] = ((ucLedIntensity & 0xF) << 4) | 0x0; /* First cycle LED intensity, 0x0 intensity for second cycle */
+ rgucBuffer[3] = (0x0 << 4) | 0x0; /* 8ms fade transition to first cycle, 8ms first cycle LED duration */
+
+ return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetHomeLight, rgucBuffer, sizeof(rgucBuffer), NULL);
+}
+
+static SDL_bool SetSlotLED(SDL_DriverSwitch_Context *ctx, Uint8 slot)
+{
+ Uint8 led_data = (1 << slot);
+ return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetPlayerLights, &led_data, sizeof(led_data), NULL);
+}
+
+static SDL_bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)
+{
+ Uint8 *pStickCal;
+ size_t stick, axis;
+ SwitchSubcommandInputPacket_t *reply = NULL;
+
+ /* Read Calibration Info */
+ SwitchSPIOpData_t readParams;
+ readParams.unAddress = k_unSPIStickCalibrationStartOffset;
+ readParams.ucLength = k_unSPIStickCalibrationLength;
+
+ if (!WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply)) {
+ return SDL_FALSE;
+ }
+
+ /* Stick calibration values are 12-bits each and are packed by bit
+ * For whatever reason the fields are in a different order for each stick
+ * Left: X-Max, Y-Max, X-Center, Y-Center, X-Min, Y-Min
+ * Right: X-Center, Y-Center, X-Min, Y-Min, X-Max, Y-Max
+ */
+ pStickCal = reply->spiReadData.rgucReadData;
+
+ /* Left stick */
+ ctx->m_StickCalData[0].axis[0].sMax = ((pStickCal[1] << 8) & 0xF00) | pStickCal[0]; /* X Axis max above center */
+ ctx->m_StickCalData[0].axis[1].sMax = (pStickCal[2] << 4) | (pStickCal[1] >> 4); /* Y Axis max above center */
+ ctx->m_StickCalData[0].axis[0].sCenter = ((pStickCal[4] << 8) & 0xF00) | pStickCal[3]; /* X Axis center */
+ ctx->m_StickCalData[0].axis[1].sCenter = (pStickCal[5] << 4) | (pStickCal[4] >> 4); /* Y Axis center */
+ ctx->m_StickCalData[0].axis[0].sMin = ((pStickCal[7] << 8) & 0xF00) | pStickCal[6]; /* X Axis min below center */
+ ctx->m_StickCalData[0].axis[1].sMin = (pStickCal[8] << 4) | (pStickCal[7] >> 4); /* Y Axis min below center */
+
+ /* Right stick */
+ ctx->m_StickCalData[1].axis[0].sCenter = ((pStickCal[10] << 8) & 0xF00) | pStickCal[9]; /* X Axis center */
+ ctx->m_StickCalData[1].axis[1].sCenter = (pStickCal[11] << 4) | (pStickCal[10] >> 4); /* Y Axis center */
+ ctx->m_StickCalData[1].axis[0].sMin = ((pStickCal[13] << 8) & 0xF00) | pStickCal[12]; /* X Axis min below center */
+ ctx->m_StickCalData[1].axis[1].sMin = (pStickCal[14] << 4) | (pStickCal[13] >> 4); /* Y Axis min below center */
+ ctx->m_StickCalData[1].axis[0].sMax = ((pStickCal[16] << 8) & 0xF00) | pStickCal[15]; /* X Axis max above center */
+ ctx->m_StickCalData[1].axis[1].sMax = (pStickCal[17] << 4) | (pStickCal[16] >> 4); /* Y Axis max above center */
+
+ /* Filter out any values that were uninitialized (0xFFF) in the SPI read */
+ for (stick = 0; stick < 2; ++stick) {
+ for (axis = 0; axis < 2; ++axis) {
+ if (ctx->m_StickCalData[stick].axis[axis].sCenter == 0xFFF) {
+ ctx->m_StickCalData[stick].axis[axis].sCenter = 0;
+ }
+ if (ctx->m_StickCalData[stick].axis[axis].sMax == 0xFFF) {
+ ctx->m_StickCalData[stick].axis[axis].sMax = 0;
+ }
+ if (ctx->m_StickCalData[stick].axis[axis].sMin == 0xFFF) {
+ ctx->m_StickCalData[stick].axis[axis].sMin = 0;
+ }
+ }
+ }
+
+ if (ctx->m_bIsUsingBluetooth) {
+ for (stick = 0; stick < 2; ++stick) {
+ for(axis = 0; axis < 2; ++axis) {
+ ctx->m_StickExtents[stick].axis[axis].sMin = (Sint16)(SDL_MIN_SINT16 * 0.5f);
+ ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(SDL_MAX_SINT16 * 0.5f);
+ }
+ }
+ } else {
+ for (stick = 0; stick < 2; ++stick) {
+ for(axis = 0; axis < 2; ++axis) {
+ ctx->m_StickExtents[stick].axis[axis].sMin = -(Sint16)(ctx->m_StickCalData[stick].axis[axis].sMin * 0.7f);
+ ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sMax * 0.7f);
+ }
+ }
+ }
+ return SDL_TRUE;
+}
+
+static float fsel(float fComparand, float fValGE, float fLT)
+{
+ return fComparand >= 0 ? fValGE : fLT;
+}
+
+static float RemapVal(float val, float A, float B, float C, float D)
+{
+ if (A == B) {
+ return fsel(val - B , D , C);
+ }
+ return C + (D - C) * (val - A) / (B - A);
+}
+
+static Sint16 ApplyStickCalibrationCentered(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue, Sint16 sCenter)
+{
+ sRawValue -= sCenter;
+
+ if (sRawValue > ctx->m_StickExtents[nStick].axis[nAxis].sMax) {
+ ctx->m_StickExtents[nStick].axis[nAxis].sMax = sRawValue;
+ }
+ if (sRawValue < ctx->m_StickExtents[nStick].axis[nAxis].sMin) {
+ ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue;
+ }
+
+ if (sRawValue > 0) {
+ return (Sint16)(RemapVal(sRawValue, 0, ctx->m_StickExtents[nStick].axis[nAxis].sMax, 0, SDL_MAX_SINT16));
+ } else {
+ return (Sint16)(RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, 0, SDL_MIN_SINT16, 0));
+ }
+}
+
+static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue)
+{
+ return ApplyStickCalibrationCentered(ctx, nStick, nAxis, sRawValue, ctx->m_StickCalData[nStick].axis[nAxis].sCenter);
+}
+
+static SDL_bool
+HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
+{
+ SDL_DriverSwitch_Context *ctx;
+ Uint8 input_mode;
+
+ ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx));
+ if (!ctx) {
+ SDL_OutOfMemory();
+ return SDL_FALSE;
+ }
+ ctx->dev = dev;
+
+ *context = ctx;
+
+ /* Initialize rumble data */
+ SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
+ SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
+
+ /* Try setting up USB mode, and if that fails we're using Bluetooth */
+ if (!BTrySetupUSB(ctx)) {
+ ctx->m_bIsUsingBluetooth = SDL_TRUE;
+ }
+
+ if (!LoadStickCalibration(ctx)) {
+ SDL_SetError("Couldn't load stick calibration");
+ SDL_free(ctx);
+ return SDL_FALSE;
+ }
+
+ if (!SetVibrationEnabled(ctx, 1)) {
+ SDL_SetError("Couldn't enable vibration");
+ SDL_free(ctx);
+ return SDL_FALSE;
+ }
+
+ /* Set the desired input mode */
+ if (ctx->m_bIsUsingBluetooth) {
+ input_mode = k_eSwitchInputReportIDs_SimpleControllerState;
+ } else {
+ input_mode = k_eSwitchInputReportIDs_FullControllerState;
+ }
+ if (!SetInputMode(ctx, input_mode)) {
+ SDL_SetError("Couldn't set input mode");
+ SDL_free(ctx);
+ return SDL_FALSE;
+ }
+
+ /* Start sending USB reports */
+ if (!ctx->m_bIsUsingBluetooth) {
+ /* ForceUSB doesn't generate an ACK, so don't wait for a reply */
+ if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, SDL_FALSE)) {
+ SDL_SetError("Couldn't start USB reports");
+ SDL_free(ctx);
+ return SDL_FALSE;
+ }
+ }
+
+ /* Set the LED state */
+ SetHomeLED(ctx, 100);
+ SetSlotLED(ctx, (joystick->instance_id % 4));
+
+ /* Initialize the joystick capabilities */
+ joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
+ joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
+ joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
+
+ return SDL_TRUE;
+}
+
+static int
+HIDAPI_DriverSwitch_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context;
+
+ /* Experimentally determined rumble values. These will only matter on some controllers as tested ones
+ * seem to disregard these and just use any non-zero rumble values as a binary flag for constant rumble
+ *
+ * More information about these values can be found here:
+ * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
+ */
+ const Uint16 k_usHighFreq = 0x0074;
+ const Uint8 k_ucHighFreqAmp = 0xBE;
+ const Uint8 k_ucLowFreq = 0x3D;
+ const Uint16 k_usLowFreqAmp = 0x806F;
+
+ if (low_frequency_rumble) {
+ EncodeRumble(&ctx->m_RumblePacket.rumbleData[0], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
+ } else {
+ SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
+ }
+
+ if (high_frequency_rumble) {
+ EncodeRumble(&ctx->m_RumblePacket.rumbleData[1], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
+ } else {
+ SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
+ }
+
+ if (!WriteRumble(ctx)) {
+ SDL_SetError("Couldn't send rumble packet");
+ return -1;
+ }
+
+ if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
+ ctx->m_nRumbleExpiration = SDL_GetTicks() + duration_ms;
+ } else {
+ ctx->m_nRumbleExpiration = 0;
+ }
+ return 0;
+}
+
+static void HandleSimpleControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
+{
+ /* 0x8000 is the neutral value for all joystick axes */
+ const Uint16 usJoystickCenter = 0x8000;
+ Sint16 axis;
+
+ if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
+ Uint8 data = packet->rgucButtons[0];
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
+
+ axis = (data & 0x40) ? 32767 : -32768;
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
+
+ axis = (data & 0x80) ? 32767 : -32768;
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
+ }
+
+ if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
+ Uint8 data = packet->rgucButtons[1];
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+ }
+
+ if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
+ SDL_bool dpad_up = SDL_FALSE;
+ SDL_bool dpad_down = SDL_FALSE;
+ SDL_bool dpad_left = SDL_FALSE;
+ SDL_bool dpad_right = SDL_FALSE;
+
+ switch (packet->ucStickHat) {
+ case 0:
+ dpad_up = SDL_TRUE;
+ break;
+ case 1:
+ dpad_up = SDL_TRUE;
+ dpad_right = SDL_TRUE;
+ break;
+ case 2:
+ dpad_right = SDL_TRUE;
+ break;
+ case 3:
+ dpad_right = SDL_TRUE;
+ dpad_down = SDL_TRUE;
+ break;
+ case 4:
+ dpad_down = SDL_TRUE;
+ break;
+ case 5:
+ dpad_left = SDL_TRUE;
+ dpad_down = SDL_TRUE;
+ break;
+ case 6:
+ dpad_left = SDL_TRUE;
+ break;
+ case 7:
+ dpad_up = SDL_TRUE;
+ dpad_left = SDL_TRUE;
+ break;
+ default:
+ break;
+ }
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
+ }
+
+ axis = ApplyStickCalibrationCentered(ctx, 0, 0, packet->sJoystickLeft[0], (Sint16)usJoystickCenter);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
+
+ axis = ApplyStickCalibrationCentered(ctx, 0, 1, packet->sJoystickLeft[1], (Sint16)usJoystickCenter);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
+
+ axis = ApplyStickCalibrationCentered(ctx, 1, 0, packet->sJoystickRight[0], (Sint16)usJoystickCenter);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
+
+ axis = ApplyStickCalibrationCentered(ctx, 1, 1, packet->sJoystickRight[1], (Sint16)usJoystickCenter);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
+
+ ctx->m_lastSimpleState = *packet;
+}
+
+static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)
+{
+ Sint16 axis;
+
+ if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) {
+ Uint8 data = packet->controllerState.rgucButtons[0];
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+ axis = (data & 0x80) ? 32767 : -32768;
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
+ }
+
+ if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
+ Uint8 data = packet->controllerState.rgucButtons[1];
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+ }
+
+ if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {
+ Uint8 data = packet->controllerState.rgucButtons[2];
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+ axis = (data & 0x80) ? 32767 : -32768;
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
+ }
+
+ axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);
+ axis = ApplyStickCalibration(ctx, 0, 0, axis);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
+
+ axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4);
+ axis = ApplyStickCalibration(ctx, 0, 1, axis);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, ~axis);
+
+ axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8);
+ axis = ApplyStickCalibration(ctx, 1, 0, axis);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
+
+ axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4);
+ axis = ApplyStickCalibration(ctx, 1, 1, axis);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, ~axis);
+
+ /* High nibble of battery/connection byte is battery level, low nibble is connection status
+ * LSB of connection nibble is USB/Switch connection status
+ */
+ if (packet->controllerState.ucBatteryAndConnection & 0x1) {
+ joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
+ } else {
+ /* LSB of the battery nibble is used to report charging.
+ * The battery level is reported from 0(empty)-8(full)
+ */
+ int level = (packet->controllerState.ucBatteryAndConnection & 0xE0) >> 4;
+ if (level == 0) {
+ joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
+ } else if (level <= 2) {
+ joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
+ } else if (level <= 6) {
+ joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
+ } else {
+ joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
+ }
+ }
+
+ ctx->m_lastFullState = *packet;
+}
+
+static void
+HIDAPI_DriverSwitch_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+ SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context;
+
+ while (ReadInput(ctx) > 0) {
+ switch (ctx->m_rgucReadBuffer[0]) {
+ case k_eSwitchInputReportIDs_SimpleControllerState:
+ HandleSimpleControllerState(joystick, ctx, (SwitchSimpleStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
+ break;
+ case k_eSwitchInputReportIDs_FullControllerState:
+ HandleFullControllerState(joystick, ctx, (SwitchStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (ctx->m_nRumbleExpiration) {
+ Uint32 now = SDL_GetTicks();
+ if (SDL_TICKS_PASSED(now, ctx->m_nRumbleExpiration)) {
+ HIDAPI_DriverSwitch_Rumble(joystick, dev, context, 0, 0, 0);
+ }
+ }
+}
+
+static void
+HIDAPI_DriverSwitch_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+ SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context;
+
+ /* Restore simple input mode for other applications */
+ SetInputMode(ctx, k_eSwitchInputReportIDs_SimpleControllerState);
+
+ SDL_free(context);
+}
+
+SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch =
+{
+ SDL_HINT_JOYSTICK_HIDAPI_SWITCH,
+ SDL_TRUE,
+ HIDAPI_DriverSwitch_IsSupportedDevice,
+ HIDAPI_DriverSwitch_GetDeviceName,
+ HIDAPI_DriverSwitch_Init,
+ HIDAPI_DriverSwitch_Rumble,
+ HIDAPI_DriverSwitch_Update,
+ HIDAPI_DriverSwitch_Quit
+};
+
+#endif /* SDL_JOYSTICK_HIDAPI_SWITCH */
+
+#endif /* SDL_JOYSTICK_HIDAPI */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360.c b/src/joystick/hidapi/SDL_hidapi_xbox360.c
new file mode 100644
index 000000000..e7e598007
--- /dev/null
+++ b/src/joystick/hidapi/SDL_hidapi_xbox360.c
@@ -0,0 +1,363 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2018 Sam Lantinga
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#include "SDL_hints.h"
+#include "SDL_log.h"
+#include "SDL_events.h"
+#include "SDL_timer.h"
+#include "SDL_joystick.h"
+#include "SDL_gamecontroller.h"
+#include "../SDL_sysjoystick.h"
+#include "SDL_hidapijoystick_c.h"
+
+
+#ifdef SDL_JOYSTICK_HIDAPI_XBOX360
+
+#define USB_PACKET_LENGTH 64
+
+typedef struct
+{
+ Uint16 vendor_id;
+ Uint16 product_id;
+ const char *name;
+} SDL_DriverXbox360_DeviceName;
+
+static const SDL_DriverXbox360_DeviceName xbox360_devicenames[] = {
+ { 0x0079, 0x18d4, "GPD Win 2 X-Box Controller" },
+ { 0x044f, 0xb326, "Thrustmaster Gamepad GP XID" },
+ { 0x045e, 0x028e, "Microsoft X-Box 360 pad" },
+ { 0x045e, 0x028f, "Microsoft X-Box 360 pad v2" },
+ { 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)" },
+ { 0x045e, 0x0719, "Xbox 360 Wireless Receiver" },
+ { 0x046d, 0xc21d, "Logitech Gamepad F310" },
+ { 0x046d, 0xc21e, "Logitech Gamepad F510" },
+ { 0x046d, 0xc21f, "Logitech Gamepad F710" },
+ { 0x046d, 0xc242, "Logitech Chillstream Controller" },
+ { 0x046d, 0xcaa3, "Logitech DriveFx Racing Wheel" },
+ { 0x056e, 0x2004, "Elecom JC-U3613M" },
+ { 0x06a3, 0xf51a, "Saitek P3600" },
+ { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller" },
+ { 0x0738, 0x4718, "Mad Catz Street Fighter IV FightStick SE" },
+ { 0x0738, 0x4726, "Mad Catz Xbox 360 Controller" },
+ { 0x0738, 0x4728, "Mad Catz Street Fighter IV FightPad" },
+ { 0x0738, 0x4736, "Mad Catz MicroCon Gamepad" },
+ { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)" },
+ { 0x0738, 0x4740, "Mad Catz Beat Pad" },
+ { 0x0738, 0x4758, "Mad Catz Arcade Game Stick" },
+ { 0x0738, 0x9871, "Mad Catz Portable Drum" },
+ { 0x0738, 0xb726, "Mad Catz Xbox controller - MW2" },
+ { 0x0738, 0xb738, "Mad Catz MVC2TE Stick 2" },
+ { 0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad" },
+ { 0x0738, 0xcb02, "Saitek Cyborg Rumble Pad - PC/Xbox 360" },
+ { 0x0738, 0xcb03, "Saitek P3200 Rumble Pad - PC/Xbox 360" },
+ { 0x0738, 0xcb29, "Saitek Aviator Stick AV8R02" },
+ { 0x0738, 0xf738, "Super SFIV FightStick TE S" },
+ { 0x07ff, 0xffff, "Mad Catz GamePad" },
+ { 0x0e6f, 0x0105, "HSM3 Xbox360 dancepad" },
+ { 0x0e6f, 0x0113, "Afterglow AX.1 Gamepad for Xbox 360" },
+ { 0x0e6f, 0x011f, "Rock Candy Gamepad Wired Controller" },
+ { 0x0e6f, 0x0131, "PDP EA Sports Controller" },
+ { 0x0e6f, 0x0133, "Xbox 360 Wired Controller" },
+ { 0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller" },
+ { 0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360" },
+ { 0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360" },
+ { 0x0e6f, 0x0301, "Logic3 Controller" },
+ { 0x0e6f, 0x0401, "Logic3 Controller" },
+ { 0x0e6f, 0x0413, "Afterglow AX.1 Gamepad for Xbox 360" },
+ { 0x0e6f, 0x0501, "PDP Xbox 360 Controller" },
+ { 0x0e6f, 0xf900, "PDP Afterglow AX.1" },
+ { 0x0f0d, 0x000a, "Hori Co. DOA4 FightStick" },
+ { 0x0f0d, 0x000c, "Hori PadEX Turbo" },
+ { 0x0f0d, 0x000d, "Hori Fighting Stick EX2" },
+ { 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX" },
+ { 0x0f0d, 0x001b, "Hori Real Arcade Pro VX" },
+ { 0x11c9, 0x55f0, "Nacon GC-100XF" },
+ { 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad" },
+ { 0x12ab, 0x0301, "PDP AFTERGLOW AX.1" },
+ { 0x12ab, 0x0303, "Mortal Kombat Klassic FightStick" },
+ { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer" },
+ { 0x1430, 0xf801, "RedOctane Controller" },
+ { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller" },
+ { 0x1532, 0x0037, "Razer Sabertooth" },
+ { 0x15e4, 0x3f00, "Power A Mini Pro Elite" },
+ { 0x15e4, 0x3f0a, "Xbox Airflo wired controller" },
+ { 0x15e4, 0x3f10, "Batarang Xbox 360 controller" },
+ { 0x162e, 0xbeef, "Joytech Neo-Se Take2" },
+ { 0x1689, 0xfd00, "Razer Onza Tournament Edition" },
+ { 0x1689, 0xfd01, "Razer Onza Classic Edition" },
+ { 0x1689, 0xfe00, "Razer Sabertooth" },
+ { 0x1bad, 0x0002, "Harmonix Rock Band Guitar" },
+ { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit" },
+ { 0x1bad, 0x0130, "Ion Drum Rocker" },
+ { 0x1bad, 0xf016, "Mad Catz Xbox 360 Controller" },
+ { 0x1bad, 0xf018, "Mad Catz Street Fighter IV SE Fighting Stick" },
+ { 0x1bad, 0xf019, "Mad Catz Brawlstick for Xbox 360" },
+ { 0x1bad, 0xf021, "Mad Cats Ghost Recon FS GamePad" },
+ { 0x1bad, 0xf023, "MLG Pro Circuit Controller (Xbox)" },
+ { 0x1bad, 0xf025, "Mad Catz Call Of Duty" },
+ { 0x1bad, 0xf027, "Mad Catz FPS Pro" },
+ { 0x1bad, 0xf028, "Street Fighter IV FightPad" },
+ { 0x1bad, 0xf02e, "Mad Catz Fightpad" },
+ { 0x1bad, 0xf030, "Mad Catz Xbox 360 MC2 MicroCon Racing Wheel" },
+ { 0x1bad, 0xf036, "Mad Catz MicroCon GamePad Pro" },
+ { 0x1bad, 0xf038, "Street Fighter IV FightStick TE" },
+ { 0x1bad, 0xf039, "Mad Catz MvC2 TE" },
+ { 0x1bad, 0xf03a, "Mad Catz SFxT Fightstick Pro" },
+ { 0x1bad, 0xf03d, "Street Fighter IV Arcade Stick TE - Chun Li" },
+ { 0x1bad, 0xf03e, "Mad Catz MLG FightStick TE" },
+ { 0x1bad, 0xf03f, "Mad Catz FightStick SoulCaliber" },
+ { 0x1bad, 0xf042, "Mad Catz FightStick TES+" },
+ { 0x1bad, 0xf080, "Mad Catz FightStick TE2" },
+ { 0x1bad, 0xf501, "HoriPad EX2 Turbo" },
+ { 0x1bad, 0xf502, "Hori Real Arcade Pro.VX SA" },
+ { 0x1bad, 0xf503, "Hori Fighting Stick VX" },
+ { 0x1bad, 0xf504, "Hori Real Arcade Pro. EX" },
+ { 0x1bad, 0xf505, "Hori Fighting Stick EX2B" },
+ { 0x1bad, 0xf506, "Hori Real Arcade Pro.EX Premium VLX" },
+ { 0x1bad, 0xf900, "Harmonix Xbox 360 Controller" },
+ { 0x1bad, 0xf901, "Gamestop Xbox 360 Controller" },
+ { 0x1bad, 0xf903, "Tron Xbox 360 controller" },
+ { 0x1bad, 0xf904, "PDP Versus Fighting Pad" },
+ { 0x1bad, 0xf906, "MortalKombat FightStick" },
+ { 0x1bad, 0xfa01, "MadCatz GamePad" },
+ { 0x1bad, 0xfd00, "Razer Onza TE" },
+ { 0x1bad, 0xfd01, "Razer Onza" },
+ { 0x24c6, 0x5000, "Razer Atrox Arcade Stick" },
+ { 0x24c6, 0x5300, "PowerA MINI PROEX Controller" },
+ { 0x24c6, 0x5303, "Xbox Airflo wired controller" },
+ { 0x24c6, 0x530a, "Xbox 360 Pro EX Controller" },
+ { 0x24c6, 0x531a, "PowerA Pro Ex" },
+ { 0x24c6, 0x5397, "FUS1ON Tournament Controller" },
+ { 0x24c6, 0x5500, "Hori XBOX 360 EX 2 with Turbo" },
+ { 0x24c6, 0x5501, "Hori Real Arcade Pro VX-SA" },
+ { 0x24c6, 0x5502, "Hori Fighting Stick VX Alt" },
+ { 0x24c6, 0x5503, "Hori Fighting Edge" },
+ { 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick" },
+ { 0x24c6, 0x550d, "Hori GEM Xbox controller" },
+ { 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360" },
+ { 0x24c6, 0x5b00, "ThrustMaster Ferrari 458 Racing Wheel" },
+ { 0x24c6, 0x5b02, "Thrustmaster, Inc. GPX Controller" },
+ { 0x24c6, 0x5b03, "Thrustmaster Ferrari 458 Racing Wheel" },
+ { 0x24c6, 0x5d04, "Razer Sabertooth" },
+ { 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360" },
+};
+
+typedef struct {
+ Uint8 last_state[USB_PACKET_LENGTH];
+ Uint32 rumble_expiration;
+} SDL_DriverXbox360_Context;
+
+
+static SDL_bool
+HIDAPI_DriverXbox360_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, int interface_number, Uint16 usage_page, Uint16 usage)
+{
+#ifdef __MACOSX__
+ return SDL_IsJoystickXbox360(vendor_id, product_id) || SDL_IsJoystickXboxOne(vendor_id, product_id);
+#else
+ return SDL_IsJoystickXbox360(vendor_id, product_id);
+#endif
+}
+
+static const char *
+HIDAPI_DriverXbox360_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
+{
+ int i;
+
+ for (i = 0; i < SDL_arraysize(xbox360_devicenames); ++i) {
+ const SDL_DriverXbox360_DeviceName *entry = &xbox360_devicenames[i];
+ if (vendor_id == entry->vendor_id && product_id == entry->product_id) {
+ return entry->name;
+ }
+ }
+ return NULL;
+}
+
+static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot)
+{
+ const Uint8 led_packet[] = { 0x01, 0x03, (2 + slot) };
+
+ if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
+ return SDL_FALSE;
+ }
+ return SDL_TRUE;
+}
+
+static SDL_bool
+HIDAPI_DriverXbox360_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
+{
+ SDL_DriverXbox360_Context *ctx;
+
+ ctx = (SDL_DriverXbox360_Context *)SDL_calloc(1, sizeof(*ctx));
+ if (!ctx) {
+ SDL_OutOfMemory();
+ return SDL_FALSE;
+ }
+ *context = ctx;
+
+ /* Set the controller LED */
+ SetSlotLED(dev, (joystick->instance_id % 4));
+
+ /* Initialize the joystick capabilities */
+ joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
+ joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
+ joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
+
+ return SDL_TRUE;
+}
+
+static int
+HIDAPI_DriverXbox360_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
+#ifdef __MACOSX__
+ /* On Mac OS X the 360Controller driver uses this short report,
+ and we need to prefix it with a magic token so hidapi passes it through untouched
+ */
+ Uint8 rumble_packet[] = { 'M', 'A', 'G', 'I', 'C', '0', 0x00, 0x04, 0x00, 0x00 };
+
+ rumble_packet[6+2] = (low_frequency_rumble >> 8);
+ rumble_packet[6+3] = (high_frequency_rumble >> 8);
+#else
+ Uint8 rumble_packet[] = { 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ rumble_packet[3] = (low_frequency_rumble >> 8);
+ rumble_packet[4] = (high_frequency_rumble >> 8);
+#endif
+
+ if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
+ return SDL_SetError("Couldn't send rumble packet");
+ }
+
+ if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
+ ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
+ } else {
+ ctx->rumble_expiration = 0;
+ }
+ return 0;
+}
+
+static void
+HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
+{
+ Sint16 axis;
+#ifdef __MACOSX__
+ const SDL_bool invert_y_axes = SDL_FALSE;
+#else
+ const SDL_bool invert_y_axes = SDL_TRUE;
+#endif
+
+ if (ctx->last_state[2] != data[2]) {
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
+ }
+
+ if (ctx->last_state[3] != data[3]) {
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
+ }
+
+ axis = ((int)data[4] * 257) - 32768;
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
+ axis = ((int)data[5] * 257) - 32768;
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
+ axis = *(Sint16*)(&data[6]);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
+ axis = *(Sint16*)(&data[8]);
+ if (invert_y_axes) {
+ axis = ~axis;
+ }
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
+ axis = *(Sint16*)(&data[10]);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
+ axis = *(Sint16*)(&data[12]);
+ if (invert_y_axes) {
+ axis = ~axis;
+ }
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
+
+ SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
+}
+
+static void
+HIDAPI_DriverXbox360_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+ SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
+ Uint8 data[USB_PACKET_LENGTH];
+ int size;
+
+ while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
+ switch (data[0]) {
+ case 0x00:
+ HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size);
+ break;
+ default:
+#ifdef DEBUG_JOYSTICK
+ SDL_Log("Unknown Xbox 360 packet: 0x%.2x\n", data[0]);
+#endif
+ break;
+ }
+ }
+
+ if (ctx->rumble_expiration) {
+ Uint32 now = SDL_GetTicks();
+ if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
+ HIDAPI_DriverXbox360_Rumble(joystick, dev, context, 0, 0, 0);
+ }
+ }
+}
+
+static void
+HIDAPI_DriverXbox360_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+ SDL_free(context);
+}
+
+SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 =
+{
+ SDL_HINT_JOYSTICK_HIDAPI_XBOX360,
+ SDL_TRUE,
+ HIDAPI_DriverXbox360_IsSupportedDevice,
+ HIDAPI_DriverXbox360_GetDeviceName,
+ HIDAPI_DriverXbox360_Init,
+ HIDAPI_DriverXbox360_Rumble,
+ HIDAPI_DriverXbox360_Update,
+ HIDAPI_DriverXbox360_Quit
+};
+
+#endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
+
+#endif /* SDL_JOYSTICK_HIDAPI */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c
new file mode 100644
index 000000000..959b281a7
--- /dev/null
+++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c
@@ -0,0 +1,364 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2018 Sam Lantinga
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#include "SDL_hints.h"
+#include "SDL_log.h"
+#include "SDL_events.h"
+#include "SDL_timer.h"
+#include "SDL_joystick.h"
+#include "SDL_gamecontroller.h"
+#include "../SDL_sysjoystick.h"
+#include "SDL_hidapijoystick_c.h"
+
+
+#ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
+
+#define USB_PACKET_LENGTH 64
+
+typedef struct
+{
+ Uint16 vendor_id;
+ Uint16 product_id;
+ const char *name;
+} SDL_DriverXboxOne_DeviceName;
+
+static const SDL_DriverXboxOne_DeviceName xboxone_devicenames[] = {
+ { 0x045e, 0x02d1, "Microsoft X-Box One pad" },
+ { 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)" },
+ { 0x045e, 0x02e3, "Microsoft X-Box One Elite pad" },
+ { 0x045e, 0x02ea, "Microsoft X-Box One S pad" },
+ { 0x045e, 0x02ff, "Microsoft X-Box One pad" },
+ { 0x0738, 0x4a01, "Mad Catz FightStick TE 2" },
+ { 0x0e6f, 0x0139, "Afterglow Prismatic Wired Controller" },
+ { 0x0e6f, 0x013a, "PDP Xbox One Controller" },
+ { 0x0e6f, 0x0146, "Rock Candy Wired Controller for Xbox One" },
+ { 0x0e6f, 0x0147, "PDP Marvel Xbox One Controller" },
+ { 0x0e6f, 0x015c, "PDP Xbox One Arcade Stick" },
+ { 0x0e6f, 0x0161, "PDP Xbox One Controller" },
+ { 0x0e6f, 0x0162, "PDP Xbox One Controller" },
+ { 0x0e6f, 0x0163, "PDP Xbox One Controller" },
+ { 0x0e6f, 0x0164, "PDP Battlefield One" },
+ { 0x0e6f, 0x0165, "PDP Titanfall 2" },
+ { 0x0e6f, 0x0246, "Rock Candy Gamepad for Xbox One 2015" },
+ { 0x0e6f, 0x02ab, "PDP Controller for Xbox One" },
+ { 0x0e6f, 0x02a4, "PDP Wired Controller for Xbox One - Stealth Series" },
+ { 0x0e6f, 0x0346, "Rock Candy Gamepad for Xbox One 2016" },
+ { 0x0f0d, 0x0063, "Hori Real Arcade Pro Hayabusa (USA) Xbox One" },
+ { 0x0f0d, 0x0067, "HORIPAD ONE" },
+ { 0x0f0d, 0x0078, "Hori Real Arcade Pro V Kai Xbox One" },
+ { 0x1532, 0x0a00, "Razer Atrox Arcade Stick" },
+ { 0x1532, 0x0a03, "Razer Wildcat" },
+ { 0x24c6, 0x541a, "PowerA Xbox One Mini Wired Controller" },
+ { 0x24c6, 0x542a, "Xbox ONE spectra" },
+ { 0x24c6, 0x543a, "PowerA Xbox One wired controller" },
+ { 0x24c6, 0x551a, "PowerA FUSION Pro Controller" },
+ { 0x24c6, 0x561a, "PowerA FUSION Controller" },
+};
+
+/*
+ * This packet is required for all Xbox One pads with 2015
+ * or later firmware installed (or present from the factory).
+ */
+static const Uint8 xboxone_fw2015_init[] = {
+ 0x05, 0x20, 0x00, 0x01, 0x00
+};
+
+/*
+ * This packet is required for the Titanfall 2 Xbox One pads
+ * (0x0e6f:0x0165) to finish initialization and for Hori pads
+ * (0x0f0d:0x0067) to make the analog sticks work.
+ */
+static const Uint8 xboxone_hori_init[] = {
+ 0x01, 0x20, 0x00, 0x09, 0x00, 0x04, 0x20, 0x3a,
+ 0x00, 0x00, 0x00, 0x80, 0x00
+};
+
+/*
+ * This packet is required for some of the PDP pads to start
+ * sending input reports. These pads include: (0x0e6f:0x02ab),
+ * (0x0e6f:0x02a4).
+ */
+static const Uint8 xboxone_pdp_init1[] = {
+ 0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
+};
+
+/*
+ * This packet is required for some of the PDP pads to start
+ * sending input reports. These pads include: (0x0e6f:0x02ab),
+ * (0x0e6f:0x02a4).
+ */
+static const Uint8 xboxone_pdp_init2[] = {
+ 0x06, 0x20, 0x00, 0x02, 0x01, 0x00
+};
+
+/*
+ * A specific rumble packet is required for some PowerA pads to start
+ * sending input reports. One of those pads is (0x24c6:0x543a).
+ */
+static const Uint8 xboxone_rumblebegin_init[] = {
+ 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
+ 0x1D, 0x1D, 0xFF, 0x00, 0x00
+};
+
+/*
+ * A rumble packet with zero FF intensity will immediately
+ * terminate the rumbling required to init PowerA pads.
+ * This should happen fast enough that the motors don't
+ * spin up to enough speed to actually vibrate the gamepad.
+ */
+static const Uint8 xboxone_rumbleend_init[] = {
+ 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * This specifies the selection of init packets that a gamepad
+ * will be sent on init *and* the order in which they will be
+ * sent. The correct sequence number will be added when the
+ * packet is going to be sent.
+ */
+typedef struct {
+ Uint16 vendor_id;
+ Uint16 product_id;
+ const Uint8 *data;
+ int size;
+} SDL_DriverXboxOne_InitPacket;
+
+static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = {
+ { 0x0e6f, 0x0165, xboxone_hori_init, sizeof(xboxone_hori_init) },
+ { 0x0f0d, 0x0067, xboxone_hori_init, sizeof(xboxone_hori_init) },
+ { 0x0000, 0x0000, xboxone_fw2015_init, sizeof(xboxone_fw2015_init) },
+ { 0x0e6f, 0x0246, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
+ { 0x0e6f, 0x0246, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
+ { 0x0e6f, 0x02ab, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
+ { 0x0e6f, 0x02ab, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
+ { 0x0e6f, 0x02a4, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
+ { 0x0e6f, 0x02a4, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
+ { 0x24c6, 0x541a, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
+ { 0x24c6, 0x542a, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
+ { 0x24c6, 0x543a, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
+ { 0x24c6, 0x541a, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
+ { 0x24c6, 0x542a, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
+ { 0x24c6, 0x543a, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
+};
+
+typedef struct {
+ Uint8 sequence;
+ Uint8 last_state[USB_PACKET_LENGTH];
+ Uint32 rumble_expiration;
+} SDL_DriverXboxOne_Context;
+
+
+static SDL_bool
+HIDAPI_DriverXboxOne_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, int interface_number, Uint16 usage_page, Uint16 usage)
+{
+ return SDL_IsJoystickXboxOne(vendor_id, product_id);
+}
+
+static const char *
+HIDAPI_DriverXboxOne_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
+{
+ int i;
+
+ for (i = 0; i < SDL_arraysize(xboxone_devicenames); ++i) {
+ const SDL_DriverXboxOne_DeviceName *entry = &xboxone_devicenames[i];
+ if (vendor_id == entry->vendor_id && product_id == entry->product_id) {
+ return entry->name;
+ }
+ }
+ return NULL;
+}
+
+static SDL_bool
+HIDAPI_DriverXboxOne_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
+{
+ SDL_DriverXboxOne_Context *ctx;
+ int i;
+ Uint8 init_packet[USB_PACKET_LENGTH];
+
+ ctx = (SDL_DriverXboxOne_Context *)SDL_calloc(1, sizeof(*ctx));
+ if (!ctx) {
+ SDL_OutOfMemory();
+ return SDL_FALSE;
+ }
+ *context = ctx;
+
+ /* Send the controller init data */
+ for (i = 0; i < SDL_arraysize(xboxone_init_packets); ++i) {
+ const SDL_DriverXboxOne_InitPacket *packet = &xboxone_init_packets[i];
+ if (!packet->vendor_id || (vendor_id == packet->vendor_id && product_id == packet->product_id)) {
+ SDL_memcpy(init_packet, packet->data, packet->size);
+ init_packet[2] = ctx->sequence++;
+ if (hid_write(dev, init_packet, packet->size) != packet->size) {
+ SDL_SetError("Couldn't write Xbox One initialization packet");
+ SDL_free(ctx);
+ return SDL_FALSE;
+ }
+ }
+ }
+
+ /* Initialize the joystick capabilities */
+ joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
+ joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
+ joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
+
+ return SDL_TRUE;
+}
+
+static int
+HIDAPI_DriverXboxOne_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context;
+ Uint8 rumble_packet[] = { 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF };
+
+ /* The Rock Candy Xbox One Controller limits the range of
+ low frequency rumble strength in the range of [0 - 0x99]
+ high frequency rumble strength in the range of [0 - 0x82]
+
+ I think the valid range of rumble at the firmware level is [0 - 0x7F]
+ */
+ rumble_packet[2] = ctx->sequence++;
+ rumble_packet[8] = (low_frequency_rumble >> 9);
+ rumble_packet[9] = (high_frequency_rumble >> 9);
+
+ if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
+ return SDL_SetError("Couldn't send rumble packet");
+ }
+
+ if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
+ ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
+ } else {
+ ctx->rumble_expiration = 0;
+ }
+ return 0;
+}
+
+static void
+HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
+{
+ Sint16 axis;
+
+ if (ctx->last_state[4] != data[4]) {
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[4] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[4] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[4] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[4] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[4] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[4] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
+ }
+
+ if (ctx->last_state[5] != data[5]) {
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[5] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[5] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[5] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[5] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[5] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[5] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[5] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[5] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
+ }
+
+ axis = ((int)*(Sint16*)(&data[6]) * 64) - 32768;
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
+ axis = ((int)*(Sint16*)(&data[8]) * 64) - 32768;
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
+ axis = *(Sint16*)(&data[10]);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
+ axis = *(Sint16*)(&data[12]);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, ~axis);
+ axis = *(Sint16*)(&data[14]);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
+ axis = *(Sint16*)(&data[16]);
+ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, ~axis);
+
+ SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
+}
+
+static void
+HIDAPI_DriverXboxOne_HandleModePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
+{
+ if (data[1] == 0x30) {
+ /* The Xbox One S controller needs acks for mode reports */
+ const Uint8 seqnum = data[2];
+ const Uint8 ack[] = { 0x01, 0x20, seqnum, 0x09, 0x00, 0x07, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ hid_write(dev, ack, sizeof(ack));
+ }
+
+ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[4] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+}
+
+static void
+HIDAPI_DriverXboxOne_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+ SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context;
+ Uint8 data[USB_PACKET_LENGTH];
+ int size;
+
+ while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
+ switch (data[0]) {
+ case 0x20:
+ HIDAPI_DriverXboxOne_HandleStatePacket(joystick, dev, ctx, data, size);
+ break;
+ case 0x07:
+ HIDAPI_DriverXboxOne_HandleModePacket(joystick, dev, ctx, data, size);
+ break;
+ default:
+#ifdef DEBUG_JOYSTICK
+ SDL_Log("Unknown Xbox One packet: 0x%.2x\n", data[0]);
+#endif
+ break;
+ }
+ }
+
+ if (ctx->rumble_expiration) {
+ Uint32 now = SDL_GetTicks();
+ if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
+ HIDAPI_DriverXboxOne_Rumble(joystick, dev, context, 0, 0, 0);
+ }
+ }
+}
+
+static void
+HIDAPI_DriverXboxOne_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
+{
+ SDL_free(context);
+}
+
+SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne =
+{
+ SDL_HINT_JOYSTICK_HIDAPI_XBOXONE,
+ SDL_TRUE,
+ HIDAPI_DriverXboxOne_IsSupportedDevice,
+ HIDAPI_DriverXboxOne_GetDeviceName,
+ HIDAPI_DriverXboxOne_Init,
+ HIDAPI_DriverXboxOne_Rumble,
+ HIDAPI_DriverXboxOne_Update,
+ HIDAPI_DriverXboxOne_Quit
+};
+
+#endif /* SDL_JOYSTICK_HIDAPI_XBOXONE */
+
+#endif /* SDL_JOYSTICK_HIDAPI */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c
new file mode 100644
index 000000000..98720aacd
--- /dev/null
+++ b/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -0,0 +1,541 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2018 Sam Lantinga
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifdef SDL_JOYSTICK_HIDAPI
+
+#include "SDL_endian.h"
+#include "SDL_hints.h"
+#include "SDL_log.h"
+#include "SDL_timer.h"
+#include "SDL_joystick.h"
+#include "../SDL_sysjoystick.h"
+#include "SDL_hidapijoystick_c.h"
+
+
+struct joystick_hwdata
+{
+ SDL_HIDAPI_DeviceDriver *driver;
+ void *context;
+
+ hid_device *dev;
+};
+
+typedef struct _SDL_HIDAPI_Device
+{
+ SDL_JoystickID instance_id;
+ char *name;
+ char *path;
+ Uint16 vendor_id;
+ Uint16 product_id;
+ SDL_JoystickGUID guid;
+ int interface_number; /* Available on Windows and Linux */
+ Uint16 usage_page; /* Available on Windows and Mac OS X */
+ Uint16 usage; /* Available on Windows and Mac OS X */
+ SDL_HIDAPI_DeviceDriver *driver;
+
+ /* Used during scanning for device changes */
+ SDL_bool seen;
+
+ struct _SDL_HIDAPI_Device *next;
+} SDL_HIDAPI_Device;
+
+static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
+#ifdef SDL_JOYSTICK_HIDAPI_PS4
+ &SDL_HIDAPI_DriverPS4,
+#endif
+#ifdef SDL_JOYSTICK_HIDAPI_STEAM
+ &SDL_HIDAPI_DriverSteam,
+#endif
+#ifdef SDL_JOYSTICK_HIDAPI_SWITCH
+ &SDL_HIDAPI_DriverSwitch,
+#endif
+#ifdef SDL_JOYSTICK_HIDAPI_XBOX360
+ &SDL_HIDAPI_DriverXbox360,
+#endif
+#ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
+ &SDL_HIDAPI_DriverXboxOne,
+#endif
+};
+static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
+static int SDL_HIDAPI_numjoysticks = 0;
+static Uint32 SDL_HIDAPI_last_detect = 0;
+
+static SDL_HIDAPI_DeviceDriver *
+HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
+{
+ int i;
+
+ if (SDL_ShouldIgnoreJoystick(device->name, device->guid)) {
+ return NULL;
+ }
+
+ for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
+ SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
+ if (driver->enabled && driver->IsSupportedDevice(device->vendor_id, device->product_id, device->interface_number, device->usage_page, device->usage)) {
+ return driver;
+ }
+ }
+ return NULL;
+}
+
+static SDL_HIDAPI_Device *
+HIDAPI_GetJoystickByIndex(int device_index)
+{
+ SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
+ while (device) {
+ if (device->driver) {
+ if (device_index == 0) {
+ break;
+ }
+ --device_index;
+ }
+ device = device->next;
+ }
+ return device;
+}
+
+static SDL_HIDAPI_Device *
+HIDAPI_GetJoystickByInfo(const char *path, Uint16 vendor_id, Uint16 product_id)
+{
+ SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
+ while (device) {
+ if (device->vendor_id == vendor_id && device->product_id == product_id &&
+ SDL_strcmp(device->path, path) == 0) {
+ break;
+ }
+ device = device->next;
+ }
+ return device;
+}
+
+static void SDLCALL
+SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
+{
+ int i;
+ SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
+ SDL_bool enabled = (!hint || !*hint || ((*hint != '0') && (SDL_strcasecmp(hint, "false") != 0)));
+
+ if (SDL_strcmp(name, SDL_HINT_JOYSTICK_HIDAPI) == 0) {
+ for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
+ SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
+ driver->enabled = SDL_GetHintBoolean(driver->hint, enabled);
+ }
+ } else {
+ for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
+ SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
+ if (SDL_strcmp(name, driver->hint) == 0) {
+ driver->enabled = enabled;
+ break;
+ }
+ }
+ }
+
+ /* Update device list if driver availability changes */
+ while (device) {
+ if (device->driver) {
+ if (!device->driver->enabled) {
+ device->driver = NULL;
+
+ --SDL_HIDAPI_numjoysticks;
+
+ SDL_PrivateJoystickRemoved(device->instance_id);
+ }
+ } else {
+ device->driver = HIDAPI_GetDeviceDriver(device);
+ if (device->driver) {
+ device->instance_id = SDL_GetNextJoystickInstanceID();
+
+ ++SDL_HIDAPI_numjoysticks;
+
+ SDL_PrivateJoystickAdded(device->instance_id);
+ }
+ }
+ device = device->next;
+ }
+}
+
+static void HIDAPI_JoystickDetect(void);
+
+static int
+HIDAPI_JoystickInit(void)
+{
+ int i;
+
+ if (hid_init() < 0) {
+ SDL_SetError("Couldn't initialize hidapi");
+ return -1;
+ }
+
+ for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
+ SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
+ SDL_AddHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
+ }
+ SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
+ SDL_HIDAPIDriverHintChanged, NULL);
+ SDL_HIDAPI_last_detect = 0;
+ HIDAPI_JoystickDetect();
+ return 0;
+}
+
+static int
+HIDAPI_JoystickGetCount(void)
+{
+ return SDL_HIDAPI_numjoysticks;
+}
+
+static void
+HIDAPI_AddDevice(struct hid_device_info *info)
+{
+ SDL_HIDAPI_Device *device;
+ SDL_HIDAPI_Device *curr, *last = NULL;
+
+ for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
+ continue;
+ }
+
+ device = (SDL_HIDAPI_Device *)SDL_calloc(1, sizeof(*device));
+ if (!device) {
+ return;
+ }
+ device->instance_id = -1;
+ device->seen = SDL_TRUE;
+ device->vendor_id = info->vendor_id;
+ device->product_id = info->product_id;
+ device->interface_number = info->interface_number;
+ device->usage_page = info->usage_page;
+ device->usage = info->usage;
+ {
+ /* FIXME: Is there any way to tell whether this is a Bluetooth device? */
+ const Uint16 vendor = device->vendor_id;
+ const Uint16 product = device->product_id;
+ const Uint16 version = 0;
+ Uint16 *guid16 = (Uint16 *)device->guid.data;
+
+ *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
+ *guid16++ = 0;
+ *guid16++ = SDL_SwapLE16(vendor);
+ *guid16++ = 0;
+ *guid16++ = SDL_SwapLE16(product);
+ *guid16++ = 0;
+ *guid16++ = SDL_SwapLE16(version);
+ *guid16++ = 0;
+
+ /* Note that this is a HIDAPI device for special handling elsewhere */
+ device->guid.data[14] = 'h';
+ device->guid.data[15] = 0;
+ }
+ device->driver = HIDAPI_GetDeviceDriver(device);
+
+ if (device->driver) {
+ const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id);
+ if (name) {
+ device->name = SDL_strdup(name);
+ }
+ }
+
+ if (!device->name && info->manufacturer_string && info->product_string) {
+ char *manufacturer_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
+ char *product_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
+ if (!manufacturer_string && !product_string) {
+ if (sizeof(wchar_t) == sizeof(Uint16)) {
+ manufacturer_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
+ product_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
+ } else if (sizeof(wchar_t) == sizeof(Uint32)) {
+ manufacturer_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
+ product_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
+ }
+ }
+ if (manufacturer_string && product_string) {
+ size_t name_size = (SDL_strlen(manufacturer_string) + 1 + SDL_strlen(product_string) + 1);
+ device->name = (char *)SDL_malloc(name_size);
+ if (device->name) {
+ SDL_snprintf(device->name, name_size, "%s %s", manufacturer_string, product_string);
+ }
+ }
+ if (manufacturer_string) {
+ SDL_free(manufacturer_string);
+ }
+ if (product_string) {
+ SDL_free(product_string);
+ }
+ }
+ if (!device->name) {
+ size_t name_size = (6 + 1 + 6 + 1);
+ device->name = (char *)SDL_malloc(name_size);
+ if (!device->name) {
+ SDL_free(device);
+ return;
+ }
+ SDL_snprintf(device->name, name_size, "0x%.4x/0x%.4x", info->vendor_id, info->product_id);
+ }
+
+ device->path = SDL_strdup(info->path);
+ if (!device->path) {
+ SDL_free(device->name);
+ SDL_free(device);
+ return;
+ }
+
+#ifdef DEBUG_HIDAPI
+ SDL_Log("Adding HIDAPI device '%s' interface %d, usage page 0x%.4x, usage 0x%.4x\n", device->name, device->interface_number, device->usage_page, device->usage);
+#endif
+
+ /* Add it to the list */
+ if (last) {
+ last->next = device;
+ } else {
+ SDL_HIDAPI_devices = device;
+ }
+
+ if (device->driver) {
+ /* It's a joystick! */
+ device->instance_id = SDL_GetNextJoystickInstanceID();
+
+ ++SDL_HIDAPI_numjoysticks;
+
+ SDL_PrivateJoystickAdded(device->instance_id);
+ }
+}
+
+
+static void
+HIDAPI_DelDevice(SDL_HIDAPI_Device *device, SDL_bool send_event)
+{
+ SDL_HIDAPI_Device *curr, *last;
+ for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
+ if (curr == device) {
+ if (last) {
+ last->next = curr->next;
+ } else {
+ SDL_HIDAPI_devices = curr->next;
+ }
+
+ if (device->driver && send_event) {
+ /* Need to decrement the joystick count before we post the event */
+ --SDL_HIDAPI_numjoysticks;
+
+ SDL_PrivateJoystickRemoved(device->instance_id);
+ }
+
+ SDL_free(device->name);
+ SDL_free(device->path);
+ SDL_free(device);
+ return;
+ }
+ }
+}
+
+static void
+HIDAPI_UpdateDeviceList(void)
+{
+ SDL_HIDAPI_Device *device;
+ struct hid_device_info *devs, *info;
+
+ /* Prepare the existing device list */
+ device = SDL_HIDAPI_devices;
+ while (device) {
+ device->seen = SDL_FALSE;
+ device = device->next;
+ }
+
+ /* Enumerate the devices */
+ devs = hid_enumerate(0, 0);
+ if (devs) {
+ for (info = devs; info; info = info->next) {
+ device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id);
+ if (device) {
+ device->seen = SDL_TRUE;
+ } else {
+ HIDAPI_AddDevice(info);
+ }
+ }
+ hid_free_enumeration(devs);
+ }
+
+ /* Remove any devices that weren't seen */
+ device = SDL_HIDAPI_devices;
+ while (device) {
+ SDL_HIDAPI_Device *next = device->next;
+
+ if (!device->seen) {
+ HIDAPI_DelDevice(device, SDL_TRUE);
+ }
+ device = next;
+ }
+}
+
+SDL_bool
+HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id)
+{
+ SDL_HIDAPI_Device *device;
+
+ /* Make sure the device list is completely up to date when we check for device presence */
+ HIDAPI_UpdateDeviceList();
+
+ device = SDL_HIDAPI_devices;
+ while (device) {
+ if (device->vendor_id == vendor_id && device->product_id == product_id && device->driver) {
+ return SDL_TRUE;
+ }
+ device = device->next;
+ }
+ return SDL_FALSE;
+}
+
+static void
+HIDAPI_JoystickDetect(void)
+{
+ const Uint32 SDL_HIDAPI_DETECT_INTERVAL_MS = 3000; /* Update every 3 seconds */
+ Uint32 now = SDL_GetTicks();
+ if (!SDL_HIDAPI_last_detect || SDL_TICKS_PASSED(now, SDL_HIDAPI_last_detect + SDL_HIDAPI_DETECT_INTERVAL_MS)) {
+ HIDAPI_UpdateDeviceList();
+ SDL_HIDAPI_last_detect = now;
+ }
+}
+
+static const char *
+HIDAPI_JoystickGetDeviceName(int device_index)
+{
+ return HIDAPI_GetJoystickByIndex(device_index)->name;
+}
+
+static SDL_JoystickGUID
+HIDAPI_JoystickGetDeviceGUID(int device_index)
+{
+ return HIDAPI_GetJoystickByIndex(device_index)->guid;
+}
+
+static SDL_JoystickID
+HIDAPI_JoystickGetDeviceInstanceID(int device_index)
+{
+ return HIDAPI_GetJoystickByIndex(device_index)->instance_id;
+}
+
+static int
+HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index)
+{
+ SDL_HIDAPI_Device *device = HIDAPI_GetJoystickByIndex(device_index);
+ struct joystick_hwdata *hwdata;
+
+ hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
+ if (!hwdata) {
+ return SDL_OutOfMemory();
+ }
+
+ hwdata->driver = device->driver;
+ hwdata->dev = hid_open_path(device->path, 0);
+ if (!hwdata->dev) {
+ SDL_free(hwdata);
+ return SDL_SetError("Couldn't open HID device %s", device->path);
+ }
+
+ if (!device->driver->Init(joystick, hwdata->dev, device->vendor_id, device->product_id, &hwdata->context)) {
+ hid_close(hwdata->dev);
+ SDL_free(hwdata);
+ return -1;
+ }
+
+ joystick->hwdata = hwdata;
+ return 0;
+}
+
+static SDL_bool
+HIDAPI_JoystickIsAttached(SDL_Joystick *joystick)
+{
+ SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
+ while (device) {
+ if (device->driver) {
+ if (joystick->instance_id == device->instance_id) {
+ return SDL_TRUE;
+ }
+ }
+ device = device->next;
+ }
+ return SDL_FALSE;
+}
+
+static int
+HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ struct joystick_hwdata *hwdata = joystick->hwdata;
+ SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
+ return driver->Rumble(joystick, hwdata->dev, hwdata->context, low_frequency_rumble, high_frequency_rumble, duration_ms);
+}
+
+static void
+HIDAPI_JoystickUpdate(SDL_Joystick * joystick)
+{
+ struct joystick_hwdata *hwdata = joystick->hwdata;
+ SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
+ driver->Update(joystick, hwdata->dev, hwdata->context);
+}
+
+static void
+HIDAPI_JoystickClose(SDL_Joystick * joystick)
+{
+ struct joystick_hwdata *hwdata = joystick->hwdata;
+ SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
+ driver->Quit(joystick, hwdata->dev, hwdata->context);
+
+ hid_close(hwdata->dev);
+ SDL_free(hwdata);
+ joystick->hwdata = NULL;
+}
+
+static void
+HIDAPI_JoystickQuit(void)
+{
+ int i;
+
+ while (SDL_HIDAPI_devices) {
+ HIDAPI_DelDevice(SDL_HIDAPI_devices, SDL_FALSE);
+ }
+ for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
+ SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
+ SDL_DelHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
+ }
+ SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
+ SDL_HIDAPIDriverHintChanged, NULL);
+ SDL_HIDAPI_numjoysticks = 0;
+
+ hid_exit();
+}
+
+SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
+{
+ HIDAPI_JoystickInit,
+ HIDAPI_JoystickGetCount,
+ HIDAPI_JoystickDetect,
+ HIDAPI_JoystickGetDeviceName,
+ HIDAPI_JoystickGetDeviceGUID,
+ HIDAPI_JoystickGetDeviceInstanceID,
+ HIDAPI_JoystickOpen,
+ HIDAPI_JoystickIsAttached,
+ HIDAPI_JoystickRumble,
+ HIDAPI_JoystickUpdate,
+ HIDAPI_JoystickClose,
+ HIDAPI_JoystickQuit,
+};
+
+#endif /* SDL_JOYSTICK_HIDAPI */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h
new file mode 100644
index 000000000..fbfd3a33a
--- /dev/null
+++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h
@@ -0,0 +1,70 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2018 Sam Lantinga
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#ifndef SDL_JOYSTICK_HIDAPI_H
+#define SDL_JOYSTICK_HIDAPI_H
+
+#include "../../hidapi/hidapi/hidapi.h"
+
+/* This is the full set of HIDAPI drivers available */
+#define SDL_JOYSTICK_HIDAPI_PS4
+#define SDL_JOYSTICK_HIDAPI_SWITCH
+#define SDL_JOYSTICK_HIDAPI_XBOX360
+#define SDL_JOYSTICK_HIDAPI_XBOXONE
+
+#ifdef __WINDOWS__
+/* On Windows, Xbox controllers are handled by the XInput driver */
+#undef SDL_JOYSTICK_HIDAPI_XBOX360
+#undef SDL_JOYSTICK_HIDAPI_XBOXONE
+#endif
+
+#ifdef __MACOSX__
+/* On Mac OS X, Xbox One controllers are handled by the Xbox 360 driver */
+#undef SDL_JOYSTICK_HIDAPI_XBOXONE
+#endif
+
+typedef struct _SDL_HIDAPI_DeviceDriver
+{
+ const char *hint;
+ SDL_bool enabled;
+ SDL_bool (*IsSupportedDevice)(Uint16 vendor_id, Uint16 product_id, int interface_number, Uint16 usage_page, Uint16 usage);
+ const char *(*GetDeviceName)(Uint16 vendor_id, Uint16 product_id);
+ SDL_bool (*Init)(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context);
+ int (*Rumble)(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
+ void (*Update)(SDL_Joystick *joystick, hid_device *dev, void *context);
+ void (*Quit)(SDL_Joystick *joystick, hid_device *dev, void *context);
+
+} SDL_HIDAPI_DeviceDriver;
+
+/* HIDAPI device support */
+extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4;
+extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam;
+extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch;
+extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360;
+extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne;
+
+/* Return true if a HID device is present and supported as a joystick */
+extern SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id);
+
+#endif /* SDL_JOYSTICK_HIDAPI_H */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/joystick/iphoneos/SDL_sysjoystick.m b/src/joystick/iphoneos/SDL_sysjoystick.m
index 2da1e6c2b..db5ab9a79 100644
--- a/src/joystick/iphoneos/SDL_sysjoystick.m
+++ b/src/joystick/iphoneos/SDL_sysjoystick.m
@@ -33,7 +33,6 @@
#include "SDL_stdinc.h"
#include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.h"
-#include "../steam/SDL_steamcontroller.h"
#if !SDL_EVENTS_DISABLED
@@ -59,7 +58,6 @@ static CMMotionManager *motionManager = nil;
static SDL_JoystickDeviceItem *deviceList = NULL;
static int numjoysticks = 0;
-static SDL_JoystickID instancecounter = 0;
int SDL_AppleTVRemoteOpenedAsJoystick = 0;
static SDL_JoystickDeviceItem *
@@ -80,10 +78,9 @@ GetDeviceForIndex(int device_index)
}
static void
-SDL_SYS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controller)
+IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controller)
{
#ifdef SDL_JOYSTICK_MFI
- const Uint16 BUS_BLUETOOTH = 0x05;
const Uint16 VENDOR_APPLE = 0x05AC;
Uint16 *guid16 = (Uint16 *)device->guid.data;
Uint16 vendor = 0;
@@ -136,7 +133,7 @@ SDL_SYS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *contr
/* We only need 16 bits for each of these; space them out to fill 128. */
/* Byteswap so devices get same GUID on little/big endian platforms. */
- *guid16++ = SDL_SwapLE16(BUS_BLUETOOTH);
+ *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
*guid16++ = 0;
*guid16++ = SDL_SwapLE16(vendor);
*guid16++ = 0;
@@ -157,7 +154,7 @@ SDL_SYS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *contr
}
static void
-SDL_SYS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
+IOS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
{
SDL_JoystickDeviceItem *device = deviceList;
@@ -183,7 +180,7 @@ SDL_SYS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
}
device->accelerometer = accelerometer;
- device->instance_id = instancecounter++;
+ device->instance_id = SDL_GetNextJoystickInstanceID();
if (accelerometer) {
#if TARGET_OS_TV
@@ -199,7 +196,7 @@ SDL_SYS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
SDL_memcpy(&device->guid.data, device->name, SDL_min(sizeof(SDL_JoystickGUID), SDL_strlen(device->name)));
#endif /* TARGET_OS_TV */
} else if (controller) {
- SDL_SYS_AddMFIJoystickDevice(device, controller);
+ IOS_AddMFIJoystickDevice(device, controller);
}
if (deviceList == NULL) {
@@ -214,11 +211,11 @@ SDL_SYS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
++numjoysticks;
- SDL_PrivateJoystickAdded(numjoysticks - 1);
+ SDL_PrivateJoystickAdded(device->instance_id);
}
static SDL_JoystickDeviceItem *
-SDL_SYS_RemoveJoystickDevice(SDL_JoystickDeviceItem *device)
+IOS_RemoveJoystickDevice(SDL_JoystickDeviceItem *device)
{
SDL_JoystickDeviceItem *prev = NULL;
SDL_JoystickDeviceItem *next = NULL;
@@ -287,78 +284,27 @@ SDL_AppleTVRemoteRotationHintChanged(void *udata, const char *name, const char *
}
#endif /* TARGET_OS_TV */
-static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickGUID guid, int *device_instance)
-{
- SDL_JoystickDeviceItem *device = (SDL_JoystickDeviceItem *)SDL_calloc(1, sizeof(SDL_JoystickDeviceItem));
- if (device == NULL) {
- return SDL_FALSE;
- }
-
- *device_instance = device->instance_id = instancecounter++;
- device->name = SDL_strdup(name);
- device->guid = guid;
- SDL_GetSteamControllerInputs(&device->nbuttons,
- &device->naxes,
- &device->nhats);
- device->m_bSteamController = SDL_TRUE;
-
- if (deviceList == NULL) {
- deviceList = device;
- } else {
- SDL_JoystickDeviceItem *lastdevice = deviceList;
- while (lastdevice->next != NULL) {
- lastdevice = lastdevice->next;
- }
- lastdevice->next = device;
- }
-
- ++numjoysticks;
-
- SDL_PrivateJoystickAdded(numjoysticks - 1);
-
- return SDL_TRUE;
-}
-
-static void SteamControllerDisconnectedCallback(int device_instance)
-{
- SDL_JoystickDeviceItem *item;
-
- for (item = deviceList; item; item = item->next) {
- if (item->instance_id == device_instance) {
- SDL_SYS_RemoveJoystickDevice(item);
- break;
- }
- }
-}
-
-/* Function to scan the system for joysticks.
- * Joystick 0 should be the system default joystick.
- * It should return 0, or -1 on an unrecoverable fatal error.
- */
-int
-SDL_SYS_JoystickInit(void)
+static int
+IOS_JoystickInit(void)
{
@autoreleasepool {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
- SDL_InitSteamControllers(SteamControllerConnectedCallback,
- SteamControllerDisconnectedCallback);
-
#if !TARGET_OS_TV
if (SDL_GetHintBoolean(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, SDL_TRUE)) {
/* Default behavior, accelerometer as joystick */
- SDL_SYS_AddJoystickDevice(nil, SDL_TRUE);
+ IOS_AddJoystickDevice(nil, SDL_TRUE);
}
#endif /* !TARGET_OS_TV */
#ifdef SDL_JOYSTICK_MFI
/* GameController.framework was added in iOS 7. */
if (![GCController class]) {
- return numjoysticks;
+ return 0;
}
for (GCController *controller in [GCController controllers]) {
- SDL_SYS_AddJoystickDevice(controller, SDL_FALSE);
+ IOS_AddJoystickDevice(controller, SDL_FALSE);
}
#if TARGET_OS_TV
@@ -371,7 +317,7 @@ SDL_SYS_JoystickInit(void)
queue:nil
usingBlock:^(NSNotification *note) {
GCController *controller = note.object;
- SDL_SYS_AddJoystickDevice(controller, SDL_FALSE);
+ IOS_AddJoystickDevice(controller, SDL_FALSE);
}];
disconnectObserver = [center addObserverForName:GCControllerDidDisconnectNotification
@@ -382,7 +328,7 @@ SDL_SYS_JoystickInit(void)
SDL_JoystickDeviceItem *device = deviceList;
while (device != NULL) {
if (device->controller == controller) {
- SDL_SYS_RemoveJoystickDevice(device);
+ IOS_RemoveJoystickDevice(device);
break;
}
device = device->next;
@@ -391,43 +337,49 @@ SDL_SYS_JoystickInit(void)
#endif /* SDL_JOYSTICK_MFI */
}
- return numjoysticks;
+ return 0;
}
-int
-SDL_SYS_NumJoysticks(void)
+static int
+IOS_JoystickGetCount(void)
{
return numjoysticks;
}
-void
-SDL_SYS_JoystickDetect(void)
+static void
+IOS_JoystickDetect(void)
{
- SDL_UpdateSteamControllers();
}
-/* Function to get the device-dependent name of a joystick */
-const char *
-SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+static const char *
+IOS_JoystickGetDeviceName(int device_index)
{
SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
return device ? device->name : "Unknown";
}
-/* Function to perform the mapping from device index to the instance id for this index */
-SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+static SDL_JoystickGUID
+IOS_JoystickGetDeviceGUID( int device_index )
{
SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
- return device ? device->instance_id : 0;
+ SDL_JoystickGUID guid;
+ if (device) {
+ guid = device->guid;
+ } else {
+ SDL_zero(guid);
+ }
+ return guid;
}
-/* Function to open a joystick for use.
- The joystick to open is specified by the device index.
- This should fill the nbuttons and naxes fields of the joystick structure.
- It returns 0, or -1 if there is an error.
- */
-int
-SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
+static SDL_JoystickID
+IOS_JoystickGetDeviceInstanceID(int device_index)
+{
+ SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
+ return device ? device->instance_id : -1;
+}
+
+static int
+IOS_JoystickOpen(SDL_Joystick * joystick, int device_index)
{
SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
if (device == NULL) {
@@ -473,15 +425,14 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
return 0;
}
-/* Function to determine if this joystick is attached to the system right now */
-SDL_bool
-SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
+static SDL_bool
+IOS_JoystickIsAttached(SDL_Joystick *joystick)
{
return joystick->hwdata != NULL;
}
static void
-SDL_SYS_AccelerometerUpdate(SDL_Joystick * joystick)
+IOS_AccelerometerUpdate(SDL_Joystick * joystick)
{
#if !TARGET_OS_TV
const float maxgforce = SDL_IPHONE_MAX_GFORCE;
@@ -526,7 +477,7 @@ SDL_SYS_AccelerometerUpdate(SDL_Joystick * joystick)
#ifdef SDL_JOYSTICK_MFI
static Uint8
-SDL_SYS_MFIJoystickHatStateForDPad(GCControllerDirectionPad *dpad)
+IOS_MFIJoystickHatStateForDPad(GCControllerDirectionPad *dpad)
{
Uint8 hat = 0;
@@ -551,7 +502,7 @@ SDL_SYS_MFIJoystickHatStateForDPad(GCControllerDirectionPad *dpad)
#endif
static void
-SDL_SYS_MFIJoystickUpdate(SDL_Joystick * joystick)
+IOS_MFIJoystickUpdate(SDL_Joystick * joystick)
{
#if SDL_JOYSTICK_MFI
@autoreleasepool {
@@ -581,7 +532,7 @@ SDL_SYS_MFIJoystickUpdate(SDL_Joystick * joystick)
gamepad.rightShoulder.isPressed,
};
- hatstate = SDL_SYS_MFIJoystickHatStateForDPad(gamepad.dpad);
+ hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
for (i = 0; i < SDL_arraysize(axes); i++) {
/* The triggers (axes 2 and 5) are resting at -32768 but SDL
@@ -608,7 +559,7 @@ SDL_SYS_MFIJoystickUpdate(SDL_Joystick * joystick)
gamepad.rightShoulder.isPressed,
};
- hatstate = SDL_SYS_MFIJoystickHatStateForDPad(gamepad.dpad);
+ hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
for (i = 0; i < SDL_arraysize(buttons); i++) {
updateplayerindex |= (joystick->buttons[i] != buttons[i]);
@@ -678,13 +629,14 @@ SDL_SYS_MFIJoystickUpdate(SDL_Joystick * joystick)
#endif /* SDL_JOYSTICK_MFI */
}
-/* Function to update the state of a joystick - called as a device poll.
- * This function shouldn't update the joystick structure directly,
- * but instead should call SDL_PrivateJoystick*() to deliver events
- * and update joystick device state.
- */
-void
-SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+static int
+IOS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ return SDL_Unsupported();
+}
+
+static void
+IOS_JoystickUpdate(SDL_Joystick * joystick)
{
SDL_JoystickDeviceItem *device = joystick->hwdata;
@@ -692,21 +644,15 @@ SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
return;
}
- if (device->m_bSteamController) {
- SDL_UpdateSteamController(joystick);
- return;
- }
-
if (device->accelerometer) {
- SDL_SYS_AccelerometerUpdate(joystick);
+ IOS_AccelerometerUpdate(joystick);
} else if (device->controller) {
- SDL_SYS_MFIJoystickUpdate(joystick);
+ IOS_MFIJoystickUpdate(joystick);
}
}
-/* Function to close a joystick after use */
-void
-SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+static void
+IOS_JoystickClose(SDL_Joystick * joystick)
{
SDL_JoystickDeviceItem *device = joystick->hwdata;
@@ -734,9 +680,8 @@ SDL_SYS_JoystickClose(SDL_Joystick * joystick)
}
}
-/* Function to perform any system-specific joystick related cleanup */
-void
-SDL_SYS_JoystickQuit(void)
+static void
+IOS_JoystickQuit(void)
{
@autoreleasepool {
#ifdef SDL_JOYSTICK_MFI
@@ -759,7 +704,7 @@ SDL_SYS_JoystickQuit(void)
#endif /* SDL_JOYSTICK_MFI */
while (deviceList != NULL) {
- SDL_SYS_RemoveJoystickDevice(deviceList);
+ IOS_RemoveJoystickDevice(deviceList);
}
#if !TARGET_OS_TV
@@ -767,34 +712,23 @@ SDL_SYS_JoystickQuit(void)
#endif /* !TARGET_OS_TV */
}
- SDL_QuitSteamControllers();
-
numjoysticks = 0;
}
-SDL_JoystickGUID
-SDL_SYS_JoystickGetDeviceGUID( int device_index )
+SDL_JoystickDriver SDL_IOS_JoystickDriver =
{
- SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
- SDL_JoystickGUID guid;
- if (device) {
- guid = device->guid;
- } else {
- SDL_zero(guid);
- }
- return guid;
-}
-
-SDL_JoystickGUID
-SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
-{
- SDL_JoystickGUID guid;
- if (joystick->hwdata) {
- guid = joystick->hwdata->guid;
- } else {
- SDL_zero(guid);
- }
- return guid;
-}
+ IOS_JoystickInit,
+ IOS_JoystickGetCount,
+ IOS_JoystickDetect,
+ IOS_JoystickGetDeviceName,
+ IOS_JoystickGetDeviceGUID,
+ IOS_JoystickGetDeviceInstanceID,
+ IOS_JoystickOpen,
+ IOS_JoystickIsAttached,
+ IOS_JoystickRumble,
+ IOS_JoystickUpdate,
+ IOS_JoystickClose,
+ IOS_JoystickQuit,
+};
/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/joystick/iphoneos/SDL_sysjoystick_c.h b/src/joystick/iphoneos/SDL_sysjoystick_c.h
index 54bc549aa..12aa296b1 100644
--- a/src/joystick/iphoneos/SDL_sysjoystick_c.h
+++ b/src/joystick/iphoneos/SDL_sysjoystick_c.h
@@ -46,9 +46,6 @@ typedef struct joystick_hwdata
int nbuttons;
int nhats;
- /* Steam Controller support */
- SDL_bool m_bSteamController;
-
struct joystick_hwdata *next;
} joystick_hwdata;
diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c
index 457c4b85b..5a69aa8b3 100644
--- a/src/joystick/linux/SDL_sysjoystick.c
+++ b/src/joystick/linux/SDL_sysjoystick.c
@@ -29,10 +29,11 @@
/* This is the Linux implementation of the SDL joystick API */
#include
-#include
+#include /* errno, strerror */
#include
-#include
#include /* For the definition of PATH_MAX */
+#include
+#include
#include
#include "SDL_assert.h"
@@ -43,6 +44,7 @@
#include "../SDL_joystick_c.h"
#include "../steam/SDL_steamcontroller.h"
#include "SDL_sysjoystick_c.h"
+#include "../hidapi/SDL_hidapijoystick_c.h"
/* This isn't defined in older Linux kernel headers */
#ifndef SYN_DROPPED
@@ -76,7 +78,6 @@ typedef struct SDL_joylist_item
static SDL_joylist_item *SDL_joylist = NULL;
static SDL_joylist_item *SDL_joylist_tail = NULL;
static int numjoysticks = 0;
-static int instance_counter = 0;
#define test_bit(nr, addr) \
@@ -209,6 +210,13 @@ IsJoystick(int fd, char *namebuf, const size_t namebuflen, SDL_JoystickGUID *gui
return 0;
}
+#ifdef SDL_JOYSTICK_HIDAPI
+ if (HIDAPI_IsDevicePresent(inpid.vendor, inpid.product)) {
+ /* The HIDAPI driver is taking care of this device */
+ return 0;
+ }
+#endif
+
/* Check the joystick blacklist */
id = MAKE_VIDPID(inpid.vendor, inpid.product);
for (i = 0; i < SDL_arraysize(joystick_blacklist); ++i) {
@@ -239,8 +247,7 @@ IsJoystick(int fd, char *namebuf, const size_t namebuflen, SDL_JoystickGUID *gui
SDL_strlcpy((char*)guid16, namebuf, sizeof(guid->data) - 4);
}
- if (SDL_IsGameControllerNameAndGUID(namebuf, *guid) &&
- SDL_ShouldIgnoreGameController(namebuf, *guid)) {
+ if (SDL_ShouldIgnoreJoystick(namebuf, *guid)) {
return 0;
}
return 1;
@@ -325,14 +332,14 @@ MaybeAddDevice(const char *path)
item->name = SDL_strdup(namebuf);
item->guid = guid;
- if ( (item->path == NULL) || (item->name == NULL) ) {
+ if ((item->path == NULL) || (item->name == NULL)) {
SDL_free(item->path);
SDL_free(item->name);
SDL_free(item);
return -1;
}
- item->device_instance = instance_counter++;
+ item->device_instance = SDL_GetNextJoystickInstanceID();
if (SDL_joylist_tail == NULL) {
SDL_joylist = SDL_joylist_tail = item;
} else {
@@ -343,7 +350,7 @@ MaybeAddDevice(const char *path)
/* Need to increment the joystick count before we post the event */
++numjoysticks;
- SDL_PrivateJoystickAdded(numjoysticks - 1);
+ SDL_PrivateJoystickAdded(item->device_instance);
return numjoysticks;
}
@@ -409,7 +416,7 @@ JoystickInitWithoutUdev(void)
MaybeAddDevice(path);
}
- return numjoysticks;
+ return 0;
}
#endif
@@ -430,7 +437,7 @@ JoystickInitWithUdev(void)
/* Force a scan to build the initial device list */
SDL_UDEV_Scan();
- return numjoysticks;
+ return 0;
}
#endif
@@ -455,7 +462,7 @@ static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickG
return SDL_FALSE;
}
- *device_instance = item->device_instance = instance_counter++;
+ *device_instance = item->device_instance = SDL_GetNextJoystickInstanceID();
if (SDL_joylist_tail == NULL) {
SDL_joylist = SDL_joylist_tail = item;
} else {
@@ -466,7 +473,7 @@ static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickG
/* Need to increment the joystick count before we post the event */
++numjoysticks;
- SDL_PrivateJoystickAdded(numjoysticks - 1);
+ SDL_PrivateJoystickAdded(item->device_instance);
return SDL_TRUE;
}
@@ -505,8 +512,8 @@ static void SteamControllerDisconnectedCallback(int device_instance)
}
}
-int
-SDL_SYS_JoystickInit(void)
+static int
+LINUX_JoystickInit(void)
{
/* First see if the user specified one or more joysticks to use */
if (SDL_getenv("SDL_JOYSTICK_DEVICE") != NULL) {
@@ -534,14 +541,14 @@ SDL_SYS_JoystickInit(void)
#endif
}
-int
-SDL_SYS_NumJoysticks(void)
+static int
+LINUX_JoystickGetCount(void)
{
return numjoysticks;
}
-void
-SDL_SYS_JoystickDetect(void)
+static void
+LINUX_JoystickDetect(void)
{
#if SDL_USE_LIBUDEV
SDL_UDEV_Poll();
@@ -569,14 +576,21 @@ JoystickByDevIndex(int device_index)
}
/* Function to get the device-dependent name of a joystick */
-const char *
-SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+static const char *
+LINUX_JoystickGetDeviceName(int device_index)
{
return JoystickByDevIndex(device_index)->name;
}
+static SDL_JoystickGUID
+LINUX_JoystickGetDeviceGUID( int device_index )
+{
+ return JoystickByDevIndex(device_index)->guid;
+}
+
/* Function to perform the mapping from device index to the instance id for this index */
-SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+static SDL_JoystickID
+LINUX_JoystickGetDeviceInstanceID(int device_index)
{
return JoystickByDevIndex(device_index)->device_instance;
}
@@ -624,6 +638,7 @@ ConfigJoystick(SDL_Joystick * joystick, int fd)
unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
unsigned long relbit[NBITS(REL_MAX)] = { 0 };
+ unsigned long ffbit[NBITS(FF_MAX)] = { 0 };
/* See if this device uses the new unified event API */
if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&
@@ -719,6 +734,15 @@ ConfigJoystick(SDL_Joystick * joystick, int fd)
}
}
}
+
+ if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) >= 0) {
+ if (test_bit(FF_RUMBLE, ffbit)) {
+ joystick->hwdata->ff_rumble = SDL_TRUE;
+ }
+ if (test_bit(FF_SINE, ffbit)) {
+ joystick->hwdata->ff_sine = SDL_TRUE;
+ }
+ }
}
@@ -727,8 +751,8 @@ ConfigJoystick(SDL_Joystick * joystick, int fd)
This should fill the nbuttons and naxes fields of the joystick structure.
It returns 0, or -1 if there is an error.
*/
-int
-SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
+static int
+LINUX_JoystickOpen(SDL_Joystick * joystick, int device_index)
{
SDL_joylist_item *item = JoystickByDevIndex(device_index);
@@ -744,6 +768,7 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
}
joystick->hwdata->item = item;
joystick->hwdata->guid = item->guid;
+ joystick->hwdata->effect.id = -1;
joystick->hwdata->m_bSteamController = item->m_bSteamController;
if (item->m_bSteamController) {
@@ -752,7 +777,7 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
&joystick->naxes,
&joystick->nhats);
} else {
- int fd = open(item->path, O_RDONLY, 0);
+ int fd = open(item->path, O_RDWR, 0);
if (fd < 0) {
SDL_free(joystick->hwdata);
joystick->hwdata = NULL;
@@ -785,11 +810,52 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
}
/* Function to determine if this joystick is attached to the system right now */
-SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
+static SDL_bool
+LINUX_JoystickIsAttached(SDL_Joystick *joystick)
{
return joystick->hwdata->item != NULL;
}
+static int
+LINUX_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ struct input_event event;
+
+ if (joystick->hwdata->effect.id < 0) {
+ if (joystick->hwdata->ff_rumble) {
+ struct ff_effect *effect = &joystick->hwdata->effect;
+
+ effect->type = FF_RUMBLE;
+ effect->replay.length = SDL_min(duration_ms, 32767);
+ effect->u.rumble.strong_magnitude = low_frequency_rumble;
+ effect->u.rumble.weak_magnitude = high_frequency_rumble;
+ } else if (joystick->hwdata->ff_sine) {
+ /* Scale and average the two rumble strengths */
+ Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
+ struct ff_effect *effect = &joystick->hwdata->effect;
+
+ effect->type = FF_PERIODIC;
+ effect->replay.length = SDL_min(duration_ms, 32767);
+ effect->u.periodic.waveform = FF_SINE;
+ effect->u.periodic.magnitude = magnitude;
+ } else {
+ return SDL_Unsupported();
+ }
+ }
+
+ if (ioctl(joystick->hwdata->fd, EVIOCSFF, &joystick->hwdata->effect) < 0) {
+ return SDL_SetError("Couldn't update rumble effect: %s", strerror(errno));
+ }
+
+ event.type = EV_FF;
+ event.code = joystick->hwdata->effect.id;
+ event.value = 1;
+ if (write(joystick->hwdata->fd, &event, sizeof(event)) < 0) {
+ return SDL_SetError("Couldn't start rumble effect: %s", strerror(errno));
+ }
+ return 0;
+}
+
static SDL_INLINE void
HandleHat(SDL_Joystick * stick, Uint8 hat, int axis, int value)
{
@@ -963,8 +1029,8 @@ HandleInputEvents(SDL_Joystick * joystick)
}
}
-void
-SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+static void
+LINUX_JoystickUpdate(SDL_Joystick * joystick)
{
int i;
@@ -990,10 +1056,14 @@ SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
}
/* Function to close a joystick after use */
-void
-SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+static void
+LINUX_JoystickClose(SDL_Joystick * joystick)
{
if (joystick->hwdata) {
+ if (joystick->hwdata->effect.id >= 0) {
+ ioctl(joystick->hwdata->fd, EVIOCRMFF, joystick->hwdata->effect.id);
+ joystick->hwdata->effect.id = -1;
+ }
if (joystick->hwdata->fd >= 0) {
close(joystick->hwdata->fd);
}
@@ -1008,8 +1078,8 @@ SDL_SYS_JoystickClose(SDL_Joystick * joystick)
}
/* Function to perform any system-specific joystick related cleanup */
-void
-SDL_SYS_JoystickQuit(void)
+static void
+LINUX_JoystickQuit(void)
{
SDL_joylist_item *item = NULL;
SDL_joylist_item *next = NULL;
@@ -1024,7 +1094,6 @@ SDL_SYS_JoystickQuit(void)
SDL_joylist = SDL_joylist_tail = NULL;
numjoysticks = 0;
- instance_counter = 0;
#if SDL_USE_LIBUDEV
SDL_UDEV_DelCallback(joystick_udev_callback);
@@ -1034,15 +1103,21 @@ SDL_SYS_JoystickQuit(void)
SDL_QuitSteamControllers();
}
-SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
+SDL_JoystickDriver SDL_LINUX_JoystickDriver =
{
- return JoystickByDevIndex(device_index)->guid;
-}
-
-SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
-{
- return joystick->hwdata->guid;
-}
+ LINUX_JoystickInit,
+ LINUX_JoystickGetCount,
+ LINUX_JoystickDetect,
+ LINUX_JoystickGetDeviceName,
+ LINUX_JoystickGetDeviceGUID,
+ LINUX_JoystickGetDeviceInstanceID,
+ LINUX_JoystickOpen,
+ LINUX_JoystickIsAttached,
+ LINUX_JoystickRumble,
+ LINUX_JoystickUpdate,
+ LINUX_JoystickClose,
+ LINUX_JoystickQuit,
+};
#endif /* SDL_JOYSTICK_LINUX */
diff --git a/src/joystick/linux/SDL_sysjoystick_c.h b/src/joystick/linux/SDL_sysjoystick_c.h
index d06b387ee..617e3200d 100644
--- a/src/joystick/linux/SDL_sysjoystick_c.h
+++ b/src/joystick/linux/SDL_sysjoystick_c.h
@@ -31,6 +31,10 @@ struct joystick_hwdata
SDL_JoystickGUID guid;
char *fname; /* Used in haptic subsystem */
+ SDL_bool ff_rumble;
+ SDL_bool ff_sine;
+ struct ff_effect effect;
+
/* The current Linux joystick driver maps hats to two axes */
struct hwdata_hat
{
diff --git a/src/joystick/windows/SDL_dinputjoystick.c b/src/joystick/windows/SDL_dinputjoystick.c
index cff868b28..b6691c222 100644
--- a/src/joystick/windows/SDL_dinputjoystick.c
+++ b/src/joystick/windows/SDL_dinputjoystick.c
@@ -27,6 +27,7 @@
#include "SDL_windowsjoystick_c.h"
#include "SDL_dinputjoystick_c.h"
#include "SDL_xinputjoystick_c.h"
+#include "../hidapi/SDL_hidapijoystick_c.h"
#ifndef DIDFT_OPTIONAL
#define DIDFT_OPTIONAL 0x80000000
@@ -35,6 +36,8 @@
#define INPUT_QSIZE 32 /* Buffer up to 32 input messages */
#define JOY_AXIS_THRESHOLD (((SDL_JOYSTICK_AXIS_MAX)-(SDL_JOYSTICK_AXIS_MIN))/100) /* 1% motion */
+#define CONVERT_MAGNITUDE(x) (((x)*10000) / 0x7FFF)
+
/* external variables referenced. */
extern HWND SDL_HelperWindow;
@@ -46,170 +49,170 @@ static UINT SDL_RawDevListCount = 0;
/* Taken from Wine - Thanks! */
static DIOBJECTDATAFORMAT dfDIJoystick2[] = {
- { &GUID_XAxis, DIJOFS_X, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_YAxis, DIJOFS_Y, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_ZAxis, DIJOFS_Z, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_RxAxis, DIJOFS_RX, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_RyAxis, DIJOFS_RY, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_RzAxis, DIJOFS_RZ, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_POV, DIJOFS_POV(0), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
- { &GUID_POV, DIJOFS_POV(1), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
- { &GUID_POV, DIJOFS_POV(2), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
- { &GUID_POV, DIJOFS_POV(3), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(0), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(1), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(2), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(3), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(4), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(5), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(6), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(7), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(8), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(9), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(10), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(11), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(12), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(13), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(14), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(15), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(16), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(17), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(18), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(19), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(20), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(21), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(22), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(23), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(24), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(25), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(26), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(27), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(28), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(29), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(30), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(31), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(32), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(33), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(34), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(35), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(36), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(37), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(38), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(39), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(40), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(41), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(42), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(43), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(44), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(45), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(46), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(47), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(48), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(49), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(50), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(51), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(52), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(53), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(54), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(55), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(56), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(57), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(58), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(59), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(60), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(61), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(62), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(63), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(64), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(65), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(66), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(67), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(68), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(69), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(70), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(71), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(72), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(73), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(74), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(75), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(76), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(77), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(78), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(79), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(80), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(81), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(82), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(83), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(84), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(85), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(86), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(87), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(88), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(89), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(90), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(91), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(92), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(93), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(94), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(95), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(96), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(97), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(98), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(99), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(100), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(101), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(102), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(103), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(104), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(105), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(106), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(107), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(108), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(109), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(110), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(111), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(112), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(113), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(114), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(115), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(116), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(117), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(118), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(119), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(120), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(121), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(122), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(123), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(124), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(125), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(126), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { NULL, DIJOFS_BUTTON(127), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
- { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lVX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lVY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lVZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lVRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lVRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lVRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lAX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lAY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lAZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lARx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lARy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lARz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lFX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lFY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lFZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lFRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lFRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lFRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
- { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_XAxis, DIJOFS_X, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_YAxis, DIJOFS_Y, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_ZAxis, DIJOFS_Z, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_RxAxis, DIJOFS_RX, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_RyAxis, DIJOFS_RY, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_RzAxis, DIJOFS_RZ, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_POV, DIJOFS_POV(0), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_POV, DIJOFS_POV(1), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_POV, DIJOFS_POV(2), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_POV, DIJOFS_POV(3), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(0), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(1), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(2), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(3), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(4), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(5), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(6), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(7), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(8), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(9), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(10), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(11), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(12), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(13), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(14), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(15), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(16), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(17), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(18), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(19), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(20), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(21), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(22), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(23), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(24), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(25), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(26), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(27), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(28), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(29), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(30), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(31), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(32), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(33), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(34), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(35), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(36), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(37), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(38), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(39), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(40), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(41), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(42), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(43), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(44), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(45), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(46), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(47), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(48), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(49), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(50), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(51), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(52), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(53), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(54), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(55), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(56), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(57), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(58), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(59), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(60), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(61), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(62), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(63), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(64), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(65), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(66), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(67), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(68), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(69), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(70), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(71), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(72), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(73), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(74), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(75), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(76), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(77), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(78), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(79), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(80), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(81), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(82), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(83), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(84), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(85), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(86), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(87), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(88), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(89), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(90), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(91), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(92), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(93), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(94), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(95), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(96), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(97), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(98), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(99), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(100), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(101), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(102), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(103), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(104), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(105), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(106), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(107), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(108), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(109), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(110), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(111), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(112), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(113), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(114), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(115), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(116), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(117), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(118), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(119), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(120), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(121), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(122), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(123), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(124), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(125), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(126), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { NULL, DIJOFS_BUTTON(127), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lVX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lVY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lVZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lVRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lVRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lVRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lAX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lAY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lAZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lARx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lARy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lARz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lFX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lFY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lFZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lFRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lFRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lFRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
+ { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
};
const DIDATAFORMAT SDL_c_dfDIJoystick2 = {
@@ -311,6 +314,61 @@ SDL_IsXInputDevice(const GUID* pGuidProductFromDirectInput)
return SDL_FALSE;
}
+void FreeRumbleEffectData(DIEFFECT *effect)
+{
+ if (!effect) {
+ return;
+ }
+ SDL_free(effect->rgdwAxes);
+ SDL_free(effect->rglDirection);
+ SDL_free(effect->lpvTypeSpecificParams);
+ SDL_free(effect);
+}
+
+DIEFFECT *CreateRumbleEffectData(Sint16 magnitude, Uint32 duration_ms)
+{
+ DIEFFECT *effect;
+ DIPERIODIC *periodic;
+
+ /* Create the effect */
+ effect = (DIEFFECT *)SDL_calloc(1, sizeof(*effect));
+ if (!effect) {
+ return NULL;
+ }
+ effect->dwSize = sizeof(*effect);
+ effect->dwGain = 10000;
+ effect->dwFlags = DIEFF_OBJECTOFFSETS;
+ effect->dwDuration = duration_ms * 1000; /* In microseconds. */
+ effect->dwTriggerButton = DIEB_NOTRIGGER;
+
+ effect->cAxes = 2;
+ effect->rgdwAxes = (DWORD *)SDL_calloc(effect->cAxes, sizeof(DWORD));
+ if (!effect->rgdwAxes) {
+ FreeRumbleEffectData(effect);
+ return NULL;
+ }
+
+ effect->rglDirection = (LONG *)SDL_calloc(effect->cAxes, sizeof(LONG));
+ if (!effect->rglDirection) {
+ FreeRumbleEffectData(effect);
+ return NULL;
+ }
+ effect->dwFlags |= DIEFF_CARTESIAN;
+
+ periodic = (DIPERIODIC *)SDL_calloc(1, sizeof(*periodic));
+ if (!periodic) {
+ FreeRumbleEffectData(effect);
+ return NULL;
+ }
+ periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
+ periodic->dwPeriod = 1000000;
+
+ effect->cbTypeSpecificParams = sizeof(*periodic);
+ effect->lpvTypeSpecificParams = periodic;
+
+ return effect;
+}
+
int
SDL_DINPUT_JoystickInit(void)
{
@@ -348,28 +406,29 @@ SDL_DINPUT_JoystickInit(void)
static BOOL CALLBACK
EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
{
- const Uint16 BUS_USB = 0x03;
- const Uint16 BUS_BLUETOOTH = 0x05;
JoyStick_DeviceData *pNewJoystick;
JoyStick_DeviceData *pPrevJoystick = NULL;
const DWORD devtype = (pdidInstance->dwDevType & 0xFF);
Uint16 *guid16;
+ Uint16 vendor = 0;
+ Uint16 product = 0;
+ Uint16 version = 0;
WCHAR hidPath[MAX_PATH];
if (devtype == DI8DEVTYPE_SUPPLEMENTAL) {
/* Add any supplemental devices that should be ignored here */
-#define MAKE_TABLE_ENTRY(VID, PID) ((((DWORD)PID)<<16)|VID)
- static DWORD ignored_devices[] = {
- MAKE_TABLE_ENTRY(0, 0)
- };
+#define MAKE_TABLE_ENTRY(VID, PID) ((((DWORD)PID)<<16)|VID)
+ static DWORD ignored_devices[] = {
+ MAKE_TABLE_ENTRY(0, 0)
+ };
#undef MAKE_TABLE_ENTRY
- unsigned int i;
+ unsigned int i;
- for (i = 0; i < SDL_arraysize(ignored_devices); ++i) {
- if (pdidInstance->guidProduct.Data1 == ignored_devices[i]) {
- return DIENUM_CONTINUE;
- }
- }
+ for (i = 0; i < SDL_arraysize(ignored_devices); ++i) {
+ if (pdidInstance->guidProduct.Data1 == ignored_devices[i]) {
+ return DIENUM_CONTINUE;
+ }
+ }
}
if (SDL_IsXInputDevice(&pdidInstance->guidProduct)) {
@@ -452,27 +511,38 @@ EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
guid16 = (Uint16 *)pNewJoystick->guid.data;
if (SDL_memcmp(&pdidInstance->guidProduct.Data4[2], "PIDVID", 6) == 0) {
- *guid16++ = SDL_SwapLE16(BUS_USB);
+ vendor = (Uint16)LOWORD(pdidInstance->guidProduct.Data1);
+ product = (Uint16)HIWORD(pdidInstance->guidProduct.Data1);
+ version = 0;
+
+ *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
*guid16++ = 0;
- *guid16++ = SDL_SwapLE16((Uint16)LOWORD(pdidInstance->guidProduct.Data1)); /* vendor */
+ *guid16++ = SDL_SwapLE16(vendor);
*guid16++ = 0;
- *guid16++ = SDL_SwapLE16((Uint16)HIWORD(pdidInstance->guidProduct.Data1)); /* product */
+ *guid16++ = SDL_SwapLE16(product);
*guid16++ = 0;
- *guid16++ = 0; /* version */
+ *guid16++ = SDL_SwapLE16(version);
*guid16++ = 0;
} else {
- *guid16++ = SDL_SwapLE16(BUS_BLUETOOTH);
+ *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
*guid16++ = 0;
SDL_strlcpy((char*)guid16, pNewJoystick->joystickname, sizeof(pNewJoystick->guid.data) - 4);
}
- if (SDL_IsGameControllerNameAndGUID(pNewJoystick->joystickname, pNewJoystick->guid) &&
- SDL_ShouldIgnoreGameController(pNewJoystick->joystickname, pNewJoystick->guid)) {
+ if (SDL_ShouldIgnoreJoystick(pNewJoystick->joystickname, pNewJoystick->guid)) {
SDL_free(pNewJoystick);
return DIENUM_CONTINUE;
}
- SDL_SYS_AddJoystickDevice(pNewJoystick);
+#ifdef SDL_JOYSTICK_HIDAPI
+ if (HIDAPI_IsDevicePresent(vendor, product)) {
+ /* The HIDAPI driver is taking care of this device */
+ SDL_free(pNewJoystick);
+ return DIENUM_CONTINUE;
+ }
+#endif
+
+ WINDOWS_AddJoystickDevice(pNewJoystick);
return DIENUM_CONTINUE; /* get next device, please */
}
@@ -683,7 +753,6 @@ SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickde
/* Force capable? */
if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
-
result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
if (FAILED(result)) {
return SetDIerror("IDirectInputDevice8::Acquire", result);
@@ -752,6 +821,89 @@ SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickde
return 0;
}
+static int
+SDL_DINPUT_JoystickInitRumble(SDL_Joystick * joystick, Sint16 magnitude, Uint32 duration_ms)
+{
+ HRESULT result;
+
+ /* Reset and then enable actuators */
+ result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_RESET);
+ if (result == DIERR_INPUTLOST || result == DIERR_NOTEXCLUSIVEACQUIRED) {
+ result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
+ if (SUCCEEDED(result)) {
+ result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_RESET);
+ }
+ }
+ if (FAILED(result)) {
+ return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand(DISFFC_RESET)", result);
+ }
+
+ result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_SETACTUATORSON);
+ if (FAILED(result)) {
+ return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand(DISFFC_SETACTUATORSON)", result);
+ }
+
+ /* Create the effect */
+ joystick->hwdata->ffeffect = CreateRumbleEffectData(magnitude, duration_ms);
+ if (!joystick->hwdata->ffeffect) {
+ return SDL_OutOfMemory();
+ }
+
+ result = IDirectInputDevice8_CreateEffect(joystick->hwdata->InputDevice, &GUID_Sine,
+ joystick->hwdata->ffeffect, &joystick->hwdata->ffeffect_ref, NULL);
+ if (FAILED(result)) {
+ return SetDIerror("IDirectInputDevice8::CreateEffect", result);
+ }
+ return 0;
+}
+
+int
+SDL_DINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ HRESULT result;
+
+ /* Scale and average the two rumble strengths */
+ Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
+
+ if (!(joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK)) {
+ return SDL_Unsupported();
+ }
+
+ if (joystick->hwdata->ff_initialized) {
+ DIPERIODIC *periodic = ((DIPERIODIC *)joystick->hwdata->ffeffect->lpvTypeSpecificParams);
+ joystick->hwdata->ffeffect->dwDuration = duration_ms * 1000; /* In microseconds. */
+ periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
+
+ result = IDirectInputEffect_SetParameters(joystick->hwdata->ffeffect_ref, joystick->hwdata->ffeffect, (DIEP_DURATION | DIEP_TYPESPECIFICPARAMS));
+ if (result == DIERR_INPUTLOST) {
+ result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
+ if (SUCCEEDED(result)) {
+ result = IDirectInputEffect_SetParameters(joystick->hwdata->ffeffect_ref, joystick->hwdata->ffeffect, (DIEP_DURATION | DIEP_TYPESPECIFICPARAMS));
+ }
+ }
+ if (FAILED(result)) {
+ return SetDIerror("IDirectInputDevice8::SetParameters", result);
+ }
+ } else {
+ if (SDL_DINPUT_JoystickInitRumble(joystick, magnitude, duration_ms) < 0) {
+ return -1;
+ }
+ joystick->hwdata->ff_initialized = SDL_TRUE;
+ }
+
+ result = IDirectInputEffect_Start(joystick->hwdata->ffeffect_ref, 1, 0);
+ if (result == DIERR_INPUTLOST || result == DIERR_NOTEXCLUSIVEACQUIRED) {
+ result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
+ if (SUCCEEDED(result)) {
+ result = IDirectInputEffect_Start(joystick->hwdata->ffeffect_ref, 1, 0);
+ }
+ }
+ if (FAILED(result)) {
+ return SetDIerror("IDirectInputDevice8::Start", result);
+ }
+ return 0;
+}
+
static Uint8
TranslatePOV(DWORD value)
{
@@ -933,8 +1085,17 @@ SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick)
void
SDL_DINPUT_JoystickClose(SDL_Joystick * joystick)
{
+ if (joystick->hwdata->ffeffect_ref) {
+ IDirectInputEffect_Unload(joystick->hwdata->ffeffect_ref);
+ joystick->hwdata->ffeffect_ref = NULL;
+ }
+ if (joystick->hwdata->ffeffect) {
+ FreeRumbleEffectData(joystick->hwdata->ffeffect);
+ joystick->hwdata->ffeffect = NULL;
+ }
IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
IDirectInputDevice8_Release(joystick->hwdata->InputDevice);
+ joystick->hwdata->ff_initialized = SDL_FALSE;
}
void
@@ -972,6 +1133,12 @@ SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickde
return SDL_Unsupported();
}
+int
+SDL_DINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ return SDL_Unsupported();
+}
+
void
SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick)
{
diff --git a/src/joystick/windows/SDL_dinputjoystick_c.h b/src/joystick/windows/SDL_dinputjoystick_c.h
index 5cc18903b..9f29fc751 100644
--- a/src/joystick/windows/SDL_dinputjoystick_c.h
+++ b/src/joystick/windows/SDL_dinputjoystick_c.h
@@ -23,6 +23,7 @@
extern int SDL_DINPUT_JoystickInit(void);
extern void SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext);
extern int SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice);
+extern int SDL_DINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
extern void SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick);
extern void SDL_DINPUT_JoystickClose(SDL_Joystick * joystick);
extern void SDL_DINPUT_JoystickQuit(void);
diff --git a/src/joystick/windows/SDL_windowsjoystick.c b/src/joystick/windows/SDL_windowsjoystick.c
index 45cbea688..47866b50c 100644
--- a/src/joystick/windows/SDL_windowsjoystick.c
+++ b/src/joystick/windows/SDL_windowsjoystick.c
@@ -61,7 +61,6 @@
/* local variables */
static SDL_bool s_bDeviceAdded = SDL_FALSE;
static SDL_bool s_bDeviceRemoved = SDL_FALSE;
-static SDL_JoystickID s_nInstanceID = -1;
static SDL_cond *s_condJoystickThread = NULL;
static SDL_mutex *s_mutexJoyStickEnum = NULL;
static SDL_Thread *s_threadJoystick = NULL;
@@ -271,30 +270,33 @@ SDL_JoystickThread(void *_data)
return 1;
}
-void SDL_SYS_AddJoystickDevice(JoyStick_DeviceData *device)
+void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device)
{
device->send_add_event = SDL_TRUE;
- device->nInstanceID = ++s_nInstanceID;
+ device->nInstanceID = SDL_GetNextJoystickInstanceID();
device->pNext = SYS_Joystick;
SYS_Joystick = device;
s_bDeviceAdded = SDL_TRUE;
}
+static void WINDOWS_JoystickDetect(void);
+static void WINDOWS_JoystickQuit(void);
+
/* Function to scan the system for joysticks.
* Joystick 0 should be the system default joystick.
* It should return 0, or -1 on an unrecoverable fatal error.
*/
-int
-SDL_SYS_JoystickInit(void)
+static int
+WINDOWS_JoystickInit(void)
{
if (SDL_DINPUT_JoystickInit() < 0) {
- SDL_SYS_JoystickQuit();
+ WINDOWS_JoystickQuit();
return -1;
}
if (SDL_XINPUT_JoystickInit() < 0) {
- SDL_SYS_JoystickQuit();
+ WINDOWS_JoystickQuit();
return -1;
}
@@ -302,19 +304,19 @@ SDL_SYS_JoystickInit(void)
s_condJoystickThread = SDL_CreateCond();
s_bDeviceAdded = SDL_TRUE; /* force a scan of the system for joysticks this first time */
- SDL_SYS_JoystickDetect();
+ WINDOWS_JoystickDetect();
if (!s_threadJoystick) {
/* spin up the thread to detect hotplug of devices */
s_bJoystickThreadQuit = SDL_FALSE;
s_threadJoystick = SDL_CreateThreadInternal(SDL_JoystickThread, "SDL_joystick", 64 * 1024, NULL);
}
- return SDL_SYS_NumJoysticks();
+ return 0;
}
/* return the number of joysticks that are connected right now */
-int
-SDL_SYS_NumJoysticks(void)
+static int
+WINDOWS_JoystickGetCount(void)
{
int nJoysticks = 0;
JoyStick_DeviceData *device = SYS_Joystick;
@@ -327,8 +329,8 @@ SDL_SYS_NumJoysticks(void)
}
/* detect any new joysticks being inserted into the system */
-void
-SDL_SYS_JoystickDetect(void)
+static void
+WINDOWS_JoystickDetect(void)
{
JoyStick_DeviceData *pCurList = NULL;
@@ -383,7 +385,7 @@ SDL_SYS_JoystickDetect(void)
SDL_DINPUT_MaybeAddDevice(&pNewJoystick->dxdevice);
}
- SDL_PrivateJoystickAdded(device_index);
+ SDL_PrivateJoystickAdded(pNewJoystick->nInstanceID);
pNewJoystick->send_add_event = SDL_FALSE;
}
@@ -394,8 +396,8 @@ SDL_SYS_JoystickDetect(void)
}
/* Function to get the device-dependent name of a joystick */
-const char *
-SDL_SYS_JoystickNameForDeviceIndex(int device_index)
+static const char *
+WINDOWS_JoystickGetDeviceName(int device_index)
{
JoyStick_DeviceData *device = SYS_Joystick;
@@ -405,9 +407,22 @@ SDL_SYS_JoystickNameForDeviceIndex(int device_index)
return device->joystickname;
}
+/* return the stable device guid for this device index */
+static SDL_JoystickGUID
+WINDOWS_JoystickGetDeviceGUID(int device_index)
+{
+ JoyStick_DeviceData *device = SYS_Joystick;
+ int index;
+
+ for (index = device_index; index > 0; index--)
+ device = device->pNext;
+
+ return device->guid;
+}
+
/* Function to perform the mapping between current device instance and this joysticks instance id */
-SDL_JoystickID
-SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
+static SDL_JoystickID
+WINDOWS_JoystickGetDeviceInstanceID(int device_index)
{
JoyStick_DeviceData *device = SYS_Joystick;
int index;
@@ -423,8 +438,8 @@ SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
This should fill the nbuttons and naxes fields of the joystick structure.
It returns 0, or -1 if there is an error.
*/
-int
-SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
+static int
+WINDOWS_JoystickOpen(SDL_Joystick * joystick, int device_index)
{
JoyStick_DeviceData *joystickdevice = SYS_Joystick;
@@ -449,14 +464,24 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
}
/* return true if this joystick is plugged in right now */
-SDL_bool
-SDL_SYS_JoystickAttached(SDL_Joystick * joystick)
+static SDL_bool
+WINDOWS_JoystickIsAttached(SDL_Joystick * joystick)
{
return joystick->hwdata && !joystick->hwdata->removed;
}
-void
-SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
+static int
+WINDOWS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ if (joystick->hwdata->bXInputDevice) {
+ return SDL_XINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms);
+ } else {
+ return SDL_DINPUT_JoystickRumble(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms);
+ }
+}
+
+static void
+WINDOWS_JoystickUpdate(SDL_Joystick * joystick)
{
if (!joystick->hwdata || joystick->hwdata->removed) {
return;
@@ -474,8 +499,8 @@ SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
}
/* Function to close a joystick after use */
-void
-SDL_SYS_JoystickClose(SDL_Joystick * joystick)
+static void
+WINDOWS_JoystickClose(SDL_Joystick * joystick)
{
if (joystick->hwdata->bXInputDevice) {
SDL_XINPUT_JoystickClose(joystick);
@@ -487,8 +512,8 @@ SDL_SYS_JoystickClose(SDL_Joystick * joystick)
}
/* Function to perform any system-specific joystick related cleanup */
-void
-SDL_SYS_JoystickQuit(void)
+static void
+WINDOWS_JoystickQuit(void)
{
JoyStick_DeviceData *device = SYS_Joystick;
@@ -524,24 +549,21 @@ SDL_SYS_JoystickQuit(void)
s_bDeviceRemoved = SDL_FALSE;
}
-/* return the stable device guid for this device index */
-SDL_JoystickGUID
-SDL_SYS_JoystickGetDeviceGUID(int device_index)
+SDL_JoystickDriver SDL_WINDOWS_JoystickDriver =
{
- JoyStick_DeviceData *device = SYS_Joystick;
- int index;
-
- for (index = device_index; index > 0; index--)
- device = device->pNext;
-
- return device->guid;
-}
-
-SDL_JoystickGUID
-SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
-{
- return joystick->hwdata->guid;
-}
+ WINDOWS_JoystickInit,
+ WINDOWS_JoystickGetCount,
+ WINDOWS_JoystickDetect,
+ WINDOWS_JoystickGetDeviceName,
+ WINDOWS_JoystickGetDeviceGUID,
+ WINDOWS_JoystickGetDeviceInstanceID,
+ WINDOWS_JoystickOpen,
+ WINDOWS_JoystickIsAttached,
+ WINDOWS_JoystickRumble,
+ WINDOWS_JoystickUpdate,
+ WINDOWS_JoystickClose,
+ WINDOWS_JoystickQuit,
+};
#endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */
diff --git a/src/joystick/windows/SDL_windowsjoystick_c.h b/src/joystick/windows/SDL_windowsjoystick_c.h
index 01b8b3ab1..2bccfb52f 100644
--- a/src/joystick/windows/SDL_windowsjoystick_c.h
+++ b/src/joystick/windows/SDL_windowsjoystick_c.h
@@ -68,6 +68,7 @@ struct joystick_hwdata
SDL_JoystickGUID guid;
SDL_bool removed;
SDL_bool send_remove_event;
+ Uint32 rumble_expiration;
#if SDL_JOYSTICK_DINPUT
LPDIRECTINPUTDEVICE8 InputDevice;
@@ -76,6 +77,9 @@ struct joystick_hwdata
input_t Inputs[MAX_INPUTS];
int NumInputs;
int NumSliders;
+ SDL_bool ff_initialized;
+ DIEFFECT *ffeffect;
+ LPDIRECTINPUTEFFECT ffeffect_ref;
#endif
SDL_bool bXInputDevice; /* SDL_TRUE if this device supports using the xinput API rather than DirectInput */
@@ -88,6 +92,6 @@ struct joystick_hwdata
extern const DIDATAFORMAT SDL_c_dfDIJoystick2;
#endif
-extern void SDL_SYS_AddJoystickDevice(JoyStick_DeviceData *device);
+extern void WINDOWS_AddJoystickDevice(JoyStick_DeviceData *device);
/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/joystick/windows/SDL_xinputjoystick.c b/src/joystick/windows/SDL_xinputjoystick.c
index 977a36bb3..88e33269d 100644
--- a/src/joystick/windows/SDL_xinputjoystick.c
+++ b/src/joystick/windows/SDL_xinputjoystick.c
@@ -26,8 +26,10 @@
#include "SDL_assert.h"
#include "SDL_hints.h"
+#include "SDL_timer.h"
#include "SDL_windowsjoystick_c.h"
#include "SDL_xinputjoystick_c.h"
+#include "../hidapi/SDL_hidapijoystick_c.h"
/*
* Internal stuff.
@@ -186,6 +188,9 @@ GuessXInputDevice(Uint8 userid, Uint16 *pVID, Uint16 *pPID, Uint16 *pVersion)
static void
AddXInputDevice(Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext)
{
+ Uint16 vendor = 0;
+ Uint16 product = 0;
+ Uint16 version = 0;
JoyStick_DeviceData *pPrevJoystick = NULL;
JoyStick_DeviceData *pNewJoystick = *pContext;
@@ -229,15 +234,11 @@ AddXInputDevice(Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext)
if (SDL_XInputUseOldJoystickMapping()) {
SDL_zero(pNewJoystick->guid);
} else {
- const Uint16 BUS_USB = 0x03;
- Uint16 vendor = 0;
- Uint16 product = 0;
- Uint16 version = 0;
Uint16 *guid16 = (Uint16 *)pNewJoystick->guid.data;
GuessXInputDevice(userid, &vendor, &product, &version);
- *guid16++ = SDL_SwapLE16(BUS_USB);
+ *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
*guid16++ = 0;
*guid16++ = SDL_SwapLE16(vendor);
*guid16++ = 0;
@@ -253,12 +254,20 @@ AddXInputDevice(Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext)
pNewJoystick->SubType = SubType;
pNewJoystick->XInputUserId = userid;
- if (SDL_ShouldIgnoreGameController(pNewJoystick->joystickname, pNewJoystick->guid)) {
+ if (SDL_ShouldIgnoreJoystick(pNewJoystick->joystickname, pNewJoystick->guid)) {
SDL_free(pNewJoystick);
return;
}
- SDL_SYS_AddJoystickDevice(pNewJoystick);
+#ifdef SDL_JOYSTICK_HIDAPI
+ if (HIDAPI_IsDevicePresent(vendor, product)) {
+ /* The HIDAPI driver is taking care of this device */
+ SDL_free(pNewJoystick);
+ return;
+ }
+#endif
+
+ WINDOWS_AddJoystickDevice(pNewJoystick);
}
void
@@ -384,12 +393,12 @@ UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState
Uint8 button;
Uint8 hat = SDL_HAT_CENTERED;
- SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX);
- SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)));
- SDL_PrivateJoystickAxis(joystick, 2, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768));
- SDL_PrivateJoystickAxis(joystick, 3, (Sint16)pXInputState->Gamepad.sThumbRX);
- SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)));
- SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768));
+ SDL_PrivateJoystickAxis(joystick, 0, pXInputState->Gamepad.sThumbLX);
+ SDL_PrivateJoystickAxis(joystick, 1, ~pXInputState->Gamepad.sThumbLY);
+ SDL_PrivateJoystickAxis(joystick, 2, ((int)pXInputState->Gamepad.bLeftTrigger * 257) - 32768);
+ SDL_PrivateJoystickAxis(joystick, 3, pXInputState->Gamepad.sThumbRX);
+ SDL_PrivateJoystickAxis(joystick, 4, ~pXInputState->Gamepad.sThumbRY);
+ SDL_PrivateJoystickAxis(joystick, 5, ((int)pXInputState->Gamepad.bRightTrigger * 257) - 32768);
for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
@@ -412,6 +421,29 @@ UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState
UpdateXInputJoystickBatteryInformation(joystick, pBatteryInformation);
}
+int
+SDL_XINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ XINPUT_VIBRATION XVibration;
+
+ if (!XINPUTSETSTATE) {
+ return SDL_Unsupported();
+ }
+
+ XVibration.wLeftMotorSpeed = low_frequency_rumble;
+ XVibration.wRightMotorSpeed = high_frequency_rumble;
+ if (XINPUTSETSTATE(joystick->hwdata->userid, &XVibration) != ERROR_SUCCESS) {
+ return SDL_SetError("XInputSetState() failed");
+ }
+
+ if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
+ joystick->hwdata->rumble_expiration = SDL_GetTicks() + duration_ms;
+ } else {
+ joystick->hwdata->rumble_expiration = 0;
+ }
+ return 0;
+}
+
void
SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
{
@@ -449,6 +481,13 @@ SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
}
joystick->hwdata->dwPacketNumber = XInputState.dwPacketNumber;
}
+
+ if (joystick->hwdata->rumble_expiration) {
+ Uint32 now = SDL_GetTicks();
+ if (SDL_TICKS_PASSED(now, joystick->hwdata->rumble_expiration)) {
+ SDL_XINPUT_JoystickRumble(joystick, 0, 0, 0);
+ }
+ }
}
void
@@ -490,6 +529,12 @@ SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickde
return SDL_Unsupported();
}
+int
+SDL_XINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+{
+ return SDL_Unsupported();
+}
+
void
SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
{
diff --git a/src/joystick/windows/SDL_xinputjoystick_c.h b/src/joystick/windows/SDL_xinputjoystick_c.h
index 63616ee30..8d57b62f6 100644
--- a/src/joystick/windows/SDL_xinputjoystick_c.h
+++ b/src/joystick/windows/SDL_xinputjoystick_c.h
@@ -26,6 +26,7 @@ extern SDL_bool SDL_XINPUT_Enabled(void);
extern int SDL_XINPUT_JoystickInit(void);
extern void SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext);
extern int SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice);
+extern int SDL_XINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
extern void SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick);
extern void SDL_XINPUT_JoystickClose(SDL_Joystick * joystick);
extern void SDL_XINPUT_JoystickQuit(void);
diff --git a/src/stdlib/SDL_string.c b/src/stdlib/SDL_string.c
index 5cbcc4bba..22aceedb1 100644
--- a/src/stdlib/SDL_string.c
+++ b/src/stdlib/SDL_string.c
@@ -416,6 +416,17 @@ SDL_strlen(const char *string)
#endif /* HAVE_STRLEN */
}
+wchar_t *
+SDL_wcsdup(const wchar_t *string)
+{
+ size_t len = ((SDL_wcslen(string) + 1) * sizeof(wchar_t));
+ wchar_t *newstr = (wchar_t *)SDL_malloc(len);
+ if (newstr) {
+ SDL_memcpy(newstr, string, len);
+ }
+ return newstr;
+}
+
size_t
SDL_wcslen(const wchar_t * string)
{
@@ -562,7 +573,7 @@ char *
SDL_strdup(const char *string)
{
size_t len = SDL_strlen(string) + 1;
- char *newstr = SDL_malloc(len);
+ char *newstr = (char *)SDL_malloc(len);
if (newstr) {
SDL_memcpy(newstr, string, len);
}
diff --git a/test/testgamecontroller.c b/test/testgamecontroller.c
index ef3a02b2f..c8616d7cd 100644
--- a/test/testgamecontroller.c
+++ b/test/testgamecontroller.c
@@ -114,6 +114,11 @@ loop(void *arg)
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
SDL_Log("Controller button %s %s\n", SDL_GameControllerGetStringForButton((SDL_GameControllerButton)event.cbutton.button), event.cbutton.state ? "pressed" : "released");
+ /* First button triggers a 0.5 second full strength rumble */
+ if (event.type == SDL_CONTROLLERBUTTONDOWN &&
+ event.cbutton.button == SDL_CONTROLLER_BUTTON_A) {
+ SDL_GameControllerRumble(gamecontroller, 0xFFFF, 0xFFFF, 500);
+ }
break;
case SDL_KEYDOWN:
if (event.key.keysym.sym != SDLK_ESCAPE) {
diff --git a/test/testjoystick.c b/test/testjoystick.c
index d2d1c4e2c..bca749244 100644
--- a/test/testjoystick.c
+++ b/test/testjoystick.c
@@ -90,6 +90,10 @@ loop(void *arg)
case SDL_JOYBUTTONDOWN:
SDL_Log("Joystick %d button %d down\n",
event.jbutton.which, event.jbutton.button);
+ /* First button triggers a 0.5 second full strength rumble */
+ if (event.jbutton.button == 0) {
+ SDL_JoystickRumble(joystick, 0xFFFF, 0xFFFF, 500);
+ }
break;
case SDL_JOYBUTTONUP:
SDL_Log("Joystick %d button %d up\n",