From ae55666a5f9eaebd638707b3ee21b4fcfc9cbf9e Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 26 Nov 2012 16:37:54 -0800 Subject: [PATCH] Added hotplug joystick support and simplified game controller API, courtesy of Alfred Reynolds --- Makefile.in | 1 + VisualC/SDL/SDL_VS2010.vcxproj | 10 +- .../testgamecontroller_VS2010.vcxproj | 227 ++++ Xcode/SDL/SDL.xcodeproj/project.pbxproj | 5 + .../SDLTest/SDLTest.xcodeproj/project.pbxproj | 202 +++ Xcode/SDLTest/testgamecontroller-Info.plist | 32 + include/SDL.h | 2 + include/SDL_events.h | 69 +- include/SDL_gamecontroller.h | 257 ++++ include/SDL_hints.h | 8 + include/SDL_joystick.h | 57 +- src/SDL.c | 143 +- src/events/SDL_events.c | 2 +- src/haptic/SDL_haptic.c | 4 +- src/haptic/windows/SDL_syshaptic.c | 2 +- src/joystick/SDL_gamecontroller.c | 1125 ++++++++++++++++ src/joystick/SDL_joystick.c | 415 ++++-- src/joystick/SDL_joystick_c.h | 13 +- src/joystick/SDL_sysjoystick.h | 44 +- src/joystick/beos/SDL_bejoystick.cc | 0 src/joystick/darwin/SDL_sysjoystick.c | 411 ++++-- src/joystick/darwin/SDL_sysjoystick_c.h | 9 +- .../iphoneos/SDLUIAccelerationDelegate.m | 0 src/joystick/iphoneos/SDL_sysjoystick.m | 0 src/joystick/linux/SDL_sysjoystick.c | 108 +- src/joystick/windows/SDL_dxjoystick.c | 1183 ++++++++++++++--- src/joystick/windows/SDL_dxjoystick_c.h | 36 +- test/Makefile.in | 4 + test/testgamecontroller.c | 205 +++ test/testjoystick.c | 219 +-- 30 files changed, 4202 insertions(+), 591 deletions(-) create mode 100644 VisualC/tests/testgamecontroller/testgamecontroller_VS2010.vcxproj create mode 100644 Xcode/SDLTest/testgamecontroller-Info.plist create mode 100644 include/SDL_gamecontroller.h create mode 100644 src/joystick/SDL_gamecontroller.c mode change 100644 => 100755 src/joystick/beos/SDL_bejoystick.cc mode change 100644 => 100755 src/joystick/iphoneos/SDLUIAccelerationDelegate.m mode change 100644 => 100755 src/joystick/iphoneos/SDL_sysjoystick.m create mode 100644 test/testgamecontroller.c diff --git a/Makefile.in b/Makefile.in index efc3f46ac..a6286c814 100644 --- a/Makefile.in +++ b/Makefile.in @@ -53,6 +53,7 @@ HDRS = \ SDL_endian.h \ SDL_error.h \ SDL_events.h \ + SDL_gamecontroller.h \ SDL_gesture.h \ SDL_haptic.h \ SDL_hints.h \ diff --git a/VisualC/SDL/SDL_VS2010.vcxproj b/VisualC/SDL/SDL_VS2010.vcxproj index b3b7c8834..8b5d18883 100644 --- a/VisualC/SDL/SDL_VS2010.vcxproj +++ b/VisualC/SDL/SDL_VS2010.vcxproj @@ -98,7 +98,7 @@ 0x0409 - winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) + winmm.lib;imm32.lib;oleaut32.lib;version.lib;%(AdditionalDependencies) true true Windows @@ -129,7 +129,7 @@ 0x0409 - winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) + winmm.lib;imm32.lib;oleaut32.lib;version.lib;%(AdditionalDependencies) true true Windows @@ -165,7 +165,7 @@ 0x0409 - winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) + winmm.lib;imm32.lib;oleaut32.lib;version.lib;%(AdditionalDependencies) true Windows $(DXSDK_DIR)\lib\x86 @@ -196,7 +196,7 @@ 0x0409 - winmm.lib;imm32.lib;version.lib;%(AdditionalDependencies) + winmm.lib;imm32.lib;oleaut32.lib;version.lib;%(AdditionalDependencies) true Windows $(DXSDK_DIR)\lib\x64 @@ -221,6 +221,7 @@ + @@ -399,6 +400,7 @@ + diff --git a/VisualC/tests/testgamecontroller/testgamecontroller_VS2010.vcxproj b/VisualC/tests/testgamecontroller/testgamecontroller_VS2010.vcxproj new file mode 100644 index 000000000..19049dc26 --- /dev/null +++ b/VisualC/tests/testgamecontroller/testgamecontroller_VS2010.vcxproj @@ -0,0 +1,227 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + testgamecontroller + testgamecontroller + {55812185-D13C-4022-9C81-32E0F4A08336} + + + + Application + false + + + Application + false + MultiByte + + + Application + false + + + Application + false + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + false + false + $(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + true + true + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + + + OnlyExplicitInline + ..\..\..\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreadedDLL + true + + + Level3 + true + Default + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + Windows + + + copy "$(SolutionDir)\SDL\$(Platform)\$(Configuration)\SDL.dll" "$(TargetDir)\SDL.dll" + + + Copy SDL + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + + + OnlyExplicitInline + ..\..\..\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreadedDLL + true + + + Level3 + true + Default + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + Windows + + + copy "$(SolutionDir)\SDL\$(Platform)\$(Configuration)\SDL.dll" "$(TargetDir)\SDL.dll" + + + Copy SDL + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + + + Disabled + ..\..\..\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + + + Level3 + true + EditAndContinue + Default + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + true + Windows + + + copy "$(SolutionDir)\SDL\$(Platform)\$(Configuration)\SDL.dll" "$(TargetDir)\SDL.dll" + + + Copy SDL + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + + + Disabled + ..\..\..\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + + + Level3 + true + ProgramDatabase + Default + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + true + Windows + + + copy "$(SolutionDir)\SDL\$(Platform)\$(Configuration)\SDL.dll" "$(TargetDir)\SDL.dll" + + + Copy SDL + + + + + + + + + + + + + \ No newline at end of file diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj index 604c77d48..e7aade86c 100755 --- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -546,6 +546,8 @@ AABCC38E164063D200AB8930 /* SDL_cocoamessagebox.h in Headers */ = {isa = PBXBuildFile; fileRef = AABCC38B164063D200AB8930 /* SDL_cocoamessagebox.h */; }; AABCC38F164063D200AB8930 /* SDL_cocoamessagebox.m in Sources */ = {isa = PBXBuildFile; fileRef = AABCC38C164063D200AB8930 /* SDL_cocoamessagebox.m */; }; AABCC390164063D200AB8930 /* SDL_cocoamessagebox.m in Sources */ = {isa = PBXBuildFile; fileRef = AABCC38C164063D200AB8930 /* SDL_cocoamessagebox.m */; }; + BBFC088B164C6514003E6A99 /* SDL_gamecontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = BBFC088A164C6514003E6A99 /* SDL_gamecontroller.c */; }; + BBFC088D164C6647003E6A99 /* SDL_gamecontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = BBFC088A164C6514003E6A99 /* SDL_gamecontroller.c */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -891,6 +893,7 @@ AA9FF9591637CBF9000DF050 /* SDL_messagebox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_messagebox.h; sourceTree = ""; }; AABCC38B164063D200AB8930 /* SDL_cocoamessagebox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_cocoamessagebox.h; sourceTree = ""; }; AABCC38C164063D200AB8930 /* SDL_cocoamessagebox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_cocoamessagebox.m; sourceTree = ""; }; + BBFC088A164C6514003E6A99 /* SDL_gamecontroller.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_gamecontroller.c; sourceTree = ""; }; BECDF66B0761BA81005FE872 /* Info-Framework.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-Framework.plist"; sourceTree = ""; }; BECDF66C0761BA81005FE872 /* SDL2.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDL2.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BECDF6B30761BA81005FE872 /* libSDL2.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSDL2.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1325,6 +1328,7 @@ 04BDFE0612E6671700899322 /* darwin */, 04BDFE1612E6671700899322 /* SDL_joystick.c */, 04BDFE1712E6671700899322 /* SDL_joystick_c.h */, + BBFC088A164C6514003E6A99 /* SDL_gamecontroller.c */, 04BDFE1812E6671700899322 /* SDL_sysjoystick.h */, ); name = joystick; @@ -2306,6 +2310,7 @@ 04BD027612E6671800899322 /* SDL_syshaptic.c in Sources */, 04BD027A12E6671800899322 /* SDL_haptic.c in Sources */, 04BD028112E6671800899322 /* SDL_sysjoystick.c in Sources */, + BBFC088D164C6647003E6A99 /* SDL_gamecontroller.c in Sources */, 04BD028B12E6671800899322 /* SDL_joystick.c in Sources */, 04BD02A312E6671800899322 /* SDL_sysloadso.c in Sources */, 04BD02AE12E6671800899322 /* SDL_syspower.c in Sources */, diff --git a/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj b/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj index 83ad6510e..cd37ca875 100755 --- a/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj +++ b/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj @@ -559,6 +559,18 @@ 00794F8709D2413B003FC8A1 /* sample.bmp in CopyFiles */ = {isa = PBXBuildFile; fileRef = 00794E6109D20839003FC8A1 /* sample.bmp */; }; 4537749F12091551002F0F45 /* libsdlcommon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BEC567FF0761D90600A33029 /* libsdlcommon.a */; }; 453774A5120915E3002F0F45 /* testshape.c in Sources */ = {isa = PBXBuildFile; fileRef = 453774A4120915E3002F0F45 /* testshape.c */; }; + BBFC08BF164C6862003E6A99 /* libsdlcommon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BEC567FF0761D90600A33029 /* libsdlcommon.a */; }; + BBFC08C0164C6862003E6A99 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 002F33A709CA188600EBEB88 /* Cocoa.framework */; }; + BBFC08C1164C6862003E6A99 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 002A863B10730545007319AE /* CoreAudio.framework */; }; + BBFC08C2164C6862003E6A99 /* ForceFeedback.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 002A863C10730545007319AE /* ForceFeedback.framework */; }; + BBFC08C3164C6862003E6A99 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 002A863D10730545007319AE /* IOKit.framework */; }; + BBFC08C4164C6862003E6A99 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 002A869F10730593007319AE /* AudioToolbox.framework */; }; + BBFC08C5164C6862003E6A99 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 002A86A010730593007319AE /* CoreFoundation.framework */; }; + BBFC08C6164C6862003E6A99 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 002A86F2107305CE007319AE /* OpenGL.framework */; }; + BBFC08C7164C6862003E6A99 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 002A871410730623007319AE /* AudioUnit.framework */; }; + BBFC08C8164C6862003E6A99 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 002A873910730675007319AE /* Carbon.framework */; }; + BBFC08C9164C6862003E6A99 /* libSDL2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 003FA645093FFD41000C53B3 /* libSDL2.a */; }; + BBFC08D0164C6876003E6A99 /* testgamecontroller.c in Sources */ = {isa = PBXBuildFile; fileRef = BBFC088E164C6820003E6A99 /* testgamecontroller.c */; }; BEC566B10761D90300A33029 /* checkkeys.c in Sources */ = {isa = PBXBuildFile; fileRef = 092D6D10FFB30A2C7F000001 /* checkkeys.c */; }; BEC566BE0761D90300A33029 /* graywin.c in Sources */ = {isa = PBXBuildFile; fileRef = 092D6D1BFFB30C237F000001 /* graywin.c */; }; BEC566CB0761D90300A33029 /* loopwave.c in Sources */ = {isa = PBXBuildFile; fileRef = 083E4872006D84C97F000001 /* loopwave.c */; }; @@ -1098,6 +1110,13 @@ remoteGlobalIDString = BEC567F70761D90600A33029; remoteInfo = sdlcommon; }; + BBFC08B9164C6862003E6A99 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BEC567F70761D90600A33029; + remoteInfo = "libsdlmain.a (Upgraded)"; + }; BEC568300761D90600A33029 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; @@ -1452,6 +1471,21 @@ 4537749212091504002F0F45 /* testshape.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testshape.app; sourceTree = BUILT_PRODUCTS_DIR; }; 453774A4120915E3002F0F45 /* testshape.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testshape.c; path = ../../test/testshape.c; sourceTree = SOURCE_ROOT; }; B207FF2404E1B19600A80002 /* sdlcommon_prefix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sdlcommon_prefix.h; sourceTree = ""; }; + BBFC088E164C6820003E6A99 /* testgamecontroller.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testgamecontroller.c; path = ../../test/testgamecontroller.c; sourceTree = ""; }; + BBFC089C164C684D003E6A99 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + BBFC089F164C684D003E6A99 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + BBFC08A0164C684D003E6A99 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + BBFC08A1164C684D003E6A99 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + BBFC08A4164C684D003E6A99 /* testgamecontroller-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "testgamecontroller-Info.plist"; sourceTree = ""; }; + BBFC08A6164C684D003E6A99 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + BBFC08A8164C684D003E6A99 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + BBFC08AA164C684D003E6A99 /* testgamecontroller-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "testgamecontroller-Prefix.pch"; sourceTree = ""; }; + BBFC08AC164C684D003E6A99 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; + BBFC08AE164C684D003E6A99 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + BBFC08AF164C684D003E6A99 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + BBFC08B2164C684D003E6A99 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; + BBFC08CD164C6862003E6A99 /* testgamecontroller.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testgamecontroller.app; sourceTree = BUILT_PRODUCTS_DIR; }; + BBFC08CF164C6863003E6A99 /* testjoystick copy-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "testjoystick copy-Info.plist"; path = "/Users/alfred/valve/steam3_rel_Client/src/external/SDL2/build/Xcode/SDLTest/testjoystick copy-Info.plist"; sourceTree = ""; }; BEC566B60761D90300A33029 /* checkkeys.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = checkkeys.app; sourceTree = BUILT_PRODUCTS_DIR; }; BEC566C30761D90300A33029 /* graywin.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = graywin.app; sourceTree = BUILT_PRODUCTS_DIR; }; BEC566D10761D90300A33029 /* loopwave.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = loopwave.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1848,6 +1882,24 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BBFC08BE164C6862003E6A99 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BBFC08BF164C6862003E6A99 /* libsdlcommon.a in Frameworks */, + BBFC08C0164C6862003E6A99 /* Cocoa.framework in Frameworks */, + BBFC08C1164C6862003E6A99 /* CoreAudio.framework in Frameworks */, + BBFC08C2164C6862003E6A99 /* ForceFeedback.framework in Frameworks */, + BBFC08C3164C6862003E6A99 /* IOKit.framework in Frameworks */, + BBFC08C4164C6862003E6A99 /* AudioToolbox.framework in Frameworks */, + BBFC08C5164C6862003E6A99 /* CoreFoundation.framework in Frameworks */, + BBFC08C6164C6862003E6A99 /* OpenGL.framework in Frameworks */, + BBFC08C7164C6862003E6A99 /* AudioUnit.framework in Frameworks */, + BBFC08C8164C6862003E6A99 /* Carbon.framework in Frameworks */, + BBFC08C9164C6862003E6A99 /* libSDL2.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; BEC566B20761D90300A33029 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -2330,7 +2382,10 @@ B207FF2404E1B19600A80002 /* sdlcommon_prefix.h */, 002F33A209CA183B00EBEB88 /* Linked Frameworks */, 00794E4609D207B4003FC8A1 /* Resources */, + BBFC08A2164C684D003E6A99 /* testgamecontroller */, + BBFC089B164C684D003E6A99 /* Frameworks */, 1AB674ADFE9D54B511CA2CBB /* Products */, + BBFC08CF164C6863003E6A99 /* testjoystick copy-Info.plist */, ); comments = "I made these tests link against our \"default\" framework which includes X11 stuff. If you didn't install the X11 headers with Xcode, you might have problems building the SDL.framework (which is a dependency). You can swap the dependencies around to get around this, or you can modify the default SDL.framework target to not include X11 stuff. (Go into its target build options and remove all the Preprocessor macros.)\n\n\n\nWe are sort of in a half-way state at the moment. Going \"all-the-way\" means we copy the SDL.framework inside the app bundle so we can run the test without the step of the user \"installing\" the framework. But there is an oversight/bug in Xcode that doesn't correctly find the location of the framework when in an embedded/nested Xcode project. We could probably try to hack this with a shell script that checks multiple directories for existence, but this is messier and more work than I prefer, so I rather just wait for Apple to fix this. In the meantime...\n\nThe \"All\" target will build the SDL framework from the Xcode project. The other targets do not have this dependency set (for flexibility reasons in case we make changes). If you have not built the framework, you will probably be unable to link. You will either need to build the framework, or you need to add \"-framework SDL\" to the link options and make sure you have the SDL.framework installed somewhere where it can be seen (like /Library/Frameworks...I think we already set this one up.) \n\nTo run though, you should have a copy of the SDL.framework in /Library/Frameworks or ~/Library/Frameworks.\n\n\n\n\ntestgl and testdyngl need -DHAVE_OPENGL\ntestgl needs to link against OpenGL.framework\n\n"; name = SDLTest; @@ -2363,6 +2418,7 @@ 00179791107432FA00F5D044 /* testime.c */, 001797B31074339C00F5D044 /* testintersections.c */, 092D6D62FFB312AA7F000001 /* testjoystick.c */, + BBFC088E164C6820003E6A99 /* testgamecontroller.c */, 092D6D6CFFB313437F000001 /* testkeys.c */, 001797D31074343E00F5D044 /* testloadso.c */, 092D6D75FFB313BB7F000001 /* testlock.c */, @@ -2440,10 +2496,53 @@ 0017991610743F1000F5D044 /* testsprite2.app */, 0017993810743FB700F5D044 /* testwm2.app */, 4537749212091504002F0F45 /* testshape.app */, + BBFC08CD164C6862003E6A99 /* testgamecontroller.app */, ); name = Products; sourceTree = ""; }; + BBFC089B164C684D003E6A99 /* Frameworks */ = { + isa = PBXGroup; + children = ( + BBFC089C164C684D003E6A99 /* Cocoa.framework */, + BBFC089E164C684D003E6A99 /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; + BBFC089E164C684D003E6A99 /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + BBFC089F164C684D003E6A99 /* AppKit.framework */, + BBFC08A0164C684D003E6A99 /* CoreData.framework */, + BBFC08A1164C684D003E6A99 /* Foundation.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + BBFC08A2164C684D003E6A99 /* testgamecontroller */ = { + isa = PBXGroup; + children = ( + BBFC08AE164C684D003E6A99 /* AppDelegate.h */, + BBFC08AF164C684D003E6A99 /* AppDelegate.m */, + BBFC08B1164C684D003E6A99 /* MainMenu.xib */, + BBFC08A3164C684D003E6A99 /* Supporting Files */, + ); + path = testgamecontroller; + sourceTree = ""; + }; + BBFC08A3164C684D003E6A99 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + BBFC08A4164C684D003E6A99 /* testgamecontroller-Info.plist */, + BBFC08A5164C684D003E6A99 /* InfoPlist.strings */, + BBFC08A8164C684D003E6A99 /* main.m */, + BBFC08AA164C684D003E6A99 /* testgamecontroller-Prefix.pch */, + BBFC08AB164C684D003E6A99 /* Credits.rtf */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -2588,6 +2687,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BBFC08BA164C6862003E6A99 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; BEC566AD0761D90300A33029 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -3182,6 +3288,26 @@ productReference = 4537749212091504002F0F45 /* testshape.app */; productType = "com.apple.product-type.application"; }; + BBFC08B7164C6862003E6A99 /* testgamecontroller */ = { + isa = PBXNativeTarget; + buildConfigurationList = BBFC08CA164C6862003E6A99 /* Build configuration list for PBXNativeTarget "testgamecontroller" */; + buildPhases = ( + BBFC08BA164C6862003E6A99 /* Headers */, + BBFC08BB164C6862003E6A99 /* Resources */, + BBFC08BC164C6862003E6A99 /* Sources */, + BBFC08BE164C6862003E6A99 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + BBFC08B8164C6862003E6A99 /* PBXTargetDependency */, + ); + name = testgamecontroller; + productInstallPath = "$(USER_APPS_DIR)"; + productName = testjoystick; + productReference = BBFC08CD164C6862003E6A99 /* testgamecontroller.app */; + productType = "com.apple.product-type.application"; + }; BEC566AB0761D90300A33029 /* checkkeys */ = { isa = PBXNativeTarget; buildConfigurationList = 001B593808BDB826006539E9 /* Build configuration list for PBXNativeTarget "checkkeys" */; @@ -3686,6 +3812,7 @@ Japanese, French, German, + en, ); mainGroup = 08FB7794FE84155DC02AAC07 /* SDLTest */; projectDirPath = ""; @@ -3743,6 +3870,7 @@ BEC567EA0761D90600A33029 /* torturethread */, BEC567F70761D90600A33029 /* sdlcommon */, 4537749112091504002F0F45 /* testshape */, + BBFC08B7164C6862003E6A99 /* testgamecontroller */, ); }; /* End PBXProject section */ @@ -3933,6 +4061,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BBFC08BB164C6862003E6A99 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; BEC566AE0761D90300A33029 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -4277,6 +4412,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BBFC08BC164C6862003E6A99 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BBFC08D0164C6876003E6A99 /* testgamecontroller.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; BEC566B00761D90300A33029 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -4797,6 +4940,11 @@ target = BEC567F70761D90600A33029 /* sdlcommon */; targetProxy = 4537749D1209152D002F0F45 /* PBXContainerItemProxy */; }; + BBFC08B8164C6862003E6A99 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BEC567F70761D90600A33029 /* sdlcommon */; + targetProxy = BBFC08B9164C6862003E6A99 /* PBXContainerItemProxy */; + }; BEC568310761D90600A33029 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = BEC567F70761D90600A33029 /* sdlcommon */; @@ -4914,6 +5062,33 @@ }; /* End PBXTargetDependency section */ +/* Begin PBXVariantGroup section */ + BBFC08A5164C684D003E6A99 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + BBFC08A6164C684D003E6A99 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + BBFC08AB164C684D003E6A99 /* Credits.rtf */ = { + isa = PBXVariantGroup; + children = ( + BBFC08AC164C684D003E6A99 /* en */, + ); + name = Credits.rtf; + sourceTree = ""; + }; + BBFC08B1164C684D003E6A99 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + BBFC08B2164C684D003E6A99 /* en */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin XCBuildConfiguration section */ 0017958910741F7900F5D044 /* Debug */ = { isa = XCBuildConfiguration; @@ -5830,6 +6005,24 @@ }; name = Release; }; + BBFC08CB164C6862003E6A99 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = "testgamecontroller-Info.plist"; + PRODUCT_NAME = testgamecontroller; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + BBFC08CC164C6862003E6A99 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = "testgamecontroller-Info.plist"; + PRODUCT_NAME = testgamecontroller; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -6256,6 +6449,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Debug; }; + BBFC08CA164C6862003E6A99 /* Build configuration list for PBXNativeTarget "testgamecontroller" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BBFC08CB164C6862003E6A99 /* Debug */, + BBFC08CC164C6862003E6A99 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; /* End XCConfigurationList section */ }; rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; diff --git a/Xcode/SDLTest/testgamecontroller-Info.plist b/Xcode/SDLTest/testgamecontroller-Info.plist new file mode 100644 index 000000000..7886bee1f --- /dev/null +++ b/Xcode/SDLTest/testgamecontroller-Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + testgamecontroller + CFBundleGetInfoString + + CFBundleIconFile + + CFBundleIdentifier + + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + + CFBundlePackageType + APPL + CFBundleShortVersionString + + CFBundleSignature + ???? + CFBundleVersion + 0.0.1d1 + NSMainNibFile + SDLMain.nib + NSPrincipalClass + NSApplication + + diff --git a/include/SDL.h b/include/SDL.h index 186ab8a18..e5d3ecd33 100644 --- a/include/SDL.h +++ b/include/SDL.h @@ -79,6 +79,7 @@ #include "SDL_endian.h" #include "SDL_error.h" #include "SDL_events.h" +#include "SDL_gamecontroller.h" #include "SDL_hints.h" #include "SDL_loadso.h" #include "SDL_log.h" @@ -115,6 +116,7 @@ extern "C" { #define SDL_INIT_VIDEO 0x00000020 #define SDL_INIT_JOYSTICK 0x00000200 #define SDL_INIT_HAPTIC 0x00001000 +#define SDL_INIT_GAMECONTROLLER 0x00002000 /**< turn on game controller also implicitly does JOYSTICK */ #define SDL_INIT_NOPARACHUTE 0x00100000 /**< Don't catch fatal signals */ #define SDL_INIT_EVERYTHING 0x0000FFFF /*@}*/ diff --git a/include/SDL_events.h b/include/SDL_events.h index 4eaf09563..a47c4aec4 100644 --- a/include/SDL_events.h +++ b/include/SDL_events.h @@ -34,6 +34,7 @@ #include "SDL_keyboard.h" #include "SDL_mouse.h" #include "SDL_joystick.h" +#include "SDL_gamecontroller.h" #include "SDL_quit.h" #include "SDL_gesture.h" #include "SDL_touch.h" @@ -90,6 +91,15 @@ typedef enum SDL_JOYHATMOTION, /**< Joystick hat position change */ SDL_JOYBUTTONDOWN, /**< Joystick button pressed */ SDL_JOYBUTTONUP, /**< Joystick button released */ + SDL_JOYDEVICEADDED, /**< A new joystick has been inserted into the system */ + SDL_JOYDEVICEREMOVED, /**< An opened joystick has been removed */ + + /* Game controller events */ + SDL_CONTROLLERAXISMOTION = 0x650, /**< Game controller axis motion */ + SDL_CONTROLLERBUTTONDOWN, /**< Game controller button pressed */ + SDL_CONTROLLERBUTTONUP, /**< Game controller button released */ + SDL_CONTROLLERDEVICEADDED, /**< A new Game controller has been inserted into the system */ + SDL_CONTROLLERDEVICEREMOVED, /**< An opened Game controller has been removed */ /* Touch events */ SDL_FINGERDOWN = 0x700, @@ -231,7 +241,7 @@ typedef struct SDL_JoyAxisEvent { Uint32 type; /**< ::SDL_JOYAXISMOTION */ Uint32 timestamp; - Uint8 which; /**< The joystick device index */ + Uint8 which; /**< The joystick instance id */ Uint8 axis; /**< The joystick axis index */ Uint8 padding1; Uint8 padding2; @@ -245,7 +255,7 @@ typedef struct SDL_JoyBallEvent { Uint32 type; /**< ::SDL_JOYBALLMOTION */ Uint32 timestamp; - Uint8 which; /**< The joystick device index */ + Uint8 which; /**< The joystick instance id */ Uint8 ball; /**< The joystick trackball index */ Uint8 padding1; Uint8 padding2; @@ -260,7 +270,7 @@ typedef struct SDL_JoyHatEvent { Uint32 type; /**< ::SDL_JOYHATMOTION */ Uint32 timestamp; - Uint8 which; /**< The joystick device index */ + Uint8 which; /**< The joystick instance id */ Uint8 hat; /**< The joystick hat index */ Uint8 value; /**< The hat position value. * \sa ::SDL_HAT_LEFTUP ::SDL_HAT_UP ::SDL_HAT_RIGHTUP @@ -279,12 +289,59 @@ typedef struct SDL_JoyButtonEvent { Uint32 type; /**< ::SDL_JOYBUTTONDOWN or ::SDL_JOYBUTTONUP */ Uint32 timestamp; - Uint8 which; /**< The joystick device index */ + Uint8 which; /**< The joystick instance id */ Uint8 button; /**< The joystick button index */ Uint8 state; /**< ::SDL_PRESSED or ::SDL_RELEASED */ Uint8 padding1; } SDL_JoyButtonEvent; +/** + * \brief Joystick device event structure (event.jdevice.*) + */ +typedef struct SDL_JoyDeviceEvent +{ + Uint32 type; /**< ::SDL_JOYDEVICEADDED or ::SDL_JOYDEVICEREMOVED */ + Uint32 timestamp; + Uint32 which; /**< The joystick device index for ADD, instance_id for REMOVE*/ +} SDL_JoyDeviceEvent; + + +/** + * \brief Game controller axis motion event structure (event.caxis.*) + */ +typedef struct SDL_ControllerAxisEvent +{ + Uint32 type; /**< ::SDL_CONTROLLERAXISMOTION */ + Uint32 timestamp; + Uint8 which; /**< The joystick instance id */ + SDL_CONTROLLER_AXIS axis; /**< The joystick axis index */ + int value; /**< The axis value (range: -32768 to 32767) */ +} SDL_ControllerAxisEvent; + + +/** + * \brief Game controller button event structure (event.cbutton.*) + */ +typedef struct SDL_ControllerButtonEvent +{ + Uint32 type; /**< ::SDL_CONTROLLERBUTTONDOWN or ::SDL_CONTROLLERBUTTONUP */ + Uint32 timestamp; + Uint8 which; /**< The joystick instance id */ + SDL_CONTROLLER_BUTTON button; /**< The joystick button index */ + Uint8 state; /**< ::SDL_PRESSED or ::SDL_RELEASED */ +} SDL_ControllerButtonEvent; + + +/** + * \brief Controller device event structure (event.cdevice.*) + */ +typedef struct SDL_ControllerDeviceEvent +{ + Uint32 type; /**< ::SDL_CONTROLLERDEVICEADDED or ::SDL_CONTROLLERDEVICEREMOVED */ + Uint32 timestamp; + Uint32 which; /**< The joystick device index for ADD, instance_id for REMOVE*/ +} SDL_ControllerDeviceEvent; + /** * \brief Touch finger motion/finger event structure (event.tfinger.*) @@ -430,6 +487,10 @@ typedef union SDL_Event SDL_JoyBallEvent jball; /**< Joystick ball event data */ SDL_JoyHatEvent jhat; /**< Joystick hat event data */ SDL_JoyButtonEvent jbutton; /**< Joystick button event data */ + SDL_JoyDeviceEvent jdevice; /**< Joystick device change event data */ + SDL_ControllerAxisEvent caxis; /**< Game Controller button event data */ + SDL_ControllerButtonEvent cbutton; /**< Game Controller button event data */ + SDL_ControllerDeviceEvent cdevice; /**< Game Controller device event data */ SDL_QuitEvent quit; /**< Quit request event data */ SDL_UserEvent user; /**< Custom event data */ SDL_SysWMEvent syswm; /**< System dependent window event data */ diff --git a/include/SDL_gamecontroller.h b/include/SDL_gamecontroller.h new file mode 100644 index 000000000..0fde9d6f9 --- /dev/null +++ b/include/SDL_gamecontroller.h @@ -0,0 +1,257 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 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. +*/ + +/** + * \file SDL_gamecontroller.h + * + * Include file for SDL game controller event handling + */ + +#ifndef _SDL_gamecontroller_h +#define _SDL_gamecontroller_h + +#include "SDL_stdinc.h" +#include "SDL_error.h" +#include "SDL_joystick.h" + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \file SDL_gamecontroller.h + * + * In order to use these functions, SDL_Init() must have been called + * with the ::SDL_INIT_JOYSTICK flag. This causes SDL to scan the system + * for game controllers, and load appropriate drivers. + */ + +/* The gamecontroller structure used to identify an SDL game controller */ +struct _SDL_GameController; +typedef struct _SDL_GameController SDL_GameController; + + +typedef enum +{ + SDL_CONTROLLER_BINDTYPE_NONE = 0, + SDL_CONTROLLER_BINDTYPE_BUTTON, + SDL_CONTROLLER_BINDTYPE_AXIS, + SDL_CONTROLLER_BINDTYPE_HAT, +} SDL_CONTROLLER_BINDTYPE; +/** + * get the sdl joystick layer binding for this controller button/axis mapping + */ +struct _SDL_GameControllerHatBind +{ + int hat; + int hat_mask; +}; + +typedef struct _SDL_GameControllerButtonBind +{ + SDL_CONTROLLER_BINDTYPE m_eBindType; + union + { + int button; + int axis; + struct _SDL_GameControllerHatBind hat; + }; + +} SDL_GameControllerButtonBind; + + +/** + * To count the number of game controllers in the system for the following: + * int nJoysticks = SDL_NumJoysticks(); + * int nGameControllers = 0; + * for ( int i = 0; i < nJoysticks; i++ ) { + * if ( SDL_IsGameController(i) ) { + * nGameControllers++; + * } + * } + * + * Using the SDL_HINT_GAMECONTROLLERCONFIG hint you can add support for controllers SDL is unaware of or cause an existing controller to have a different binding. The format is: + * guid,name,mappings + * + * Where GUID is the string value from SDL_JoystickGetGUIDString(), name is the human readable string for the device and mappings are controller mappings to joystick ones. + * Under Windows there is a reserved GUID of "xinput" that covers any XInput devices. + * The mapping format for joystick is: + * bX - a joystick button, index X + * hX.Y - hat X with value Y + * aX - axis X of the joystick + * Buttons can be used as a controller axis and vice versa. + * + * This string shows an example of a valid mapping for a controller + * "341a3608000000000000504944564944,Aferglow PS3 Controller,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7", + * + */ + + +/** + * Is the joystick on this index supported by the game controller interface? + * returns 1 if supported, 0 otherwise. + */ +extern DECLSPEC int SDLCALL SDL_IsGameController(int joystick_index); + + +/** + * Get the implementation dependent name of a game controller. + * This can be called before any controllers are opened. + * If no name can be found, this function returns NULL. + */ +extern DECLSPEC const char *SDLCALL SDL_GameControllerNameForIndex(int joystick_index); + +/** + * Open a game controller for use. + * The index passed as an argument refers to the N'th game controller on the system. + * This index is the value which will identify this controller in future controller + * events. + * + * \return A controller identifier, or NULL if an error occurred. + */ +extern DECLSPEC SDL_GameController *SDLCALL SDL_GameControllerOpen(int joystick_index); + +/** + * Return the name for this currently opened controller + */ +extern DECLSPEC const char *SDLCALL SDL_GameControllerName(SDL_GameController * gamecontroller); + +/** + * Returns 1 if the controller has been opened and currently connected, or 0 if it has not. + */ +extern DECLSPEC int SDLCALL SDL_GameControllerGetAttached(SDL_GameController * gamecontroller); + +/** + * Get the underlying joystick object used by a controller + */ +extern DECLSPEC SDL_Joystick *SDLCALL SDL_GameControllerGetJoystick(SDL_GameController * gamecontroller); + +/** + * Enable/disable controller event polling. + * + * If controller events are disabled, you must call SDL_GameControllerUpdate() + * yourself and check the state of the controller when you want controller + * information. + * + * The state can be one of ::SDL_QUERY, ::SDL_ENABLE or ::SDL_IGNORE. + */ +extern DECLSPEC int SDLCALL SDL_GameControllerEventState(int state); + +/** + * The list of axii available from a controller + */ +typedef enum +{ + SDL_CONTROLLER_AXIS_INVALID = -1, + SDL_CONTROLLER_AXIS_LEFTX, + SDL_CONTROLLER_AXIS_LEFTY, + SDL_CONTROLLER_AXIS_RIGHTX, + SDL_CONTROLLER_AXIS_RIGHTY, + SDL_CONTROLLER_AXIS_TRIGGERLEFT, + SDL_CONTROLLER_AXIS_TRIGGERRIGHT, + SDL_CONTROLLER_AXIS_MAX +} SDL_CONTROLLER_AXIS; + +/** + * turn this string into a axis mapping + */ +extern DECLSPEC SDL_CONTROLLER_AXIS SDLCALL SDL_GameControllerGetAxisFromString(const char *pchString); + +/** + * get the sdl joystick layer binding for this controller button mapping + */ +extern DECLSPEC SDL_GameControllerButtonBind SDLCALL SDL_GameControllerGetBindForAxis(SDL_GameController * gamecontroller, SDL_CONTROLLER_AXIS button); + +/** + * Get the current state of an axis control on a game controller. + * + * The state is a value ranging from -32768 to 32767. + * + * The axis indices start at index 0. + */ +extern DECLSPEC Sint16 SDLCALL SDL_GameControllerGetAxis(SDL_GameController * gamecontroller, + SDL_CONTROLLER_AXIS axis); + +/** + * The list of buttons available from a controller + */ +typedef enum +{ + SDL_CONTROLLER_BUTTON_INVALID = -1, + SDL_CONTROLLER_BUTTON_A, + SDL_CONTROLLER_BUTTON_B, + SDL_CONTROLLER_BUTTON_X, + SDL_CONTROLLER_BUTTON_Y, + SDL_CONTROLLER_BUTTON_BACK, + SDL_CONTROLLER_BUTTON_GUIDE, + SDL_CONTROLLER_BUTTON_START, + SDL_CONTROLLER_BUTTON_LEFTSTICK, + SDL_CONTROLLER_BUTTON_RIGHTSTICK, + SDL_CONTROLLER_BUTTON_LEFTSHOULDER, + SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, + SDL_CONTROLLER_BUTTON_DPAD_UP, + SDL_CONTROLLER_BUTTON_DPAD_DOWN, + SDL_CONTROLLER_BUTTON_DPAD_LEFT, + SDL_CONTROLLER_BUTTON_DPAD_RIGHT, + SDL_CONTROLLER_BUTTON_MAX +} SDL_CONTROLLER_BUTTON; + +/** + * turn this string into a button mapping + */ +extern DECLSPEC SDL_CONTROLLER_BUTTON SDLCALL SDL_GameControllerGetButtonFromString(const char *pchString); + + +/** + * get the sdl joystick layer binding for this controller button mapping + */ +extern DECLSPEC SDL_GameControllerButtonBind SDLCALL SDL_GameControllerGetBindForButton(SDL_GameController * gamecontroller, SDL_CONTROLLER_BUTTON button); + + +/** + * Get the current state of a button on a game controller. + * + * The button indices start at index 0. + */ +extern DECLSPEC Uint8 SDLCALL SDL_GameControllerGetButton(SDL_GameController * gamecontroller, + SDL_CONTROLLER_BUTTON button); + +/** + * Close a controller previously opened with SDL_GameControllerOpen(). + */ +extern DECLSPEC void SDLCALL SDL_GameControllerClose(SDL_GameController * gamecontrollerk); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_gamecontroller_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/include/SDL_hints.h b/include/SDL_hints.h index c3a366181..8b2fcb91c 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -189,6 +189,14 @@ extern "C" { #define SDL_HINT_ORIENTATIONS "SDL_IOS_ORIENTATIONS" +/** + * \brief A variable that lets you manually hint extra gamecontroller db entries + * + * The variable expected newline delimited rows of gamecontroller config data, see SDL_gamecontroller.h + */ +#define SDL_HINT_GAMECONTROLLERCONFIG "SDL_GAMECONTROLLERCONFIG" + + /** * \brief An enumeration of hint priorities */ diff --git a/include/SDL_joystick.h b/include/SDL_joystick.h index 602206527..8fd75a03e 100644 --- a/include/SDL_joystick.h +++ b/include/SDL_joystick.h @@ -23,6 +23,17 @@ * \file SDL_joystick.h * * Include file for SDL joystick event handling + * + * The term "device_index" identifies currently plugged in joystick devices between 0 and SDL_NumJoysticks, with the exact joystick + * behind a device_index changing as joysticks are plugged and unplugged. + * + * The term "instance_id" is the current instantiation of a joystick device in the system, if the joystick is removed and then re-inserted + * then it will get a new instance_id, instance_id's are monotonically increasing identifiers of a joystick plugged in. + * + * The term JoystickGUID is a stable 128-bit identifier for a joystick device that does not change over time, it identifies class of + * the device (a X360 wired controller for example). This identifier is platform dependent. + * + * */ #ifndef _SDL_joystick_h @@ -51,10 +62,11 @@ extern "C" { struct _SDL_Joystick; typedef struct _SDL_Joystick SDL_Joystick; - +typedef int SDL_JoystickID; + /* Function prototypes */ /** - * Count the number of joysticks attached to the system + * Count the number of joysticks attached to the system right now */ extern DECLSPEC int SDLCALL SDL_NumJoysticks(void); @@ -63,7 +75,7 @@ extern DECLSPEC int SDLCALL SDL_NumJoysticks(void); * This can be called before any joysticks are opened. * If no name can be found, this function returns NULL. */ -extern DECLSPEC const char *SDLCALL SDL_JoystickName(int device_index); +extern DECLSPEC const char *SDLCALL SDL_JoystickNameForIndex(int device_index); /** * Open a joystick for use. @@ -76,14 +88,47 @@ extern DECLSPEC const char *SDLCALL SDL_JoystickName(int device_index); extern DECLSPEC SDL_Joystick *SDLCALL SDL_JoystickOpen(int device_index); /** - * Returns 1 if the joystick has been opened, or 0 if it has not. + * Return the name for this currently opened joystick. + * If no name can be found, this function returns NULL. + */ +extern DECLSPEC const char *SDLCALL SDL_JoystickName(SDL_Joystick * joystick); + +/* A structure that encodes the stable unique id for a joystick device */ +typedef struct +{ + Uint8 data[16]; +} JoystickGUID; + +/** + * Return the GUID for the joystick at this index + */ +extern DECLSPEC JoystickGUID SDLCALL SDL_JoystickGetDeviceGUID(int device_index); + +/** + * Return the GUID for this opened joystick + */ +extern DECLSPEC JoystickGUID SDLCALL SDL_JoystickGetGUID(SDL_Joystick * joystick); + +/** + * Return a string representation for this guid. You are responsible for freeing memory from this call + */ +extern DECLSPEC char *SDLCALL SDL_JoystickGetGUIDString(JoystickGUID guid); + +/** + * convert a string into a joystick formatted guid + */ +extern DECLSPEC JoystickGUID SDLCALL SDL_JoystickGetGUIDFromString(const char *pchGUID); + + +/** + * Returns 1 if the joystick has been opened and currently connected, or 0 if it has not. */ -extern DECLSPEC int SDLCALL SDL_JoystickOpened(int device_index); +extern DECLSPEC int SDLCALL SDL_JoystickGetAttached(SDL_Joystick * joystick); /** * Get the device index of an opened joystick. */ -extern DECLSPEC int SDLCALL SDL_JoystickIndex(SDL_Joystick * joystick); +extern DECLSPEC SDL_JoystickID SDLCALL SDL_JoystickInstanceID(SDL_Joystick * joystick); /** * Get the number of general axis controls on a joystick. diff --git a/src/SDL.c b/src/SDL.c index 1a88f83d2..0cf39ecc1 100644 --- a/src/SDL.c +++ b/src/SDL.c @@ -44,7 +44,20 @@ extern int SDL_HelperWindowDestroy(void); /* The initialized subsystems */ static Uint32 SDL_initialized = 0; static Uint32 ticks_started = 0; +static SDL_bool SDL_bInMainQuit = SDL_FALSE; +static Uint8 SDL_SubsystemRefCount[ 32 ]; // keep a per subsystem init +/* helper func to return the index of the MSB in an int */ +int msb32_idx( Uint32 n) +{ + int b = 0; + if (!n) return -1; + +#define step(x) if (n >= ((Uint32)1) << x) b += x, n >>= x + step(16); step(8); step(4); step(2); step(1); +#undef step + return b; +} int SDL_InitSubSystem(Uint32 flags) @@ -55,11 +68,16 @@ SDL_InitSubSystem(Uint32 flags) SDL_StartTicks(); ticks_started = 1; } - if ((flags & SDL_INIT_TIMER) && !(SDL_initialized & SDL_INIT_TIMER)) { - if (SDL_TimerInit() < 0) { - return (-1); - } - SDL_initialized |= SDL_INIT_TIMER; + + if ((flags & SDL_INIT_TIMER) ){ + SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_TIMER) ]++; + SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_TIMER) ] < 254 ); + if ( !(SDL_initialized & SDL_INIT_TIMER)) { + if (SDL_TimerInit() < 0) { + return (-1); + } + SDL_initialized |= SDL_INIT_TIMER; + } } #else if (flags & SDL_INIT_TIMER) { @@ -70,11 +88,15 @@ SDL_InitSubSystem(Uint32 flags) #if !SDL_VIDEO_DISABLED /* Initialize the video/event subsystem */ - if ((flags & SDL_INIT_VIDEO) && !(SDL_initialized & SDL_INIT_VIDEO)) { - if (SDL_VideoInit(NULL) < 0) { - return (-1); - } - SDL_initialized |= SDL_INIT_VIDEO; + if ((flags & SDL_INIT_VIDEO) ) { + SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_VIDEO) ]++; + SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_VIDEO) ] < 254 ); + if ( !(SDL_initialized & SDL_INIT_VIDEO)) { + if (SDL_VideoInit(NULL) < 0) { + return (-1); + } + SDL_initialized |= SDL_INIT_VIDEO; + } } #else if (flags & SDL_INIT_VIDEO) { @@ -85,11 +107,15 @@ SDL_InitSubSystem(Uint32 flags) #if !SDL_AUDIO_DISABLED /* Initialize the audio subsystem */ - if ((flags & SDL_INIT_AUDIO) && !(SDL_initialized & SDL_INIT_AUDIO)) { - if (SDL_AudioInit(NULL) < 0) { - return (-1); - } - SDL_initialized |= SDL_INIT_AUDIO; + if ((flags & SDL_INIT_AUDIO) ) { + SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_AUDIO) ]++; + SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_AUDIO) ] < 254 ); + if ( !(SDL_initialized & SDL_INIT_AUDIO)) { + if (SDL_AudioInit(NULL) < 0) { + return (-1); + } + SDL_initialized |= SDL_INIT_AUDIO; + } } #else if (flags & SDL_INIT_AUDIO) { @@ -100,10 +126,23 @@ SDL_InitSubSystem(Uint32 flags) #if !SDL_JOYSTICK_DISABLED /* Initialize the joystick subsystem */ - if ((flags & SDL_INIT_JOYSTICK) && !(SDL_initialized & SDL_INIT_JOYSTICK)) { - if (SDL_JoystickInit() < 0) { + if ( ( (flags & SDL_INIT_JOYSTICK) ) || ((flags & SDL_INIT_GAMECONTROLLER) ) ) { // game controller implies joystick + SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_JOYSTICK) ]++; + SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_JOYSTICK) ] < 254 ); + if ( !(SDL_initialized & SDL_INIT_JOYSTICK) && SDL_JoystickInit() < 0) { return (-1); } + + if ((flags & SDL_INIT_GAMECONTROLLER) ) { + SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_GAMECONTROLLER) ]++; + SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_GAMECONTROLLER) ] < 254 ); + if ( !(SDL_initialized & SDL_INIT_GAMECONTROLLER)) { + if (SDL_GameControllerInit() < 0) { + return (-1); + } + SDL_initialized |= SDL_INIT_GAMECONTROLLER; + } + } SDL_initialized |= SDL_INIT_JOYSTICK; } #else @@ -115,11 +154,15 @@ SDL_InitSubSystem(Uint32 flags) #if !SDL_HAPTIC_DISABLED /* Initialize the haptic subsystem */ - if ((flags & SDL_INIT_HAPTIC) && !(SDL_initialized & SDL_INIT_HAPTIC)) { - if (SDL_HapticInit() < 0) { - return (-1); - } - SDL_initialized |= SDL_INIT_HAPTIC; + if ((flags & SDL_INIT_HAPTIC) ) { + SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_HAPTIC) ]++; + SDL_assert( SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_HAPTIC) ] < 254 ); + if ( !(SDL_initialized & SDL_INIT_HAPTIC)) { + if (SDL_HapticInit() < 0) { + return (-1); + } + SDL_initialized |= SDL_INIT_HAPTIC; + } } #else if (flags & SDL_INIT_HAPTIC) { @@ -156,6 +199,7 @@ SDL_Init(Uint32 flags) SDL_InstallParachute(); } + SDL_memset( SDL_SubsystemRefCount, 0x0, sizeof(SDL_SubsystemRefCount) ); return (0); } @@ -164,33 +208,62 @@ SDL_QuitSubSystem(Uint32 flags) { /* Shut down requested initialized subsystems */ #if !SDL_JOYSTICK_DISABLED - if ((flags & SDL_initialized & SDL_INIT_JOYSTICK)) { - SDL_JoystickQuit(); - SDL_initialized &= ~SDL_INIT_JOYSTICK; + if ((flags & SDL_initialized & SDL_INIT_JOYSTICK) || (flags & SDL_initialized & SDL_INIT_GAMECONTROLLER)) { + if ( (flags & SDL_initialized & SDL_INIT_GAMECONTROLLER) ) { + SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_GAMECONTROLLER) ]--; + if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_GAMECONTROLLER) ] == 0 ) { + SDL_GameControllerQuit(); + SDL_initialized &= ~SDL_INIT_GAMECONTROLLER; + } + } + + SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_JOYSTICK) ]--; + if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_JOYSTICK) ] == 0 ) + { + SDL_JoystickQuit(); + SDL_initialized &= ~SDL_INIT_JOYSTICK; + } + } #endif #if !SDL_HAPTIC_DISABLED if ((flags & SDL_initialized & SDL_INIT_HAPTIC)) { - SDL_HapticQuit(); - SDL_initialized &= ~SDL_INIT_HAPTIC; + SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_HAPTIC) ]--; + if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_HAPTIC) ] == 0 ) + { + SDL_HapticQuit(); + SDL_initialized &= ~SDL_INIT_HAPTIC; + } } #endif #if !SDL_AUDIO_DISABLED if ((flags & SDL_initialized & SDL_INIT_AUDIO)) { - SDL_AudioQuit(); - SDL_initialized &= ~SDL_INIT_AUDIO; + SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_AUDIO) ]--; + if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_AUDIO) ] == 0 ) + { + SDL_AudioQuit(); + SDL_initialized &= ~SDL_INIT_AUDIO; + } } #endif #if !SDL_VIDEO_DISABLED if ((flags & SDL_initialized & SDL_INIT_VIDEO)) { - SDL_VideoQuit(); - SDL_initialized &= ~SDL_INIT_VIDEO; + SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_VIDEO) ]--; + if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_VIDEO) ] == 0 ) + { + SDL_VideoQuit(); + SDL_initialized &= ~SDL_INIT_VIDEO; + } } #endif #if !SDL_TIMERS_DISABLED if ((flags & SDL_initialized & SDL_INIT_TIMER)) { - SDL_TimerQuit(); - SDL_initialized &= ~SDL_INIT_TIMER; + SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_TIMER) ]--; + if ( SDL_bInMainQuit || SDL_SubsystemRefCount[ msb32_idx(SDL_INIT_TIMER) ] == 0 ) + { + SDL_TimerQuit(); + SDL_initialized &= ~SDL_INIT_TIMER; + } } #endif } @@ -207,6 +280,7 @@ SDL_WasInit(Uint32 flags) void SDL_Quit(void) { + SDL_bInMainQuit = SDL_TRUE; /* Quit all subsystems */ #if defined(__WIN32__) SDL_HelperWindowDestroy(); @@ -219,6 +293,9 @@ SDL_Quit(void) SDL_ClearHints(); SDL_AssertionsQuit(); SDL_LogResetPriorities(); + + SDL_memset( SDL_SubsystemRefCount, 0x0, sizeof(SDL_SubsystemRefCount) ); + SDL_bInMainQuit = SDL_FALSE; } /* Get the library version number */ diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index 4083f17b4..c6aa97415 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -70,7 +70,7 @@ static __inline__ SDL_bool SDL_ShouldPollJoystick() { #if !SDL_JOYSTICK_DISABLED - if (SDL_numjoysticks && + if (SDL_PrivateJoystickNeedsPolling() && (!SDL_disabled_events[SDL_JOYAXISMOTION >> 8] || SDL_JoystickEventState(SDL_QUERY))) { return SDL_TRUE; diff --git a/src/haptic/SDL_haptic.c b/src/haptic/SDL_haptic.c index ae2ccebfa..eca9c9632 100644 --- a/src/haptic/SDL_haptic.c +++ b/src/haptic/SDL_haptic.c @@ -238,7 +238,7 @@ SDL_JoystickIsHaptic(SDL_Joystick * joystick) int ret; /* Must be a valid joystick */ - if (!SDL_PrivateJoystickValid(&joystick)) { + if (!SDL_PrivateJoystickValid(joystick)) { return -1; } @@ -263,7 +263,7 @@ SDL_HapticOpenFromJoystick(SDL_Joystick * joystick) SDL_Haptic *haptic; /* Must be a valid joystick */ - if (!SDL_PrivateJoystickValid(&joystick)) { + if (!SDL_PrivateJoystickValid(joystick)) { SDL_SetError("Haptic: Joystick isn't valid."); return NULL; } diff --git a/src/haptic/windows/SDL_syshaptic.c b/src/haptic/windows/SDL_syshaptic.c index d351e613b..fcda86e3b 100644 --- a/src/haptic/windows/SDL_syshaptic.c +++ b/src/haptic/windows/SDL_syshaptic.c @@ -520,7 +520,7 @@ SDL_SYS_HapticMouse(void) /* Grab the first mouse haptic device we find. */ for (i = 0; i < SDL_numhaptics; i++) { - if (SDL_hapticlist[i].capabilities.dwDevType == DIDEVTYPE_MOUSE) { + if (SDL_hapticlist[i].capabilities.dwDevType == DI8DEVCLASS_POINTER ) { return i; } } diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c new file mode 100644 index 000000000..10445c198 --- /dev/null +++ b/src/joystick/SDL_gamecontroller.c @@ -0,0 +1,1125 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 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_config.h" + +/* This is the game controller API for Simple DirectMedia Layer */ + +#include "SDL_events.h" +#include "SDL_assert.h" +#include "SDL_sysjoystick.h" +#include "SDL_hints.h" + +#if !SDL_EVENTS_DISABLED +#include "../events/SDL_events_c.h" +#endif +#define ABS(_x) ((_x) < 0 ? -(_x) : (_x)) + + +/* a list of currently opened game controllers */ +static SDL_GameController *SDL_gamecontrollers = NULL; + +/* keep track of the hat and mask value that transforms this hat movement into a button press */ +struct _SDL_HatAsButton +{ + int hat; + Uint8 mask; +}; + +#define k_nMaxReverseEntries 20 + +/* our in memory mapping db between joystick objects and controller mappings*/ +struct _SDL_ControllerMapping +{ + JoystickGUID guid; + const char *name; + + // mapping of axis/button id to controller version + int axes[SDL_CONTROLLER_AXIS_MAX]; + int buttons[SDL_CONTROLLER_BUTTON_MAX]; + + int axesasbutton[SDL_CONTROLLER_BUTTON_MAX]; + struct _SDL_HatAsButton hatasbutton[SDL_CONTROLLER_BUTTON_MAX]; + int buttonasaxis[SDL_CONTROLLER_AXIS_MAX]; + + // reverse mapping, joystick indices to buttons + SDL_CONTROLLER_AXIS raxes[k_nMaxReverseEntries]; + SDL_CONTROLLER_BUTTON rbuttons[k_nMaxReverseEntries]; + SDL_CONTROLLER_BUTTON raxesasbutton[k_nMaxReverseEntries]; + struct _SDL_HatAsButton rhatasbutton[k_nMaxReverseEntries]; + SDL_CONTROLLER_AXIS rbuttonasaxis[k_nMaxReverseEntries]; +}; + + +/* our hard coded list of mapping support */ +typedef struct _ControllerMapping_t +{ + JoystickGUID guid; + char *name; + const char *mapping; + struct _ControllerMapping_t *next; +} ControllerMapping_t; + + +/* default mappings we support */ +const char *s_ControllerMappings [] = +{ +#ifdef __WIN32__ + "xinput,X360 Controller,a:b10,b:b11,y:b13,x:b12,start:b4,guide:b14,back:b5,dpup:b0,dpleft:b2,dpdown:b1,dpright:b3,leftshoulder:b8,rightshoulder:b9,leftstick:b6,rightstick:b7,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5", + "341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7", + "88880803000000000000504944564944,PS3,a:b2,b:b1,x:b0,y:b3,start:b11,back:b8,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.4,dpdown:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b6,righttrigger:b7,guide:b12", + "25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,x:b0,y:b3,start:b8,guide:,back:b9,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.4,dpdown:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5", +#elif defined(__MACOSX__) + "5e040000000000008e02000000000000,X360 Controller,a:b0,b:b1,y:b3,x:b2,start:b8,guide:b10,back:b9,dpup:b11,dpleft:b13,dpdown:b12,dpright:b14,leftshoulder:b4,rightshoulder:b5,leftstick:b6,rightstick:b7,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5", + "4c050000000000006802000000000000,PS3 Controller,a:b14,b:b13,x:b12,y:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b6,dpdown:b7,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9", +#elif defined(__LINUX__) + +#endif + NULL +}; + +static ControllerMapping_t *s_pSupportedControllers = NULL; +static ControllerMapping_t *s_pXInputMapping = NULL; + +/* The SDL game controller structure */ +struct _SDL_GameController +{ + SDL_Joystick *joystick; /* underlying joystick device */ + int ref_count; + struct _SDL_ControllerMapping mapping; /* the mapping object for this controller */ + struct _SDL_GameController *next; /* pointer to next game controller we have allocated */ +}; + + +int SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_CONTROLLER_AXIS axis, Sint16 value); +int SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_CONTROLLER_BUTTON button, Uint8 state); + +/* + * Event filter to fire controller events from joystick ones + */ +int SDL_GameControllerEventWatcher(void *userdata, SDL_Event * event) +{ + switch( event->type ) + { + case SDL_JOYAXISMOTION: + { + SDL_GameController *controllerlist = SDL_gamecontrollers; + while ( controllerlist ) + { + if ( controllerlist->joystick->instance_id == event->jaxis.which ) + { + if ( controllerlist->mapping.raxes[event->jaxis.axis] >= 0 ) // simple axis to axis, send it through + { + SDL_PrivateGameControllerAxis( controllerlist, controllerlist->mapping.raxes[event->jaxis.axis], event->jaxis.value ); + } + else if ( controllerlist->mapping.raxesasbutton[event->jaxis.axis] >= 0 ) // simlate an axis as a button + { + SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.raxesasbutton[event->jaxis.axis], ABS(event->jaxis.value) > 32768/2 ? 1 : 0 ); + } + break; + } + controllerlist = controllerlist->next; + } + } + break; + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + { + SDL_GameController *controllerlist = SDL_gamecontrollers; + while ( controllerlist ) + { + if ( controllerlist->joystick->instance_id == event->jbutton.which ) + { + if ( controllerlist->mapping.rbuttons[event->jbutton.button] >= 0 ) // simple button as button + { + SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rbuttons[event->jbutton.button], event->jbutton.state ); + } + else if ( controllerlist->mapping.rbuttonasaxis[event->jbutton.button] >= 0 ) // an button pretending to be an axis + { + SDL_PrivateGameControllerAxis( controllerlist, controllerlist->mapping.rbuttonasaxis[event->jbutton.button], event->jbutton.state > 0 ? 32768 : 0 ); + } + break; + } + controllerlist = controllerlist->next; + } + } + break; + case SDL_JOYHATMOTION: + { + if ( event->jhat.hat == 0 ) // BUGBUG - multiple hat support?? + { + SDL_GameController *controllerlist = SDL_gamecontrollers; + while ( controllerlist ) + { + if ( controllerlist->joystick->instance_id == event->jhat.which ) + { + static Uint8 bHatsDown = 0; + if ( event->jhat.value == 0 ) + { + if ( bHatsDown & SDL_HAT_DOWN ) + SDL_PrivateGameControllerButton( controllerlist, SDL_CONTROLLER_BUTTON_DPAD_DOWN, 0 ); + if ( bHatsDown & SDL_HAT_UP ) + SDL_PrivateGameControllerButton( controllerlist, SDL_CONTROLLER_BUTTON_DPAD_UP, 0 ); + if ( bHatsDown & SDL_HAT_LEFT ) + SDL_PrivateGameControllerButton( controllerlist, SDL_CONTROLLER_BUTTON_DPAD_LEFT, 0 ); + if ( bHatsDown & SDL_HAT_RIGHT ) + SDL_PrivateGameControllerButton( controllerlist, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, 0 ); + bHatsDown = 0; + } + else if ( controllerlist->mapping.rhatasbutton[event->jhat.value].hat >= 0 ) + { + bHatsDown |= event->jhat.value; + SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[event->jhat.value].hat, (event->jhat.value & controllerlist->mapping.rhatasbutton[event->jhat.value].mask) > 0 ? 1 : 0 ); + } + break; + } + controllerlist = controllerlist->next; + } + } + } + break; + case SDL_JOYDEVICEADDED: + { + if ( SDL_IsGameController(event->jdevice.which ) ) + { + SDL_Event deviceevent; + deviceevent.type = SDL_CONTROLLERDEVICEADDED; + deviceevent.cdevice.which = event->jdevice.which; + SDL_PushEvent(&deviceevent); + } + } + break; + case SDL_JOYDEVICEREMOVED: + { + SDL_GameController *controllerlist = SDL_gamecontrollers; + while ( controllerlist ) + { + if ( controllerlist->joystick->instance_id == event->jdevice.which ) + { + SDL_Event deviceevent; + deviceevent.type = SDL_CONTROLLERDEVICEREMOVED; + deviceevent.cdevice.which = event->jdevice.which; + SDL_PushEvent(&deviceevent); + break; + } + controllerlist = controllerlist->next; + } + } + break; + default: + break; + } + + return 1; +} + +/* + * Helper function to determine pre-caclulated offset to certain joystick mappings + */ +ControllerMapping_t *SDL_PrivateGetControllerMapping(int device_index) +{ +#ifdef __WIN32__ + if ( SDL_SYS_IsXInputDeviceIndex(device_index) && s_pXInputMapping ) + { + return s_pXInputMapping; + } +#endif + return NULL; +} + + +/* + * convert a string to its enum equivalent + */ +SDL_CONTROLLER_AXIS SDL_GameControllerGetAxisFromString( const char *pchString ) +{ + if ( !pchString || !pchString[0] ) + return SDL_CONTROLLER_AXIS_INVALID; + + if ( !SDL_strcasecmp( pchString, "leftx" ) ) + return SDL_CONTROLLER_AXIS_LEFTX; + else if ( !SDL_strcasecmp( pchString, "lefty" ) ) + return SDL_CONTROLLER_AXIS_LEFTY; + else if ( !SDL_strcasecmp( pchString, "rightx" ) ) + return SDL_CONTROLLER_AXIS_RIGHTX; + else if ( !SDL_strcasecmp( pchString, "righty" ) ) + return SDL_CONTROLLER_AXIS_RIGHTY; + else if ( !SDL_strcasecmp( pchString, "lefttrigger" ) ) + return SDL_CONTROLLER_AXIS_TRIGGERLEFT; + else if ( !SDL_strcasecmp( pchString, "righttrigger" ) ) + return SDL_CONTROLLER_AXIS_TRIGGERRIGHT; + else + return SDL_CONTROLLER_AXIS_INVALID; +} + + +/* + * convert a string to its enum equivalent + */ +SDL_CONTROLLER_BUTTON SDL_GameControllerGetButtonFromString( const char *pchString ) +{ + if ( !pchString || !pchString[0] ) + return SDL_CONTROLLER_BUTTON_INVALID; + + if ( !SDL_strcasecmp( pchString, "a" ) ) + return SDL_CONTROLLER_BUTTON_A; + else if ( !SDL_strcasecmp( pchString, "b" ) ) + return SDL_CONTROLLER_BUTTON_B; + else if ( !SDL_strcasecmp( pchString, "x" ) ) + return SDL_CONTROLLER_BUTTON_X; + else if ( !SDL_strcasecmp( pchString, "y" ) ) + return SDL_CONTROLLER_BUTTON_Y; + else if ( !SDL_strcasecmp( pchString, "start" ) ) + return SDL_CONTROLLER_BUTTON_START; + else if ( !SDL_strcasecmp( pchString, "guide" ) ) + return SDL_CONTROLLER_BUTTON_GUIDE; + else if ( !SDL_strcasecmp( pchString, "back" ) ) + return SDL_CONTROLLER_BUTTON_BACK; + else if ( !SDL_strcasecmp( pchString, "dpup" ) ) + return SDL_CONTROLLER_BUTTON_DPAD_UP; + else if ( !SDL_strcasecmp( pchString, "dpdown" ) ) + return SDL_CONTROLLER_BUTTON_DPAD_DOWN; + else if ( !SDL_strcasecmp( pchString, "dpleft" ) ) + return SDL_CONTROLLER_BUTTON_DPAD_LEFT; + else if ( !SDL_strcasecmp( pchString, "dpright" ) ) + return SDL_CONTROLLER_BUTTON_DPAD_RIGHT; + else if ( !SDL_strcasecmp( pchString, "leftshoulder" ) ) + return SDL_CONTROLLER_BUTTON_LEFTSHOULDER; + else if ( !SDL_strcasecmp( pchString, "rightshoulder" ) ) + return SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; + else if ( !SDL_strcasecmp( pchString, "leftstick" ) ) + return SDL_CONTROLLER_BUTTON_LEFTSTICK; + else if ( !SDL_strcasecmp( pchString, "rightstick" ) ) + return SDL_CONTROLLER_BUTTON_RIGHTSTICK; + else + return SDL_CONTROLLER_BUTTON_INVALID; +} + + +/* + * given a controller button name and a joystick name update our mapping structure with it + */ +void SDL_PrivateGameControllerParseButton( const char *szGameButton, const char *szJoystickButton, struct _SDL_ControllerMapping *pMapping ) +{ + int iSDLButton = 0; + SDL_CONTROLLER_BUTTON button; + SDL_CONTROLLER_AXIS axis; + button = SDL_GameControllerGetButtonFromString( szGameButton ); + axis = SDL_GameControllerGetAxisFromString( szGameButton ); + iSDLButton = SDL_atoi( &szJoystickButton[1] ); + + if ( iSDLButton >= k_nMaxReverseEntries ) + { + SDL_SetError("Button index too large: %d", iSDLButton ); + return; + } + + if ( szJoystickButton[0] == 'a' ) + { + if ( axis != SDL_CONTROLLER_AXIS_INVALID ) + { + pMapping->axes[ axis ] = iSDLButton; + pMapping->raxes[ iSDLButton ] = axis; + } + else if ( button != SDL_CONTROLLER_BUTTON_INVALID ) + { + pMapping->buttonasaxis[ button ] = iSDLButton; + pMapping->rbuttonasaxis[ iSDLButton ] = button; + } + else + { + SDL_assert( !"How did we get here?" ); + } + + } + else if ( szJoystickButton[0] == 'b' ) + { + if ( button != SDL_CONTROLLER_BUTTON_INVALID ) + { + pMapping->buttons[ button ] = iSDLButton; + pMapping->rbuttons[ iSDLButton ] = button; + } + else if ( axis != SDL_CONTROLLER_AXIS_INVALID ) + { + pMapping->buttonasaxis[ axis ] = iSDLButton; + pMapping->rbuttonasaxis[ iSDLButton ] = axis; + } + else + { + SDL_assert( !"How did we get here?" ); + } + } + else if ( szJoystickButton[0] == 'h' ) + { + int hat = SDL_atoi( &szJoystickButton[1] ); + int mask = SDL_atoi( &szJoystickButton[3] ); + + if ( button != SDL_CONTROLLER_BUTTON_INVALID ) + { + pMapping->hatasbutton[ button ].hat = hat; + pMapping->hatasbutton[ button ].mask = mask; + pMapping->rhatasbutton[ mask ].hat = button; + pMapping->rhatasbutton[ mask ].mask = mask; + } + else if ( axis != SDL_CONTROLLER_AXIS_INVALID ) + { + SDL_assert( !"Support hat as axis" ); + } + else + { + SDL_assert( !"How did we get here?" ); + } + } + +} + + +/* + * given a controller mapping string update our mapping object + */ +static void +SDL_PrivateGameControllerParseControllerConfigString( struct _SDL_ControllerMapping *pMapping, const char *pchString ) +{ + char szGameButton[20]; + char szJoystickButton[20]; + SDL_bool bGameButton = SDL_TRUE; + int i = 0; + const char *pchPos = pchString; + + SDL_memset( szGameButton, 0x0, sizeof(szGameButton) ); + SDL_memset( szJoystickButton, 0x0, sizeof(szJoystickButton) ); + + while ( pchPos && *pchPos ) + { + if ( *pchPos == ':' ) + { + i = 0; + bGameButton = SDL_FALSE; + } + else if ( *pchPos == ' ' ) + { + + } + else if ( *pchPos == ',' ) + { + i = 0; + bGameButton = SDL_TRUE; + SDL_PrivateGameControllerParseButton( szGameButton, szJoystickButton, pMapping ); + SDL_memset( szGameButton, 0x0, sizeof(szGameButton) ); + SDL_memset( szJoystickButton, 0x0, sizeof(szJoystickButton) ); + + } + else if ( bGameButton ) + { + if ( i >= sizeof(szGameButton)) + { + SDL_SetError( "Button name too large: %s", szGameButton ); + return; + } + szGameButton[i] = *pchPos; + i++; + } + else + { + if ( i >= sizeof(szJoystickButton)) + { + SDL_SetError( "Joystick button name too large: %s", szJoystickButton ); + return; + } + szJoystickButton[i] = *pchPos; + i++; + } + pchPos++; + } + + SDL_PrivateGameControllerParseButton( szGameButton, szJoystickButton, pMapping ); + +} + +/* + * Make a new button mapping struct + */ +void SDL_PrivateLoadButtonMapping( struct _SDL_ControllerMapping *pMapping, JoystickGUID guid, const char *pchName, const char *pchMapping ) +{ + int j; + + pMapping->guid = guid; + pMapping->name = pchName; + + // set all the button mappings to non defaults + for ( j = 0; j < SDL_CONTROLLER_AXIS_MAX; j++ ) + { + pMapping->axes[j] = -1; + pMapping->buttonasaxis[j] = -1; + } + for ( j = 0; j < SDL_CONTROLLER_BUTTON_MAX; j++ ) + { + pMapping->buttons[j] = -1; + pMapping->axesasbutton[j] = -1; + pMapping->hatasbutton[j].hat = -1; + } + + for ( j = 0; j < k_nMaxReverseEntries; j++ ) + { + pMapping->raxes[j] = SDL_CONTROLLER_AXIS_INVALID; + pMapping->rbuttons[j] = SDL_CONTROLLER_BUTTON_INVALID; + pMapping->raxesasbutton[j] = SDL_CONTROLLER_BUTTON_INVALID; + pMapping->rhatasbutton[j].hat = -1; + pMapping->rbuttonasaxis[j] = SDL_CONTROLLER_AXIS_INVALID; + } + + SDL_PrivateGameControllerParseControllerConfigString( pMapping, pchMapping ); +} + + +/* + * grab the guid string from a mapping string + */ +char *SDL_PrivateGetControllerGUIDFromMappingString( const char *pMapping ) +{ + const char *pFirstComma = SDL_strchr( pMapping, ',' ); + if ( pFirstComma ) + { + char *pchGUID = SDL_malloc( pFirstComma - pMapping + 1 ); + if ( !pchGUID ) + { + SDL_OutOfMemory(); + return NULL; + } + SDL_memcpy( pchGUID, pMapping, pFirstComma - pMapping ); + pchGUID[ pFirstComma - pMapping ] = 0; + return pchGUID; + } + return NULL; +} + + +/* + * grab the name string from a mapping string + */ +char *SDL_PrivateGetControllerNameFromMappingString( const char *pMapping ) +{ + const char *pFirstComma = SDL_strchr( pMapping, ',' ); + const char *pSecondComma = SDL_strchr( pFirstComma + 1, ',' ); + if ( pFirstComma && pSecondComma ) + { + char *pchName = SDL_malloc( pSecondComma - pFirstComma ); + if ( !pchName ) + { + SDL_OutOfMemory(); + return NULL; + } + SDL_memcpy( pchName, pFirstComma + 1, pSecondComma - pFirstComma ); + pchName[ pSecondComma - pFirstComma - 1 ] = 0; + return pchName; + } + return NULL; +} + + +/* + * grab the button mapping string from a mapping string + */ +const char *SDL_PrivateGetControllerMappingFromMappingString( const char *pMapping ) +{ + const char *pFirstComma = SDL_strchr( pMapping, ',' ); + const char *pSecondComma = SDL_strchr( pFirstComma + 1, ',' ); + if ( pSecondComma ) + return pSecondComma + 1; // mapping is everything after the 3rd comma, no need to malloc it + else + return NULL; +} + + +/* + * Initialize the game controller system, mostly load our DB of controller config mappings + */ +int +SDL_GameControllerInit(void) +{ + int i = 0; + const char *pMappingString = NULL; + s_pSupportedControllers = NULL; + pMappingString = s_ControllerMappings[i]; + while ( pMappingString ) + { + ControllerMapping_t *pControllerMapping; + char *pchGUID; + char *pchName; + const char *pchMapping; + pControllerMapping = SDL_malloc( sizeof(*pControllerMapping) ); + if ( !pControllerMapping ) + { + SDL_OutOfMemory(); + return -1; + } + + pchGUID = SDL_PrivateGetControllerGUIDFromMappingString( pMappingString ); + pchName = SDL_PrivateGetControllerNameFromMappingString( pMappingString ); + pchMapping = SDL_PrivateGetControllerMappingFromMappingString( pMappingString ); + if ( pchGUID && pchName ) + { +#ifdef __WIN32__ + if ( !SDL_strcasecmp( pchGUID, "xinput" ) ) + { + s_pXInputMapping = pControllerMapping; + } +#endif + pControllerMapping->guid = SDL_JoystickGetGUIDFromString( pchGUID ); + pControllerMapping->name = pchName; + pControllerMapping->mapping = pchMapping; + pControllerMapping->next = s_pSupportedControllers; + s_pSupportedControllers = pControllerMapping; + + SDL_free( pchGUID ); + } + + i++; + pMappingString = s_ControllerMappings[i]; + } + + // load in any user supplied config + { + const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG); + if ( hint && hint[0] ) + { + int nchHints = SDL_strlen( hint ); + char *pUserMappings = SDL_malloc( nchHints + 1 ); + SDL_memcpy( pUserMappings, hint, nchHints ); + while ( pUserMappings ) + { + char *pchGUID; + char *pchName; + const char *pchMapping; + char *pchNewLine = NULL; + ControllerMapping_t *pControllerMapping; + + pchNewLine = SDL_strchr( pUserMappings, '\n' ); + if ( pchNewLine ) + *pchNewLine = '\0'; + + pControllerMapping = SDL_malloc( sizeof(*pControllerMapping) ); + if ( !pControllerMapping ) + { + SDL_OutOfMemory(); + return -1; + } + + pchGUID = SDL_PrivateGetControllerGUIDFromMappingString( pUserMappings ); + pchName = SDL_PrivateGetControllerNameFromMappingString( pUserMappings ); + pchMapping = SDL_PrivateGetControllerMappingFromMappingString( pUserMappings ); + + if ( pchGUID && pchName ) + { +#ifdef __WIN32__ + if ( !SDL_strcasecmp( pchGUID, "xinput" ) ) + { + s_pXInputMapping = pControllerMapping; + } +#endif + + pControllerMapping->guid = SDL_JoystickGetGUIDFromString( pchGUID ); + pControllerMapping->name = pchName; + pControllerMapping->mapping = pchMapping; + pControllerMapping->next = s_pSupportedControllers; + s_pSupportedControllers = pControllerMapping; + + SDL_free( pchGUID ); + } + + if ( pchNewLine ) + pUserMappings = pchNewLine + 1; + else + pUserMappings = NULL; + } + } + } + + /* watch for joy events and fire controller ones if needed */ + SDL_AddEventWatch( SDL_GameControllerEventWatcher, NULL ); + return (0); +} + + +/* + * Get the implementation dependent name of a controller + */ +const char * +SDL_GameControllerNameForIndex(int device_index) +{ + ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index); + if ( pSupportedController ) + { + return pSupportedController->name; + } + else + { + JoystickGUID jGUID = SDL_JoystickGetDeviceGUID( device_index ); + pSupportedController = s_pSupportedControllers; + while ( pSupportedController ) + { + if ( !SDL_memcmp( &jGUID, &pSupportedController->guid, sizeof(jGUID) ) ) + { + return pSupportedController->name; + } + pSupportedController = pSupportedController->next; + } + } + return NULL; +} + + +/* + * Return 1 if the joystick at this device index is a supported controller + */ +int SDL_IsGameController(int device_index) +{ + ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index); + if ( pSupportedController ) + { + return 1; + } + else + { + JoystickGUID jGUID = SDL_JoystickGetDeviceGUID( device_index ); + pSupportedController = s_pSupportedControllers; + // debug code to help get the guid string for a new joystick + /*const char *pchGUID = SDL_JoystickGetGUIDString( jGUID ); + printf( "%s\n", pchGUID ); + SDL_free( pchGUID );*/ + while ( pSupportedController ) + { + if ( !SDL_memcmp( &jGUID, &pSupportedController->guid, sizeof(jGUID) ) ) + { + return 1; + } + pSupportedController = pSupportedController->next; + } + } + return 0; +} + +/* + * Open a controller for use - the index passed as an argument refers to + * the N'th controller on the system. This index is the value which will + * identify this controller in future controller events. + * + * This function returns a controller identifier, or NULL if an error occurred. + */ +SDL_GameController * +SDL_GameControllerOpen(int device_index) +{ + SDL_GameController *gamecontroller; + SDL_GameController *gamecontrollerlist; + ControllerMapping_t *pSupportedController = NULL; + + int deviceMappingIndex = -1; + + if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) { + SDL_SetError("There are %d joysticks available", SDL_NumJoysticks()); + return (NULL); + } + + gamecontrollerlist = SDL_gamecontrollers; + // If the controller is already open, return it + while ( gamecontrollerlist ) + { + if ( SDL_SYS_GetInstanceIdOfDeviceIndex(device_index) == gamecontrollerlist->joystick->instance_id ) { + gamecontroller = gamecontrollerlist; + ++gamecontroller->ref_count; + return (gamecontroller); + } + gamecontrollerlist = gamecontrollerlist->next; + } + + // Create and initialize the joystick + gamecontroller = (SDL_GameController *) SDL_malloc((sizeof *gamecontroller)); + if (gamecontroller == NULL) { + SDL_OutOfMemory(); + return NULL; + } + + pSupportedController = SDL_PrivateGetControllerMapping(device_index); + if ( !pSupportedController ) + { + JoystickGUID jGUID; + + jGUID = SDL_JoystickGetDeviceGUID( device_index ); + pSupportedController = s_pSupportedControllers; + while ( pSupportedController ) + { + if ( !SDL_memcmp( &jGUID, &pSupportedController->guid, sizeof(jGUID) ) ) + { + break; + } + + pSupportedController = pSupportedController->next; + } + } + + if ( !pSupportedController ) + { + SDL_SetError("Couldn't find mapping for device (%d)", device_index ); + return (NULL); + } + + SDL_memset(gamecontroller, 0, (sizeof *gamecontroller)); + gamecontroller->joystick = SDL_JoystickOpen(device_index); + if ( !gamecontroller->joystick ) { + SDL_free(gamecontroller); + return NULL; + } + + SDL_PrivateLoadButtonMapping( &gamecontroller->mapping, pSupportedController->guid, pSupportedController->name, pSupportedController->mapping ); + + // Add joystick to list + ++gamecontroller->ref_count; + // Link the joystick in the list + gamecontroller->next = SDL_gamecontrollers; + SDL_gamecontrollers = gamecontroller; + + SDL_SYS_JoystickUpdate( gamecontroller->joystick ); + + return (gamecontroller); +} + + +/* + * Get the current state of an axis control on a controller + */ +Sint16 +SDL_GameControllerGetAxis(SDL_GameController * gamecontroller, SDL_CONTROLLER_AXIS axis) +{ + if ( !gamecontroller ) + return 0; + + if (gamecontroller->mapping.axes[axis] >= 0 ) + { + return ( SDL_JoystickGetAxis( gamecontroller->joystick, gamecontroller->mapping.axes[axis]) ); + } + else if (gamecontroller->mapping.buttonasaxis[axis] >= 0 ) + { + Uint8 value; + value = SDL_JoystickGetButton( gamecontroller->joystick, gamecontroller->mapping.buttonasaxis[axis] ); + if ( value > 0 ) + return 32767; + return 0; + } + return 0; +} + + +/* + * Get the current state of a button on a controller + */ +Uint8 +SDL_GameControllerGetButton(SDL_GameController * gamecontroller, SDL_CONTROLLER_BUTTON button) +{ + if ( !gamecontroller ) + return 0; + + if ( gamecontroller->mapping.buttons[button] >= 0 ) + { + return ( SDL_JoystickGetButton( gamecontroller->joystick, gamecontroller->mapping.buttons[button] ) ); + } + else if ( gamecontroller->mapping.axesasbutton[button] >= 0 ) + { + Sint16 value; + value = SDL_JoystickGetAxis( gamecontroller->joystick, gamecontroller->mapping.axesasbutton[button] ); + if ( ABS(value) > 32768/2 ) + return 1; + return 0; + } + else if ( gamecontroller->mapping.hatasbutton[button].hat >= 0 ) + { + Uint8 value; + value = SDL_JoystickGetHat( gamecontroller->joystick, gamecontroller->mapping.hatasbutton[button].hat ); + + if ( value & gamecontroller->mapping.hatasbutton[button].mask ) + return 1; + return 0; + } + + return 0; +} + +/* + * Return if the joystick in question is currently attached to the system, + * \return 0 if not plugged in, 1 if still present. + */ +int +SDL_GameControllerGetAttached( SDL_GameController * gamecontroller ) +{ + if ( !gamecontroller ) + return 0; + + return SDL_JoystickGetAttached(gamecontroller->joystick); +} + + +/* + * Get the number of multi-dimensional axis controls on a joystick + */ +const char * +SDL_GameControllerName(SDL_GameController * gamecontroller) +{ + if ( !gamecontroller ) + return NULL; + + return (gamecontroller->mapping.name); +} + + +/* + * Get the joystick for this controller + */ +SDL_Joystick *SDL_GameControllerGetJoystick(SDL_GameController * gamecontroller) +{ + if ( !gamecontroller ) + return NULL; + + return gamecontroller->joystick; +} + +/** + * get the sdl joystick layer binding for this controller axi mapping + */ +SDL_GameControllerButtonBind SDL_GameControllerGetBindForAxis( SDL_GameController * gamecontroller, SDL_CONTROLLER_AXIS axis ) +{ + SDL_GameControllerButtonBind bind; + SDL_memset( &bind, 0x0, sizeof(bind) ); + + if ( !gamecontroller || axis == SDL_CONTROLLER_AXIS_INVALID ) + return bind; + + if (gamecontroller->mapping.axes[axis] >= 0 ) + { + bind.m_eBindType = SDL_CONTROLLER_BINDTYPE_AXIS; + bind.button = gamecontroller->mapping.axes[axis]; + } + else if (gamecontroller->mapping.buttonasaxis[axis] >= 0 ) + { + bind.m_eBindType = SDL_CONTROLLER_BINDTYPE_BUTTON; + bind.button = gamecontroller->mapping.buttonasaxis[axis]; + } + + return bind; +} + + +/** + * get the sdl joystick layer binding for this controller button mapping + */ +SDL_GameControllerButtonBind SDL_GameControllerGetBindForButton( SDL_GameController * gamecontroller, SDL_CONTROLLER_BUTTON button ) +{ + SDL_GameControllerButtonBind bind; + SDL_memset( &bind, 0x0, sizeof(bind) ); + + if ( !gamecontroller || button == SDL_CONTROLLER_BUTTON_INVALID ) + return bind; + + if ( gamecontroller->mapping.buttons[button] >= 0 ) + { + bind.m_eBindType = SDL_CONTROLLER_BINDTYPE_BUTTON; + bind.button = gamecontroller->mapping.buttons[button]; + } + else if ( gamecontroller->mapping.axesasbutton[button] >= 0 ) + { + bind.m_eBindType = SDL_CONTROLLER_BINDTYPE_AXIS; + bind.axis = gamecontroller->mapping.axesasbutton[button]; + } + else if ( gamecontroller->mapping.hatasbutton[button].hat >= 0 ) + { + bind.m_eBindType = SDL_CONTROLLER_BINDTYPE_HAT; + bind.hat.hat = gamecontroller->mapping.hatasbutton[button].hat; + bind.hat.hat_mask = gamecontroller->mapping.hatasbutton[button].mask; + } + + return bind; +} + + +/* + * Close a joystick previously opened with SDL_JoystickOpen() + */ +void +SDL_GameControllerClose(SDL_GameController * gamecontroller) +{ + SDL_GameController *gamecontrollerlist, *gamecontrollerlistprev; + + if ( !gamecontroller ) + return; + + // First decrement ref count + if (--gamecontroller->ref_count > 0) { + return; + } + + SDL_JoystickClose( gamecontroller->joystick ); + + gamecontrollerlist = SDL_gamecontrollers; + gamecontrollerlistprev = NULL; + while ( gamecontrollerlist ) + { + if (gamecontroller == gamecontrollerlist) + { + if ( gamecontrollerlistprev ) + { + // unlink this entry + gamecontrollerlistprev->next = gamecontrollerlist->next; + } + else + { + SDL_gamecontrollers = gamecontroller->next; + } + + break; + } + gamecontrollerlistprev = gamecontrollerlist; + gamecontrollerlist = gamecontrollerlist->next; + } + + SDL_free(gamecontroller); +} + + +/* + * Quit the controller subsystem + */ +void +SDL_GameControllerQuit(void) +{ + ControllerMapping_t *pControllerMap; + while ( SDL_gamecontrollers ) + { + SDL_gamecontrollers->ref_count = 1; + SDL_GameControllerClose(SDL_gamecontrollers); + } + + pControllerMap = s_pSupportedControllers; + while ( s_pSupportedControllers ) + { + pControllerMap = s_pSupportedControllers; + s_pSupportedControllers = s_pSupportedControllers->next; + SDL_free( pControllerMap->name ); + SDL_free( pControllerMap ); + } + + SDL_DelEventWatch( SDL_GameControllerEventWatcher, NULL ); + +} + +/* + * Event filter to transform joystick events into appropriate game controller ones + */ +int +SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_CONTROLLER_AXIS axis, Sint16 value) +{ + int posted; + + /* translate the event, if desired */ + posted = 0; +#if !SDL_EVENTS_DISABLED + if (SDL_GetEventState(SDL_CONTROLLERAXISMOTION) == SDL_ENABLE) { + SDL_Event event; + event.type = SDL_CONTROLLERAXISMOTION; + event.caxis.which = gamecontroller->joystick->instance_id; + event.caxis.axis = axis; + event.caxis.value = value; + posted = SDL_PushEvent(&event) == 1; + } +#endif /* !SDL_EVENTS_DISABLED */ + return (posted); +} + + +/* + * Event filter to transform joystick events into appropriate game controller ones + */ +int +SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_CONTROLLER_BUTTON button, Uint8 state) +{ + int posted; +#if !SDL_EVENTS_DISABLED + SDL_Event event; + + switch (state) { + case SDL_PRESSED: + event.type = SDL_CONTROLLERBUTTONDOWN; + break; + case SDL_RELEASED: + event.type = SDL_CONTROLLERBUTTONUP; + break; + default: + /* Invalid state -- bail */ + return (0); + } +#endif /* !SDL_EVENTS_DISABLED */ + + /* translate the event, if desired */ + posted = 0; +#if !SDL_EVENTS_DISABLED + if (SDL_GetEventState(event.type) == SDL_ENABLE) { + event.cbutton.which = gamecontroller->joystick->instance_id; + event.cbutton.button = button; + event.cbutton.state = state; + posted = SDL_PushEvent(&event) == 1; + } +#endif /* !SDL_EVENTS_DISABLED */ + return (posted); +} + +/* + * Turn off controller events + */ +int +SDL_GameControllerEventState(int state) +{ +#if SDL_EVENTS_DISABLED + return SDL_IGNORE; +#else + const Uint32 event_list[] = { + SDL_CONTROLLERAXISMOTION, SDL_CONTROLLERBUTTONDOWN, SDL_CONTROLLERBUTTONUP, + SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEREMOVED, + }; + unsigned int i; + + switch (state) { + case SDL_QUERY: + state = SDL_IGNORE; + for (i = 0; i < SDL_arraysize(event_list); ++i) { + state = SDL_EventState(event_list[i], SDL_QUERY); + if (state == SDL_ENABLE) { + break; + } + } + break; + default: + for (i = 0; i < SDL_arraysize(event_list); ++i) { + SDL_EventState(event_list[i], state); + } + break; + } + return (state); +#endif /* SDL_EVENTS_DISABLED */ +} + + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index 9bc6e5864..2fc52ee28 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -24,34 +24,22 @@ #include "SDL_events.h" #include "SDL_sysjoystick.h" -#include "SDL_joystick_c.h" #include "SDL_assert.h" #if !SDL_EVENTS_DISABLED #include "../events/SDL_events_c.h" #endif -Uint8 SDL_numjoysticks = 0; -SDL_Joystick **SDL_joysticks = NULL; +SDL_Joystick *SDL_joysticks = NULL; int SDL_JoystickInit(void) { - int arraylen; int status; - SDL_numjoysticks = 0; status = SDL_SYS_JoystickInit(); if (status >= 0) { - arraylen = (status + 1) * sizeof(*SDL_joysticks); - SDL_joysticks = (SDL_Joystick **) SDL_malloc(arraylen); - if (SDL_joysticks == NULL) { - SDL_numjoysticks = 0; - } else { - SDL_memset(SDL_joysticks, 0, arraylen); - SDL_numjoysticks = status; - } - status = 0; + status = 0; } return (status); } @@ -62,20 +50,20 @@ SDL_JoystickInit(void) int SDL_NumJoysticks(void) { - return SDL_numjoysticks; + return SDL_SYS_NumJoysticks(); } /* * Get the implementation dependent name of a joystick */ const char * -SDL_JoystickName(int device_index) +SDL_JoystickNameForIndex(int device_index) { - if ((device_index < 0) || (device_index >= SDL_numjoysticks)) { - SDL_SetError("There are %d joysticks available", SDL_numjoysticks); + if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) { + SDL_SetError("There are %d joysticks available", SDL_NumJoysticks()); return (NULL); } - return (SDL_SYS_JoystickName(device_index)); + return (SDL_SYS_JoystickNameForIndex(device_index)); } /* @@ -88,21 +76,27 @@ SDL_JoystickName(int device_index) SDL_Joystick * SDL_JoystickOpen(int device_index) { - int i; 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); + if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) { + SDL_SetError("There are %d joysticks available", SDL_NumJoysticks()); return (NULL); } - /* If the joystick is already open, return it */ - for (i = 0; SDL_joysticks[i]; ++i) { - if (device_index == SDL_joysticks[i]->index) { - joystick = SDL_joysticks[i]; - ++joystick->ref_count; - return (joystick); - } + joysticklist = SDL_joysticks; + /* If the joystick is already open, return it + * it is important that we have a single joystick * for each instance id + */ + while ( joysticklist ) + { + if ( SDL_SYS_GetInstanceIdOfDeviceIndex(device_index) == joysticklist->instance_id ) { + joystick = joysticklist; + ++joystick->ref_count; + return (joystick); + } + joysticklist = joysticklist->next; } /* Create and initialize the joystick */ @@ -113,11 +107,17 @@ SDL_JoystickOpen(int device_index) } SDL_memset(joystick, 0, (sizeof *joystick)); - joystick->index = device_index; - if (SDL_SYS_JoystickOpen(joystick) < 0) { + if (SDL_SYS_JoystickOpen(joystick, device_index) < 0) { SDL_free(joystick); return NULL; } + + joystickname = SDL_SYS_JoystickNameForIndex( device_index ); + if ( joystickname ) + joystick->name = SDL_strdup( joystickname ); + else + joystick->name = NULL; + if (joystick->naxes > 0) { joystick->axes = (Sint16 *) SDL_malloc (joystick->naxes * sizeof(Sint16)); @@ -158,29 +158,13 @@ SDL_JoystickOpen(int device_index) /* Add joystick to list */ ++joystick->ref_count; - for (i = 0; SDL_joysticks[i]; ++i) - /* Skip to next joystick */ ; - SDL_joysticks[i] = joystick; + /* Link the joystick in the list */ + joystick->next = SDL_joysticks; + SDL_joysticks = joystick; - return (joystick); -} - -/* - * Returns 1 if the joystick has been opened, or 0 if it has not. - */ -int -SDL_JoystickOpened(int device_index) -{ - int i, opened; + SDL_SYS_JoystickUpdate( joystick ); - opened = 0; - for (i = 0; SDL_joysticks[i]; ++i) { - if (SDL_joysticks[i]->index == (Uint8) device_index) { - opened = 1; - break; - } - } - return (opened); + return (joystick); } @@ -188,38 +172,32 @@ SDL_JoystickOpened(int device_index) * Checks to make sure the joystick is valid. */ int -SDL_PrivateJoystickValid(SDL_Joystick ** joystick) +SDL_PrivateJoystickValid(SDL_Joystick * joystick) { int valid; - if (*joystick == NULL) { + if ( joystick == NULL ) { SDL_SetError("Joystick hasn't been opened yet"); valid = 0; } else { valid = 1; } + + if ( joystick && joystick->closed ) + { + valid = 0; + } + return valid; } -/* - * Get the device index of an opened joystick. - */ -int -SDL_JoystickIndex(SDL_Joystick * joystick) -{ - if (!SDL_PrivateJoystickValid(&joystick)) { - return (-1); - } - return (joystick->index); -} - /* * Get the number of multi-dimensional axis controls on a joystick */ int SDL_JoystickNumAxes(SDL_Joystick * joystick) { - if (!SDL_PrivateJoystickValid(&joystick)) { + if (!SDL_PrivateJoystickValid(joystick)) { return (-1); } return (joystick->naxes); @@ -231,7 +209,7 @@ SDL_JoystickNumAxes(SDL_Joystick * joystick) int SDL_JoystickNumHats(SDL_Joystick * joystick) { - if (!SDL_PrivateJoystickValid(&joystick)) { + if (!SDL_PrivateJoystickValid(joystick)) { return (-1); } return (joystick->nhats); @@ -243,7 +221,7 @@ SDL_JoystickNumHats(SDL_Joystick * joystick) int SDL_JoystickNumBalls(SDL_Joystick * joystick) { - if (!SDL_PrivateJoystickValid(&joystick)) { + if (!SDL_PrivateJoystickValid(joystick)) { return (-1); } return (joystick->nballs); @@ -255,7 +233,7 @@ SDL_JoystickNumBalls(SDL_Joystick * joystick) int SDL_JoystickNumButtons(SDL_Joystick * joystick) { - if (!SDL_PrivateJoystickValid(&joystick)) { + if (!SDL_PrivateJoystickValid(joystick)) { return (-1); } return (joystick->nbuttons); @@ -269,7 +247,7 @@ SDL_JoystickGetAxis(SDL_Joystick * joystick, int axis) { Sint16 state; - if (!SDL_PrivateJoystickValid(&joystick)) { + if (!SDL_PrivateJoystickValid(joystick)) { return (0); } if (axis < joystick->naxes) { @@ -289,7 +267,7 @@ SDL_JoystickGetHat(SDL_Joystick * joystick, int hat) { Uint8 state; - if (!SDL_PrivateJoystickValid(&joystick)) { + if (!SDL_PrivateJoystickValid(joystick)) { return (0); } if (hat < joystick->nhats) { @@ -309,7 +287,7 @@ SDL_JoystickGetBall(SDL_Joystick * joystick, int ball, int *dx, int *dy) { int retval; - if (!SDL_PrivateJoystickValid(&joystick)) { + if (!SDL_PrivateJoystickValid(joystick)) { return (-1); } @@ -338,7 +316,7 @@ SDL_JoystickGetButton(SDL_Joystick * joystick, int button) { Uint8 state; - if (!SDL_PrivateJoystickValid(&joystick)) { + if (!SDL_PrivateJoystickValid(joystick)) { return (0); } if (button < joystick->nbuttons) { @@ -350,15 +328,56 @@ SDL_JoystickGetButton(SDL_Joystick * joystick, int button) return (state); } +/* + * Return if the joystick in question is currently attached to the system, + * \return 0 if not plugged in, 1 if still present. + */ +int +SDL_JoystickGetAttached( SDL_Joystick * joystick ) +{ + if (!SDL_PrivateJoystickValid(joystick)) { + return (0); + } + + return SDL_SYS_JoystickAttached(joystick); +} + +/* + * Get the instance id for this opened joystick + */ +SDL_JoystickID +SDL_JoystickInstanceID( SDL_Joystick * joystick ) +{ + if (!SDL_PrivateJoystickValid(joystick)) { + return (-1); + } + + return (joystick->instance_id); +} + +/* + * Get the friendly name of this joystick + */ +const char * +SDL_JoystickName(SDL_Joystick * joystick) +{ + if (!SDL_PrivateJoystickValid(joystick)) { + return (NULL); + } + + return (joystick->name); +} + /* * Close a joystick previously opened with SDL_JoystickOpen() */ void SDL_JoystickClose(SDL_Joystick * joystick) { - int i; + SDL_Joystick *joysticklist; + SDL_Joystick *joysticklistprev; - if (!SDL_PrivateJoystickValid(&joystick)) { + if (!joystick) { return; } @@ -368,15 +387,31 @@ SDL_JoystickClose(SDL_Joystick * joystick) } SDL_SYS_JoystickClose(joystick); - - /* Remove joystick from list */ - for (i = 0; SDL_joysticks[i]; ++i) { - if (joystick == SDL_joysticks[i]) { - SDL_memmove(&SDL_joysticks[i], &SDL_joysticks[i + 1], - (SDL_numjoysticks - i) * sizeof(joystick)); - break; - } - } + + joysticklist = SDL_joysticks; + joysticklistprev = NULL; + while ( joysticklist ) + { + if (joystick == joysticklist) + { + if ( joysticklistprev ) + { + // unlink this entry + joysticklistprev->next = joysticklist->next; + } + else + { + SDL_joysticks = joystick->next; + } + + break; + } + joysticklistprev = joysticklist; + joysticklist = joysticklist->next; + } + + if (joystick->name) + SDL_free(joystick->name); /* Free the data associated with this joystick */ if (joystick->axes) { @@ -397,26 +432,15 @@ SDL_JoystickClose(SDL_Joystick * joystick) void SDL_JoystickQuit(void) { - const int numsticks = SDL_numjoysticks; - int i; - /* Stop the event polling */ - SDL_numjoysticks = 0; - - for (i = numsticks; i--; ) { - SDL_Joystick *stick = SDL_joysticks[i]; - if (stick && (stick->ref_count >= 1)) { - stick->ref_count = 1; - SDL_JoystickClose(stick); - } - } + while ( SDL_joysticks ) + { + SDL_joysticks->ref_count = 1; + SDL_JoystickClose(SDL_joysticks); + } /* Quit the joystick setup */ SDL_SYS_JoystickQuit(); - if (SDL_joysticks) { - SDL_free(SDL_joysticks); - SDL_joysticks = NULL; - } } @@ -441,14 +465,10 @@ SDL_PrivateJoystickAxis(SDL_Joystick * joystick, Uint8 axis, Sint16 value) if (SDL_GetEventState(SDL_JOYAXISMOTION) == SDL_ENABLE) { SDL_Event event; event.type = SDL_JOYAXISMOTION; - event.jaxis.which = joystick->index; + event.jaxis.which = joystick->instance_id; event.jaxis.axis = axis; event.jaxis.value = value; - if ((SDL_EventOK == NULL) - || (*SDL_EventOK) (SDL_EventOKParam, &event)) { - posted = 1; - SDL_PushEvent(&event); - } + posted = SDL_PushEvent(&event) == 1; } #endif /* !SDL_EVENTS_DISABLED */ return (posted); @@ -473,14 +493,10 @@ SDL_PrivateJoystickHat(SDL_Joystick * joystick, Uint8 hat, Uint8 value) if (SDL_GetEventState(SDL_JOYHATMOTION) == SDL_ENABLE) { SDL_Event event; event.jhat.type = SDL_JOYHATMOTION; - event.jhat.which = joystick->index; + event.jhat.which = joystick->instance_id; event.jhat.hat = hat; event.jhat.value = value; - if ((SDL_EventOK == NULL) - || (*SDL_EventOK) (SDL_EventOKParam, &event)) { - posted = 1; - SDL_PushEvent(&event); - } + posted = SDL_PushEvent(&event) == 1; } #endif /* !SDL_EVENTS_DISABLED */ return (posted); @@ -507,15 +523,11 @@ SDL_PrivateJoystickBall(SDL_Joystick * joystick, Uint8 ball, if (SDL_GetEventState(SDL_JOYBALLMOTION) == SDL_ENABLE) { SDL_Event event; event.jball.type = SDL_JOYBALLMOTION; - event.jball.which = joystick->index; + event.jball.which = joystick->instance_id; event.jball.ball = ball; event.jball.xrel = xrel; event.jball.yrel = yrel; - if ((SDL_EventOK == NULL) - || (*SDL_EventOK) (SDL_EventOKParam, &event)) { - posted = 1; - SDL_PushEvent(&event); - } + posted = SDL_PushEvent(&event) == 1; } #endif /* !SDL_EVENTS_DISABLED */ return (posted); @@ -553,14 +565,10 @@ SDL_PrivateJoystickButton(SDL_Joystick * joystick, Uint8 button, Uint8 state) posted = 0; #if !SDL_EVENTS_DISABLED if (SDL_GetEventState(event.type) == SDL_ENABLE) { - event.jbutton.which = joystick->index; + event.jbutton.which = joystick->instance_id; event.jbutton.button = button; event.jbutton.state = state; - if ((SDL_EventOK == NULL) - || (*SDL_EventOK) (SDL_EventOKParam, &event)) { - posted = 1; - SDL_PushEvent(&event); - } + posted = SDL_PushEvent(&event) == 1; } #endif /* !SDL_EVENTS_DISABLED */ return (posted); @@ -569,11 +577,39 @@ SDL_PrivateJoystickButton(SDL_Joystick * joystick, Uint8 button, Uint8 state) void SDL_JoystickUpdate(void) { - int i; - - for (i = 0; SDL_joysticks[i]; ++i) { - SDL_SYS_JoystickUpdate(SDL_joysticks[i]); - } + SDL_Joystick *joystick; + + joystick = SDL_joysticks; + while ( joystick ) + { + SDL_Joystick *joysticknext; + /* save off the next pointer, the Update call may cause a joystick removed event + * and cause our joystick pointer to be freed + */ + joysticknext = joystick->next; + + SDL_SYS_JoystickUpdate( joystick ); + + if ( joystick->closed && joystick->uncentered ) + { + int i; + joystick->uncentered = 0; + + // Tell the app that everything is centered/unpressed... + for (i = 0; i < joystick->naxes; i++) + SDL_PrivateJoystickAxis(joystick, i, 0); + + for (i = 0; i < joystick->nbuttons; i++) + SDL_PrivateJoystickButton(joystick, i, 0); + + for (i = 0; i < joystick->nhats; i++) + SDL_PrivateJoystickHat(joystick, i, SDL_HAT_CENTERED); + } + + joystick = joysticknext; + } + + SDL_SYS_JoystickDetect(); } int @@ -584,7 +620,7 @@ SDL_JoystickEventState(int state) #else const Uint32 event_list[] = { SDL_JOYAXISMOTION, SDL_JOYBALLMOTION, SDL_JOYHATMOTION, - SDL_JOYBUTTONDOWN, SDL_JOYBUTTONUP, + SDL_JOYBUTTONDOWN, SDL_JOYBUTTONUP, SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED }; unsigned int i; @@ -608,4 +644,121 @@ SDL_JoystickEventState(int state) #endif /* SDL_EVENTS_DISABLED */ } +/* return 1 if you want to run the joystick update loop this frame, used by hotplug support */ +int +SDL_PrivateJoystickNeedsPolling() +{ + if ( SDL_SYS_JoystickNeedsPolling() ) + { + // sys layer needs us to think + return 1; + } + else + { + // otherwise only do it if a joystick is opened + return SDL_joysticks != NULL; + } +} + + +/* return the guid for this index*/ +JoystickGUID SDL_JoystickGetDeviceGUID( int device_index ) +{ + return SDL_SYS_PrivateJoystickGetDeviceID( device_index ); +} + +/* return the guid for this openeded device*/ +JoystickGUID SDL_JoystickGetGUID(SDL_Joystick * joystick) +{ + return SDL_SYS_PrivateJoystickGetGUID( joystick ); + +} + +/* convert the guid to a printable string */ +char *SDL_JoystickGetGUIDString(JoystickGUID guid) +{ + static const char k_rgchHexToASCII[] = "0123456789abcdef"; + char *pchOut = NULL; + char *pchString = NULL; + int i; + pchString = SDL_malloc(33); // 16 bytes + if ( !pchString ) + { + SDL_OutOfMemory(); + return NULL; + } + + pchOut = pchString; + + for ( i = 0; i < sizeof(guid); i++ ) + { + // each input byte writes 2 ascii chars, and might write a null byte. + // If we don't have room for next input byte, stop + unsigned char c = guid.data[i]; + + *pchOut++ = k_rgchHexToASCII[ c >> 4 ]; + *pchOut++ = k_rgchHexToASCII[ c & 0x0F ]; + } + *pchOut = '\0'; + return pchString; +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns the 4 bit nibble for a hex character +// Input : c - +// Output : unsigned char +//----------------------------------------------------------------------------- +static unsigned char nibble( char c ) +{ + if ( ( c >= '0' ) && + ( c <= '9' ) ) + { + return (unsigned char)(c - '0'); + } + + if ( ( c >= 'A' ) && + ( c <= 'F' ) ) + { + return (unsigned char)(c - 'A' + 0x0a); + } + + if ( ( c >= 'a' ) && + ( c <= 'f' ) ) + { + return (unsigned char)(c - 'a' + 0x0a); + } + + // received an invalid character, and no real way to return an error + // AssertMsg1( false, "Q_nibble invalid hex character '%c' ", c ); + return 0; +} + + +/* convert the string version of a joystick guid to the struct */ +JoystickGUID SDL_JoystickGetGUIDFromString(const char *pchGUID) +{ + JoystickGUID guid; + int maxoutputbytes= sizeof(guid); + int len = SDL_strlen( pchGUID ); + Uint8 *p; + int i; + + // Make sure it's even + len = ( len ) & ~0x1; + + SDL_memset( &guid, 0x00, sizeof(guid) ); + + p = (Uint8 *)&guid; + for ( i = 0; + ( i < len ) && ( ( p - (Uint8 *)&guid ) < maxoutputbytes ); + i+=2, p++ ) + { + *p = ( nibble( pchGUID[i] ) << 4 ) | nibble( pchGUID[i+1] ); + } + + return guid; +} + + /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h index 1705219c2..b647c7b15 100644 --- a/src/joystick/SDL_joystick_c.h +++ b/src/joystick/SDL_joystick_c.h @@ -23,13 +23,15 @@ /* Useful functions and variables from SDL_joystick.c */ #include "SDL_joystick.h" -/* The number of available joysticks on the system */ -extern Uint8 SDL_numjoysticks; - /* Initialization and shutdown functions */ extern int SDL_JoystickInit(void); extern void SDL_JoystickQuit(void); +/* Initialization and shutdown functions */ +extern int SDL_GameControllerInit(void); +extern void SDL_GameControllerQuit(void); + + /* Internal event queueing functions */ extern int SDL_PrivateJoystickAxis(SDL_Joystick * joystick, Uint8 axis, Sint16 value); @@ -39,8 +41,11 @@ extern int SDL_PrivateJoystickHat(SDL_Joystick * joystick, Uint8 hat, Uint8 value); extern int SDL_PrivateJoystickButton(SDL_Joystick * joystick, Uint8 button, Uint8 state); + +/* Helper function to let lower sys layer tell the event system if the joystick code needs to think */ +extern int SDL_PrivateJoystickNeedsPolling(); /* Internal sanity checking functions */ -extern int SDL_PrivateJoystickValid(SDL_Joystick ** joystick); +extern int SDL_PrivateJoystickValid(SDL_Joystick * joystick); /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h index 2a9b78595..165a96a25 100644 --- a/src/joystick/SDL_sysjoystick.h +++ b/src/joystick/SDL_sysjoystick.h @@ -23,12 +23,13 @@ /* This is the system specific header for the SDL joystick API */ #include "SDL_joystick.h" +#include "SDL_joystick_c.h" /* The SDL joystick structure */ struct _SDL_Joystick { - Uint8 index; /* Device index */ - const char *name; /* Joystick name - system dependent */ + int instance_id; /* Device instance, monotonically increasing from 0 */ + char *name; /* Joystick name - system dependent */ int naxes; /* Number of axis controls on the joystick */ Sint16 *axes; /* Current axis states */ @@ -49,6 +50,10 @@ struct _SDL_Joystick struct joystick_hwdata *hwdata; /* Driver dependent information */ int ref_count; /* Reference count for multiple opens */ + + Uint8 closed; /* 1 if this device is no longer valid */ + Uint8 uncentered; /* 1 if this device needs to have its state reset to 0 */ + struct _SDL_Joystick *next; /* pointer to next joystick we have allocated */ }; /* Function to scan the system for joysticks. @@ -59,14 +64,17 @@ struct _SDL_Joystick extern int SDL_SYS_JoystickInit(void); /* Function to get the device-dependent name of a joystick */ -extern const char *SDL_SYS_JoystickName(int index); +extern const char *SDL_SYS_JoystickNameForIndex(int 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 open a joystick for use. The joystick to open is specified by the index field of the joystick. 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); +extern int SDL_SYS_JoystickOpen(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, @@ -81,4 +89,32 @@ extern void SDL_SYS_JoystickClose(SDL_Joystick * joystick); /* Function to perform any system-specific joystick related cleanup */ extern void SDL_SYS_JoystickQuit(void); +/* Function to query if the joystick is currently attached + * It returns 1 if attached, 0 otherwise. + */ +extern int SDL_SYS_JoystickAttached(SDL_Joystick * joystick); + +/* Function to return the number of joystick devices plugged in right now*/ +extern int SDL_SYS_NumJoysticks(); + +/* Function to cause any queued joystick insertions to be processed + */ +extern void SDL_SYS_JoystickDetect(); + +/* Function to determine if the joystick loop needs to run right now + */ +extern int SDL_SYS_JoystickNeedsPolling(); + +/* Function to return the stable GUID for a plugged in device + */ +extern JoystickGUID SDL_SYS_PrivateJoystickGetDeviceID( int device_index ); + +/* Function to return the stable GUID for a opened joystick + */ +extern JoystickGUID SDL_SYS_PrivateJoystickGetGUID(SDL_Joystick * joystick); + +#ifdef __WIN32__ +/* Function to get the current instance id of the joystick located at device_index */ +extern int SDL_SYS_IsXInputDeviceIndex( int device_index ); +#endif /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/beos/SDL_bejoystick.cc b/src/joystick/beos/SDL_bejoystick.cc old mode 100644 new mode 100755 diff --git a/src/joystick/darwin/SDL_sysjoystick.c b/src/joystick/darwin/SDL_sysjoystick.c index ce7658407..05ee36aa2 100644 --- a/src/joystick/darwin/SDL_sysjoystick.c +++ b/src/joystick/darwin/SDL_sysjoystick.c @@ -42,6 +42,7 @@ #include #include #include /* for NewPtrClear, DisposePtr */ +#include /* For force feedback testing. */ #include @@ -51,11 +52,21 @@ #include "../SDL_sysjoystick.h" #include "../SDL_joystick_c.h" #include "SDL_sysjoystick_c.h" +#include "SDL_events.h" +#if !SDL_EVENTS_DISABLED +#include "../../events/SDL_events_c.h" +#endif /* Linked list of all available devices */ static recDevice *gpDeviceList = NULL; +/* OSX reference to the notification object that tells us about device insertion/removal */ +IONotificationPortRef notificationPort = 0; +/* if 1 then a device was added since the last update call */ +Uint8 s_bDeviceAdded = 0; +/* static incrementing counter for new joystick devices seen on the system. Devices should start with index 0 */ +static int s_joystick_instance_id = -1; static void HIDReportErrorNum(char *strError, long numError) @@ -115,10 +126,20 @@ HIDRemovalCallback(void *target, IOReturn result, void *refcon, void *sender) { recDevice *device = (recDevice *) refcon; device->removed = 1; - device->uncentered = 1; } +/* Called by the io port notifier on removal of this device + */ +void JoystickDeviceWasRemovedCallback( void * refcon, io_service_t service, natural_t messageType, void * messageArgument ) +{ + if( messageType == kIOMessageServiceIsTerminated && refcon ) + { + recDevice *device = (recDevice *) refcon; + device->removed = 1; + } +} + /* Create and open an interface to device, required prior to extracting values or building queues. * Note: appliction now owns the device and must close and release it prior to exiting @@ -162,9 +183,33 @@ HIDCreateOpenDeviceInterface(io_object_t hidDevice, recDevice * pDevice) HIDReportErrorNum ("Failed to open pDevice->interface via open.", result); else + { + pDevice->portIterator = 0; + + // It's okay if this fails, we have another detection method below (*(pDevice->interface))->setRemovalCallback(pDevice->interface, HIDRemovalCallback, pDevice, pDevice); + + /* now connect notification for new devices */ + pDevice->notificationPort = IONotificationPortCreate(kIOMasterPortDefault); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), + IONotificationPortGetRunLoopSource(pDevice->notificationPort), + kCFRunLoopDefaultMode); + + // Register for notifications when a serial port is added to the system + result = IOServiceAddInterestNotification(pDevice->notificationPort, + hidDevice, + kIOGeneralInterest, + JoystickDeviceWasRemovedCallback, + pDevice, + &pDevice->portIterator); + if (kIOReturnSuccess != result) { + HIDReportErrorNum + ("Failed to register for removal callback.", result); + } + } } return result; @@ -195,6 +240,12 @@ HIDCloseReleaseInterface(recDevice * pDevice) HIDReportErrorNum("Failed to release IOHIDDeviceInterface.", result); pDevice->interface = NULL; + + if ( pDevice->portIterator ) + { + IOObjectRelease( pDevice->portIterator ); + pDevice->portIterator = 0; + } } return result; } @@ -461,6 +512,26 @@ HIDGetDeviceInfo(io_object_t hidDevice, CFMutableDictionaryRef hidProperties, ("CFNumberGetValue error retrieving pDevice->usage."); } + refCF = + CFDictionaryGetValue(hidProperties, + CFSTR(kIOHIDVendorIDKey)); + if (refCF) { + if (!CFNumberGetValue + (refCF, kCFNumberLongType, &pDevice->guid.data[0])) + SDL_SetError + ("CFNumberGetValue error retrieving pDevice->guid."); + } + refCF = + CFDictionaryGetValue(hidProperties, + CFSTR(kIOHIDProductIDKey)); + if (refCF) { + if (!CFNumberGetValue + (refCF, kCFNumberLongType, &pDevice->guid.data[8])) + SDL_SetError + ("CFNumberGetValue error retrieving pDevice->guid[8]."); + } + + if (NULL == refCF) { /* get top level element HID usage page or usage */ /* use top level element instead */ CFTypeRef refCFTopElement = 0; @@ -505,6 +576,7 @@ HIDBuildDevice(io_object_t hidDevice) if (kIOReturnSuccess == result) { HIDGetDeviceInfo(hidDevice, hidProperties, pDevice); /* hidDevice used to find parents in registry tree */ HIDGetCollectionElements(hidProperties, pDevice); + pDevice->instance_id = ++s_joystick_instance_id; } else { DisposePtr((Ptr) pDevice); pDevice = NULL; @@ -569,6 +641,79 @@ HIDDisposeDevice(recDevice ** ppDevice) } +/* Given an io_object_t from OSX adds a joystick device to our list if appropriate + */ +int +AddDeviceHelper( io_object_t ioHIDDeviceObject ) +{ + recDevice *device; + + /* build a device record */ + device = HIDBuildDevice(ioHIDDeviceObject); + if (!device) + return 0; + + /* Filter device list to non-keyboard/mouse stuff */ + if ((device->usagePage != kHIDPage_GenericDesktop) || + ((device->usage != kHIDUsage_GD_Joystick && + device->usage != kHIDUsage_GD_GamePad && + device->usage != kHIDUsage_GD_MultiAxisController))) { + + /* release memory for the device */ + HIDDisposeDevice(&device); + DisposePtr((Ptr) device); + return 0; + } + + /* We have to do some storage of the io_service_t for + * SDL_HapticOpenFromJoystick */ + if (FFIsForceFeedback(ioHIDDeviceObject) == FF_OK) { + device->ffservice = ioHIDDeviceObject; + } else { + device->ffservice = 0; + } + + device->send_open_event = 1; + s_bDeviceAdded = 1; + + /* Add device to the end of the list */ + if ( !gpDeviceList ) + { + gpDeviceList = device; + } + else + { + recDevice *curdevice; + + curdevice = gpDeviceList; + while ( curdevice->pNext ) + { + curdevice = curdevice->pNext; + } + curdevice->pNext = device; + } + + return 1; +} + + +/* Called by our IO port notifier on the master port when a HID device is inserted, we iterate + * and check for new joysticks + */ +void JoystickDeviceWasAddedCallback( void *refcon, io_iterator_t iterator ) +{ + io_object_t ioHIDDeviceObject = 0; + + while ( ( ioHIDDeviceObject = IOIteratorNext(iterator) ) ) + { + if ( ioHIDDeviceObject ) + { + AddDeviceHelper( ioHIDDeviceObject ); + } + } +} + + /* 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 @@ -581,10 +726,8 @@ SDL_SYS_JoystickInit(void) mach_port_t masterPort = 0; io_iterator_t hidObjectIterator = 0; CFMutableDictionaryRef hidMatchDictionary = NULL; - recDevice *device, *lastDevice; io_object_t ioHIDDeviceObject = 0; - - SDL_numjoysticks = 0; + io_iterator_t portIterator = 0; if (gpDeviceList) { SDL_SetError("Joystick: Device list already inited."); @@ -629,70 +772,49 @@ SDL_SYS_JoystickInit(void) } if (!hidObjectIterator) { /* there are no joysticks */ gpDeviceList = NULL; - SDL_numjoysticks = 0; return 0; } /* IOServiceGetMatchingServices consumes a reference to the dictionary, so we don't need to release the dictionary ref. */ /* build flat linked list of devices from device iterator */ - gpDeviceList = lastDevice = NULL; + gpDeviceList = NULL; while ((ioHIDDeviceObject = IOIteratorNext(hidObjectIterator))) { - /* build a device record */ - device = HIDBuildDevice(ioHIDDeviceObject); - if (!device) - continue; - - /* Filter device list to non-keyboard/mouse stuff */ - if ((device->usagePage != kHIDPage_GenericDesktop) || - ((device->usage != kHIDUsage_GD_Joystick && - device->usage != kHIDUsage_GD_GamePad && - device->usage != kHIDUsage_GD_MultiAxisController))) { - - /* release memory for the device */ - HIDDisposeDevice(&device); - DisposePtr((Ptr) device); - continue; - } - - /* We have to do some storage of the io_service_t for - * SDL_HapticOpenFromJoystick */ - if (FFIsForceFeedback(ioHIDDeviceObject) == FF_OK) { - device->ffservice = ioHIDDeviceObject; - } else { - device->ffservice = 0; - } - - /* Add device to the end of the list */ - if (lastDevice) - lastDevice->pNext = device; - else - gpDeviceList = device; - lastDevice = device; + AddDeviceHelper( ioHIDDeviceObject ); } result = IOObjectRelease(hidObjectIterator); /* release the iterator */ - - /* Count the total number of devices we found */ - device = gpDeviceList; - while (device) { - SDL_numjoysticks++; - device = device->pNext; - } - - return SDL_numjoysticks; + + /* now connect notification for new devices */ + notificationPort = IONotificationPortCreate(masterPort); + hidMatchDictionary = IOServiceMatching(kIOHIDDeviceKey); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), + IONotificationPortGetRunLoopSource(notificationPort), + kCFRunLoopDefaultMode); + + // Register for notifications when a serial port is added to the system + result = IOServiceAddMatchingNotification(notificationPort, + kIOFirstMatchNotification, + hidMatchDictionary, + JoystickDeviceWasAddedCallback, + NULL, + &portIterator); + while (IOIteratorNext(portIterator)) {}; // Run out the iterator or notifications won't start (you can also use it to iterate the available devices). + + return SDL_SYS_NumJoysticks(); } /* Function to get the device-dependent name of a joystick */ const char * -SDL_SYS_JoystickName(int index) +SDL_SYS_JoystickNameForIndex(int index) { recDevice *device = gpDeviceList; for (; index > 0; index--) device = device->pNext; - return device->product; + return device->product; } /* Function to open a joystick for use. @@ -701,25 +823,77 @@ SDL_SYS_JoystickName(int index) * It returns 0, or -1 if there is an error. */ int -SDL_SYS_JoystickOpen(SDL_Joystick * joystick) +SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) { recDevice *device = gpDeviceList; int index; - for (index = joystick->index; index > 0; index--) + for (index = device_index; index > 0; index--) device = device->pNext; joystick->hwdata = device; - joystick->name = device->product; - - joystick->naxes = device->axes; - joystick->nhats = device->hats; - joystick->nballs = 0; - joystick->nbuttons = device->buttons; + joystick->instance_id = device->instance_id; + joystick->name = device->product; + joystick->naxes = device->axes; + joystick->nhats = device->hats; + joystick->nballs = 0; + joystick->nbuttons = device->buttons; return 0; } + +/* Function to return the instance id of the joystick at device_index + */ +SDL_JoystickID +SDL_SYS_GetInstanceIdOfDeviceIndex( int device_index ) +{ + recDevice *device = gpDeviceList; + int index; + + for (index = device_index; index > 0; index--) + device = device->pNext; + + return device->instance_id; +} + + +/* Function to cause any queued joystick insertions to be processed + */ +void +SDL_SYS_JoystickDetect() +{ + if ( s_bDeviceAdded ) + { + recDevice *device = gpDeviceList; + s_bDeviceAdded = 0; + int device_index = 0; + // send notifications + while ( device ) + { + if ( device->send_open_event ) + { + device->send_open_event = 0; +#if !SDL_EVENTS_DISABLED + SDL_Event event; + event.type = SDL_JOYDEVICEADDED; + + if (SDL_GetEventState(event.type) == SDL_ENABLE) { + event.jdevice.which = device_index; + if ((SDL_EventOK == NULL) + || (*SDL_EventOK) (SDL_EventOKParam, &event)) { + SDL_PushEvent(&event); + } + } +#endif /* !SDL_EVENTS_DISABLED */ + } + device_index++; + device = device->pNext; + } + } +} + + /* 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 @@ -728,26 +902,49 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick) void SDL_SYS_JoystickUpdate(SDL_Joystick * joystick) { - recDevice *device = joystick->hwdata; + recDevice *device = joystick->hwdata; recElement *element; SInt32 value, range; int i; - if (device->removed) { /* device was unplugged; ignore it. */ - if (device->uncentered) { - device->uncentered = 0; - - /* Tell the app that everything is centered/unpressed... */ - for (i = 0; i < device->axes; i++) - SDL_PrivateJoystickAxis(joystick, i, 0); - - for (i = 0; i < device->buttons; i++) - SDL_PrivateJoystickButton(joystick, i, 0); - - for (i = 0; i < device->hats; i++) - SDL_PrivateJoystickHat(joystick, i, SDL_HAT_CENTERED); - } + if ( !device ) + return; + if (device->removed) { /* device was unplugged; ignore it. */ + recDevice *devicelist = gpDeviceList; + joystick->closed = 1; + joystick->uncentered = 1; + + if ( devicelist == device ) + { + gpDeviceList = device->pNext; + } + else + { + while ( devicelist->pNext != device ) + { + devicelist = devicelist->pNext; + } + + devicelist->pNext = device->pNext; + } + + DisposePtr((Ptr) device); + joystick->hwdata = NULL; + +#if !SDL_EVENTS_DISABLED + SDL_Event event; + event.type = SDL_JOYDEVICEREMOVED; + + if (SDL_GetEventState(event.type) == SDL_ENABLE) { + event.jdevice.which = joystick->instance_id; + if ((SDL_EventOK == NULL) + || (*SDL_EventOK) (SDL_EventOKParam, &event)) { + SDL_PushEvent(&event); + } + } +#endif /* !SDL_EVENTS_DISABLED */ + return; } @@ -826,12 +1023,33 @@ SDL_SYS_JoystickUpdate(SDL_Joystick * joystick) return; } + +/* Function to query if the joystick is currently attached + * It returns 1 if attached, 0 otherwise. + */ +int +SDL_SYS_JoystickAttached(SDL_Joystick * joystick) +{ + recDevice *device = gpDeviceList; + int index; + + while ( device ) + { + if ( joystick->instance_id == device->instance_id ) + return (1); + + device = device->pNext; + } + + return 0; +} + + /* Function to close a joystick after use */ void SDL_SYS_JoystickClose(SDL_Joystick * joystick) -{ - /* Should we do anything here? */ - return; +{ + joystick->closed = 1; } /* Function to perform any system-specific joystick related cleanup */ @@ -840,6 +1058,51 @@ SDL_SYS_JoystickQuit(void) { while (NULL != gpDeviceList) gpDeviceList = HIDDisposeDevice(&gpDeviceList); + + if ( notificationPort ) + { + IONotificationPortDestroy( notificationPort ); + notificationPort = 0; + } +} + + +/* Function to return the number of joystick devices plugged in right now*/ +int +SDL_SYS_NumJoysticks() +{ + recDevice *device = gpDeviceList; + int nJoySticks = 0; + + while ( device ) + { + nJoySticks++; + device = device->pNext; + } + + return nJoySticks; +} + +int +SDL_SYS_JoystickNeedsPolling() +{ + return s_bDeviceAdded; +} + +JoystickGUID SDL_SYS_PrivateJoystickGetDeviceID( int device_index ) +{ + recDevice *device = gpDeviceList; + int index; + + for (index = device_index; index > 0; index--) + device = device->pNext; + + return device->guid; +} + +JoystickGUID SDL_SYS_PrivateJoystickGetGUID(SDL_Joystick *joystick) +{ + return joystick->hwdata->guid; } #endif /* SDL_JOYSTICK_IOKIT */ diff --git a/src/joystick/darwin/SDL_sysjoystick_c.h b/src/joystick/darwin/SDL_sysjoystick_c.h index 65dd814bf..0cbc539f9 100644 --- a/src/joystick/darwin/SDL_sysjoystick_c.h +++ b/src/joystick/darwin/SDL_sysjoystick_c.h @@ -25,6 +25,7 @@ #include #include +#include struct recElement @@ -58,7 +59,9 @@ struct joystick_hwdata { io_service_t ffservice; /* Interface for force feedback, 0 = no ff */ IOHIDDeviceInterface **interface; /* interface to device, NULL = no interface */ - + IONotificationPortRef notificationPort; /* port to be notified on joystick removal */ + io_iterator_t portIterator; /* iterator for removal callback */ + char product[256]; /* name of product */ long usage; /* usage page from IOUSBHID Parser.h which defines general usage */ long usagePage; /* usage within above page from IOUSBHID Parser.h which defines specific usage */ @@ -74,6 +77,10 @@ struct joystick_hwdata int removed; int uncentered; + + int instance_id; + JoystickGUID guid; + Uint8 send_open_event; /* 1 if we need to send an Added event for this device */ struct joystick_hwdata *pNext; /* next device */ }; diff --git a/src/joystick/iphoneos/SDLUIAccelerationDelegate.m b/src/joystick/iphoneos/SDLUIAccelerationDelegate.m old mode 100644 new mode 100755 diff --git a/src/joystick/iphoneos/SDL_sysjoystick.m b/src/joystick/iphoneos/SDL_sysjoystick.m old mode 100644 new mode 100755 diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c index 1649eb636..78319e7a5 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/src/joystick/linux/SDL_sysjoystick.c @@ -298,7 +298,7 @@ CountLogicalJoysticks(int max) ret = 0; for (i = 0; i < max; i++) { - name = SDL_SYS_JoystickName(i); + name = SDL_SYS_JoystickNameForIndex(i); fd = open(SDL_joylist[i].fname, O_RDONLY, 0); if (fd >= 0) { @@ -390,6 +390,8 @@ EV_IsJoystick(int fd) #endif /* SDL_INPUT_LINUXEV */ +int SDL_SYS_numjoysticks = 0; + /* Function to scan the system for joysticks */ int SDL_SYS_JoystickInit(void) @@ -491,7 +493,7 @@ SDL_SYS_JoystickInit(void) will be duplicates but without extra information about their hats or balls. Unfortunately, the event devices can't currently be calibrated, so it's a win-lose situation. - So : /dev/input/eventX = /dev/input/jsY = /dev/jsY + So : /dev/input/eventX = /dev/input/jsY = /dev/js */ if ((i == 0) && (numjoysticks > 0)) break; @@ -501,12 +503,13 @@ SDL_SYS_JoystickInit(void) numjoysticks += CountLogicalJoysticks(numjoysticks); #endif + SDL_SYS_numjoysticks = numjoysticks; return (numjoysticks); } /* Function to get the device-dependent name of a joystick */ const char * -SDL_SYS_JoystickName(int index) +SDL_SYS_JoystickNameForIndex(int index) { int fd; static char namebuf[128]; @@ -601,7 +604,7 @@ JS_ConfigJoystick(SDL_Joystick * joystick, int fd) joystick->nbuttons = n; } - name = SDL_SYS_JoystickName(joystick->index); + name = SDL_SYS_JoystickNameForIndex(joystick->instance_id); /* Generic analog joystick support */ if (SDL_strstr(name, "Analog") == name && SDL_strstr(name, "-hat")) { @@ -774,8 +777,8 @@ ConfigLogicalJoystick(SDL_Joystick * joystick) { struct joystick_logical_layout *layout; - layout = SDL_joylist[joystick->index].map->layout + - SDL_joylist[joystick->index].logicalno; + layout = SDL_joylist[joystick->instance_id].map->layout + + SDL_joylist[joystick->instance_id].logicalno; joystick->nbuttons = layout->nbuttons; joystick->nhats = layout->nhats; @@ -791,7 +794,7 @@ ConfigLogicalJoystick(SDL_Joystick * joystick) It returns 0, or -1 if there is an error. */ int -SDL_SYS_JoystickOpen(SDL_Joystick * joystick) +SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) { int fd; char *fname; @@ -800,8 +803,8 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick) /* Open the joystick and set the joystick file descriptor */ #ifndef NO_LOGICAL_JOYSTICKS - if (SDL_joylist[joystick->index].fname == NULL) { - SDL_joylist_head(realindex, joystick->index); + if (SDL_joylist[joystick->instance_id].fname == NULL) { + SDL_joylist_head(realindex, joystick->instance_id); realjoy = SDL_JoystickOpen(realindex); if (realjoy == NULL) @@ -811,17 +814,17 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick) fname = realjoy->hwdata->fname; } else { - fd = open(SDL_joylist[joystick->index].fname, O_RDONLY, 0); - fname = SDL_joylist[joystick->index].fname; + fd = open(SDL_joylist[joystick->instance_id].fname, O_RDONLY, 0); + fname = SDL_joylist[joystick->instance_id].fname; } - SDL_joylist[joystick->index].joy = joystick; + SDL_joylist[joystick->instance_id].joy = joystick; #else - fd = open(SDL_joylist[joystick->index].fname, O_RDONLY, 0); - fname = SDL_joylist[joystick->index].fname; + fd = open(SDL_joylist[joystick->instance_id].fname, O_RDONLY, 0); + fname = SDL_joylist[joystick->instance_id].fname; #endif if (fd < 0) { - SDL_SetError("Unable to open %s\n", SDL_joylist[joystick->index]); + SDL_SetError("Unable to open %s\n", SDL_joylist[joystick->instance_id]); return (-1); } joystick->hwdata = (struct joystick_hwdata *) @@ -834,6 +837,7 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick) SDL_memset(joystick->hwdata, 0, sizeof(*joystick->hwdata)); joystick->hwdata->fd = fd; joystick->hwdata->fname = fname; + joystick->instance_id = device_index; /* Set the joystick to non-blocking read mode */ fcntl(fd, F_SETFL, O_NONBLOCK); @@ -861,7 +865,7 @@ FindLogicalJoystick(SDL_Joystick * joystick, SDL_Joystick *logicaljoy; register int i; - i = joystick->index; + i = joystick->instance_id; logicaljoy = NULL; /* get the fake joystick that will receive the event @@ -891,12 +895,12 @@ LogicalJoystickButton(SDL_Joystick * joystick, Uint8 button, Uint8 state) /* if there's no map then this is just a regular joystick */ - if (SDL_joylist[joystick->index].map == NULL) + if (SDL_joylist[joystick->instance_id].map == NULL) return 0; /* get the logical joystick that will receive the event */ - buttons = SDL_joylist[joystick->index].map->buttonmap + button; + buttons = SDL_joylist[joystick->instance_id].map->buttonmap + button; logicaljoy = FindLogicalJoystick(joystick, buttons); if (logicaljoy == NULL) @@ -915,12 +919,12 @@ LogicalJoystickAxis(SDL_Joystick * joystick, Uint8 axis, Sint16 value) /* if there's no map then this is just a regular joystick */ - if (SDL_joylist[joystick->index].map == NULL) + if (SDL_joylist[joystick->instance_id].map == NULL) return 0; /* get the logical joystick that will receive the event */ - axes = SDL_joylist[joystick->index].map->axismap + axis; + axes = SDL_joylist[joystick->instance_id].map->axismap + axis; logicaljoy = FindLogicalJoystick(joystick, axes); if (logicaljoy == NULL) @@ -958,11 +962,11 @@ HandleHat(SDL_Joystick * stick, Uint8 hat, int axis, int value) #ifndef NO_LOGICAL_JOYSTICKS /* if there's no map then this is just a regular joystick */ - if (SDL_joylist[stick->index].map != NULL) { + if (SDL_joylist[stick->instance_id].map != NULL) { /* get the fake joystick that will receive the event */ - hats = SDL_joylist[stick->index].map->hatmap + hat; + hats = SDL_joylist[stick->instance_id].map->hatmap + hat; logicaljoy = FindLogicalJoystick(stick, hats); } @@ -997,8 +1001,8 @@ JS_HandleEvents(SDL_Joystick * joystick) Uint8 other_axis; #ifndef NO_LOGICAL_JOYSTICKS - if (SDL_joylist[joystick->index].fname == NULL) { - SDL_joylist_head(i, joystick->index); + if (SDL_joylist[joystick->instance_id].fname == NULL) { + SDL_joylist_head(i, joystick->instance_id); JS_HandleEvents(SDL_joylist[i].joy); return; } @@ -1089,8 +1093,8 @@ EV_HandleEvents(SDL_Joystick * joystick) int code; #ifndef NO_LOGICAL_JOYSTICKS - if (SDL_joylist[joystick->index].fname == NULL) { - SDL_joylist_head(i, joystick->index); + if (SDL_joylist[joystick->instance_id].fname == NULL) { + SDL_joylist_head(i, joystick->instance_id); return EV_HandleEvents(SDL_joylist[i].joy); } #endif @@ -1198,15 +1202,15 @@ SDL_SYS_JoystickClose(SDL_Joystick * joystick) { #ifndef NO_LOGICAL_JOYSTICKS register int i; - if (SDL_joylist[joystick->index].fname == NULL) { - SDL_joylist_head(i, joystick->index); + if (SDL_joylist[joystick->instance_id].fname == NULL) { + SDL_joylist_head(i, joystick->instance_id); SDL_JoystickClose(SDL_joylist[i].joy); } #endif if (joystick->hwdata) { #ifndef NO_LOGICAL_JOYSTICKS - if (SDL_joylist[joystick->index].fname != NULL) + if (SDL_joylist[joystick->instance_id].fname != NULL) #endif close(joystick->hwdata->fd); if (joystick->hwdata->hats) { @@ -1218,6 +1222,7 @@ SDL_SYS_JoystickClose(SDL_Joystick * joystick) SDL_free(joystick->hwdata); joystick->hwdata = NULL; } + joystick->closed = 1; } /* Function to perform any system-specific joystick related cleanup */ @@ -1234,5 +1239,50 @@ SDL_SYS_JoystickQuit(void) } } +/* Function to perform the mapping from device index to the instance id for this index */ +SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int index) +{ + return index; +} + +/* Function to determine is this joystick is attached to the system right now */ +int SDL_SYS_JoystickAttached(SDL_Joystick *joystick) +{ + return joystick->closed; +} + +int SDL_SYS_NumJoysticks() +{ + return SDL_SYS_numjoysticks; +} + +int SDL_SYS_JoystickNeedsPolling() +{ + return 0; +} + +void SDL_SYS_JoystickDetect() +{ +} + +JoystickGUID SDL_SYS_PrivateJoystickGetDeviceID( int device_index ) +{ + static JoystickGUID guid; + // the GUID is just the first 16 chars of the name for now + const char *name = SDL_SYS_JoystickNameForIndex( device_index ); + SDL_memcpy( &guid, name, sizeof(guid) ); + return guid; +} + + +JoystickGUID SDL_SYS_PrivateJoystickGetGUID(SDL_Joystick * joystick) +{ + static JoystickGUID guid; + // the GUID is just the first 16 chars of the name for now + const char *name = SDL_SYS_JoystickNameForIndex( joystick->name ); + SDL_memcpy( &guid, name, sizeof(guid) ); + return guid; +} + #endif /* SDL_JOYSTICK_LINUX */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/windows/SDL_dxjoystick.c b/src/joystick/windows/SDL_dxjoystick.c index f8719e03b..b7258a74e 100644 --- a/src/joystick/windows/SDL_dxjoystick.c +++ b/src/joystick/windows/SDL_dxjoystick.c @@ -29,17 +29,22 @@ * doesn't use them and I don't own any joysticks with them. * * We don't bother to use event notification here. It doesn't seem to work - * with polled devices, and it's fine to call IDirectInputDevice2_GetDeviceData and + * with polled devices, and it's fine to call IDirectInputDevice8_GetDeviceData and * let it return 0 events. */ #include "SDL_error.h" #include "SDL_events.h" #include "SDL_joystick.h" #include "../SDL_sysjoystick.h" -#include "../SDL_joystick_c.h" #define INITGUID /* Only set here, if set twice will cause mingw32 to break. */ #include "SDL_dxjoystick_c.h" - +#include "SDL_thread.h" +#include "SDL_timer.h" +#include "SDL_mutex.h" +#include "SDL_events.h" +#if !SDL_EVENTS_DISABLED +#include "../../events/SDL_events_c.h" +#endif #ifndef DIDFT_OPTIONAL #define DIDFT_OPTIONAL 0x80000000 @@ -47,7 +52,7 @@ #define INPUT_QSIZE 32 /* Buffer up to 32 input messages */ -#define MAX_JOYSTICKS 8 +#define MAX_JOYSTICKS 8 #define AXIS_MIN -32768 /* minimum value for axis coordinate */ #define AXIS_MAX 32767 /* maximum value for axis coordinate */ #define JOY_AXIS_THRESHOLD (((AXIS_MAX)-(AXIS_MIN))/100) /* 1% motion */ @@ -59,13 +64,64 @@ extern HWND SDL_HelperWindow; /* local variables */ static SDL_bool coinitialized = SDL_FALSE; static LPDIRECTINPUT dinput = NULL; +static SDL_bool s_bDeviceAdded = SDL_FALSE; +static SDL_bool s_bDeviceRemoved = SDL_FALSE; +static int s_nInstanceID = -1; +static GUID *s_pKnownJoystickGUIDs = NULL; +static SDL_cond *s_condJoystickThread = NULL; +static SDL_mutex *s_mutexJoyStickEnum = NULL; +static SDL_Thread *s_threadJoystick = NULL; +static SDL_bool s_bJoystickThreadQuit = SDL_FALSE; +static HANDLE s_pXInputDLL = 0; + extern HRESULT(WINAPI * DInputCreate) (HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUT * ppDI, LPUNKNOWN punkOuter); -static DIDEVICEINSTANCE SYS_Joystick[MAX_JOYSTICKS]; /* array to hold joystick ID values */ -static char *SYS_JoystickNames[MAX_JOYSTICKS]; -static int SYS_NumJoysticks; +struct JoyStick_DeviceData_ +{ + JoystickGUID guid; + DIDEVICEINSTANCE dxdevice; + char *joystickname; + Uint8 send_add_event; + int nInstanceID; + Uint8 bXInputDevice; + Uint8 XInputUserId; + struct JoyStick_DeviceData_ *pNext; +}; + + +/* Forward decl's for XInput API's we load dynamically and use if available */ +typedef DWORD (WINAPI *XInputGetState_t) + ( + DWORD dwUserIndex, // [in] Index of the gamer associated with the device + XINPUT_STATE_EX* pState // [out] Receives the current state + ); + +typedef DWORD (WINAPI *XInputSetState_t) + ( + DWORD dwUserIndex, // [in] Index of the gamer associated with the device + XINPUT_VIBRATION* pVibration // [in, out] The vibration information to send to the controller + ); +typedef DWORD (WINAPI *XInputGetCapabilities_t) + ( + DWORD dwUserIndex, // [in] Index of the gamer associated with the device + DWORD dwFlags, // [in] Input flags that identify the device type + XINPUT_CAPABILITIES* pCapabilities // [out] Receives the capabilities + ); + +XInputGetState_t PC_XInputGetState; +XInputSetState_t PC_XInputSetState; +XInputGetCapabilities_t PC_XInputGetCapabilities; + +#define XINPUTGETSTATE PC_XInputGetState +#define XINPUTSETSTATE PC_XInputSetState +#define XINPUTGETCAPABILITIES PC_XInputGetCapabilities +#define INVALID_XINPUT_USERID 255 + +typedef struct JoyStick_DeviceData_ JoyStick_DeviceData; + +static JoyStick_DeviceData *SYS_Joystick; /* array to hold joystick ID values */ /* local prototypes */ static void SetDIerror(const char *function, HRESULT code); @@ -82,7 +138,7 @@ static int SDL_PrivateJoystickHat_Int(SDL_Joystick * joystick, Uint8 hat, static int SDL_PrivateJoystickButton_Int(SDL_Joystick * joystick, Uint8 button, Uint8 state); -/* Taken from Wine - Thanks! */ +// Taken from Wine - Thanks! DIOBJECTDATAFORMAT dfDIJoystick2[] = { { &GUID_XAxis,DIJOFS_X,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, { &GUID_YAxis,DIJOFS_Y,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, @@ -272,6 +328,270 @@ SetDIerror(const char *function, HRESULT code) } +#define SAFE_RELEASE(p) \ +{ \ + if (p) { \ + (p)->lpVtbl->Release((p)); \ + (p) = 0; \ + } \ +} + + +DEFINE_GUID(CLSID_WbemLocator, 0x4590f811,0x1d3a,0x11d0,0x89,0x1F,0x00,0xaa,0x00,0x4b,0x2e,0x24); +DEFINE_GUID(IID_IWbemLocator, 0xdc12a687,0x737f,0x11cf,0x88,0x4d,0x00,0xaa,0x00,0x4b,0x2e,0x24); + +//----------------------------------------------------------------------------- +// +// code from MSDN: http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014(v=vs.85).aspx +// +// Enum each PNP device using WMI and check each device ID to see if it contains +// "IG_" (ex. "VID_045E&PID_028E&IG_00"). If it does, then it's an XInput device +// Unfortunately this information can not be found by just using DirectInput +//----------------------------------------------------------------------------- +BOOL IsXInputDevice( const GUID* pGuidProductFromDirectInput ) +{ + IWbemLocator* pIWbemLocator = NULL; + IEnumWbemClassObject* pEnumDevices = NULL; + IWbemClassObject* pDevices[20]; + IWbemServices* pIWbemServices = NULL; + DWORD uReturned = 0; + BSTR bstrNamespace = NULL; + BSTR bstrDeviceID = NULL; + BSTR bstrClassName = NULL; + SDL_bool bIsXinputDevice= SDL_FALSE; + UINT iDevice = 0; + VARIANT var; + HRESULT hr; + DWORD bCleanupCOM; + + SDL_memset( pDevices, 0x0, sizeof(pDevices) ); + + // CoInit if needed + hr = CoInitialize(NULL); + bCleanupCOM = SUCCEEDED(hr); + + // Create WMI + hr = CoCreateInstance( &CLSID_WbemLocator, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IWbemLocator, + (LPVOID*) &pIWbemLocator); + if( FAILED(hr) || pIWbemLocator == NULL ) + goto LCleanup; + + bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );if( bstrNamespace == NULL ) goto LCleanup; + bstrClassName = SysAllocString( L"Win32_PNPEntity" ); if( bstrClassName == NULL ) goto LCleanup; + bstrDeviceID = SysAllocString( L"DeviceID" ); if( bstrDeviceID == NULL ) goto LCleanup; + + // Connect to WMI + hr = IWbemLocator_ConnectServer( pIWbemLocator, bstrNamespace, NULL, NULL, 0L, + 0L, NULL, NULL, &pIWbemServices ); + if( FAILED(hr) || pIWbemServices == NULL ) + goto LCleanup; + + // Switch security level to IMPERSONATE. + CoSetProxyBlanket( (IUnknown *)pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, + RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE ); + + hr = IWbemServices_CreateInstanceEnum( pIWbemServices, bstrClassName, 0, NULL, &pEnumDevices ); + if( FAILED(hr) || pEnumDevices == NULL ) + goto LCleanup; + + // Loop over all devices + for( ;; ) + { + // Get 20 at a time + hr = IEnumWbemClassObject_Next( pEnumDevices, 10000, 20, pDevices, &uReturned ); + if( FAILED(hr) ) + goto LCleanup; + if( uReturned == 0 ) + break; + + for( iDevice=0; iDeviceData1 ) + { + bIsXinputDevice = SDL_TRUE; + } + } + if ( pDeviceString ) + SDL_free( pDeviceString ); + + if ( bIsXinputDevice ) + break; + } + SAFE_RELEASE( pDevices[iDevice] ); + } + } + +LCleanup: + + for( iDevice=0; iDevice<20; iDevice++ ) + SAFE_RELEASE( pDevices[iDevice] ); + SAFE_RELEASE( pEnumDevices ); + SAFE_RELEASE( pIWbemLocator ); + SAFE_RELEASE( pIWbemServices ); + + if ( bstrNamespace ) + SysFreeString( bstrNamespace ); + if ( bstrClassName ) + SysFreeString( bstrClassName ); + if ( bstrDeviceID ) + SysFreeString( bstrDeviceID ); + + if( bCleanupCOM ) + CoUninitialize(); + + return bIsXinputDevice; +} + + +static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE; + +/* windowproc for our joystick detect thread message only window, to detect any usb device addition/removal + */ +LRESULT CALLBACK SDL_PrivateJoystickDetectProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { + switch (message) { + case WM_DEVICECHANGE: + switch (wParam) { + case DBT_DEVICEARRIVAL: + if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { + s_bWindowsDeviceChanged = SDL_TRUE; + } + break; + case DBT_DEVICEREMOVECOMPLETE: + if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { + s_bWindowsDeviceChanged = SDL_TRUE; + } + break; + } + return 0; + } + + return DefWindowProc (hwnd, message, wParam, lParam); +} + + +/* helper func to create a hidden, message only window for the joystick detect thread + */ +HWND CreateHiddenJoystickDetectWindow() { + WNDCLASSEX wincl; + HWND hMessageWindow = 0; + SDL_memset( &wincl, 0x0, sizeof(wincl) ); + wincl.hInstance = GetModuleHandle( NULL ); + wincl.lpszClassName = L"Message"; + wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc; // This function is called by windows + wincl.cbSize = sizeof (WNDCLASSEX); + + if (!RegisterClassEx (&wincl)) + return 0; + + hMessageWindow = (HWND)CreateWindowEx( 0, L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL ); + return hMessageWindow; +} + +DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, \ + 0xC0, 0x4F, 0xB9, 0x51, 0xED); + +/* Function/thread to scan the system for joysticks. + */ +static int +SDL_JoystickThread(void *_data) +{ + int nDevicesLast = 0; + HRESULT result = S_OK; + HWND messageWindow = 0; + HDEVNOTIFY hNotify = 0; + DEV_BROADCAST_DEVICEINTERFACE dbh; + + result = WIN_CoInitialize(); + + messageWindow = CreateHiddenJoystickDetectWindow(); + if ( !messageWindow ) + { + SDL_SetError("Failed to create message window for joystick autodetect.", + GetLastError()); + return -1; + } + + SDL_memset(&dbh, 0x0, sizeof(dbh)); + + dbh.dbcc_size = sizeof(dbh); + dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + dbh.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE; + + hNotify = RegisterDeviceNotification( messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE ); + if ( !hNotify ) + { + SDL_SetError("Failed to create notify device for joystick autodetect.", + GetLastError()); + return -1; + } + + SDL_LockMutex( s_mutexJoyStickEnum ); + while ( s_bJoystickThreadQuit == SDL_FALSE ) + { + MSG messages; + SDL_CondWaitTimeout( s_condJoystickThread, s_mutexJoyStickEnum, 300 ); + + while ( s_bJoystickThreadQuit == SDL_FALSE && PeekMessage(&messages, messageWindow, 0, 0, PM_NOREMOVE) ) + { + if ( GetMessage(&messages, messageWindow, 0, 0) != 0 ) { + TranslateMessage(&messages); + DispatchMessage(&messages); + } + } + + if ( s_pKnownJoystickGUIDs && s_bWindowsDeviceChanged ) + { + int nDevices = 0; + + SDL_Delay( 300 ); // wait for direct input to find out about this device + + s_bDeviceRemoved = SDL_TRUE; + s_bDeviceAdded = SDL_TRUE; + s_bWindowsDeviceChanged = SDL_FALSE; + } + } + SDL_UnlockMutex( s_mutexJoyStickEnum ); + + if ( hNotify ) + UnregisterDeviceNotification( hNotify ); + + if ( messageWindow ) + DestroyWindow( messageWindow ); + messageWindow = 0; + WIN_CoUninitialize(); + return 1; +} + + /* Function to scan the system for joysticks. * This function should set SDL_numjoysticks to the number of available * joysticks. Joystick 0 should be the system default joystick. @@ -283,8 +603,6 @@ SDL_SYS_JoystickInit(void) HRESULT result; HINSTANCE instance; - SYS_NumJoysticks = 0; - result = WIN_CoInitialize(); if (FAILED(result)) { SetDIerror("CoInitialize", result); @@ -293,8 +611,8 @@ SDL_SYS_JoystickInit(void) coinitialized = SDL_TRUE; - result = CoCreateInstance(&CLSID_DirectInput, NULL, CLSCTX_INPROC_SERVER, - &IID_IDirectInput, (LPVOID)&dinput); + result = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectInput8, (LPVOID)&dinput); if (FAILED(result)) { SDL_SYS_JoystickQuit(); @@ -310,7 +628,7 @@ SDL_SYS_JoystickInit(void) GetLastError()); return (-1); } - result = IDirectInput_Initialize(dinput, instance, DIRECTINPUT_VERSION); + result = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION); if (FAILED(result)) { SDL_SYS_JoystickQuit(); @@ -318,34 +636,54 @@ SDL_SYS_JoystickInit(void) return (-1); } - /* Look for joysticks, wheels, head trackers, gamepads, etc.. */ - result = IDirectInput_EnumDevices(dinput, - DIDEVTYPE_JOYSTICK, - EnumJoysticksCallback, - NULL, DIEDFL_ATTACHEDONLY); + s_mutexJoyStickEnum = SDL_CreateMutex(); + s_condJoystickThread = SDL_CreateCond(); + s_bDeviceAdded = SDL_TRUE; // force a scan of the system for joysticks this first time + SDL_SYS_JoystickDetect(); - return SYS_NumJoysticks; -} - -static BOOL CALLBACK -EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext) -{ - SDL_memcpy(&SYS_Joystick[SYS_NumJoysticks], pdidInstance, - sizeof(DIDEVICEINSTANCE)); - SYS_JoystickNames[SYS_NumJoysticks] = WIN_StringToUTF8(pdidInstance->tszProductName); - SYS_NumJoysticks++; + // try to load XInput support if available + s_pXInputDLL = LoadLibrary( L"XInput1_3.dll" ); + if ( !s_pXInputDLL ) + s_pXInputDLL = LoadLibrary( L"bin\\XInput1_3.dll" ); + if ( s_pXInputDLL ) + { + // 100 is the ordinal for _XInputGetStateEx, which returns the same struct as XinputGetState, but with extra data in wButtons for the guide button, we think... + PC_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, (LPCSTR)100 ); + PC_XInputSetState = (XInputSetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputSetState" ); + PC_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputGetCapabilities" ); + if ( !PC_XInputGetState || !PC_XInputSetState || !PC_XInputGetCapabilities ) + { + SDL_SYS_JoystickQuit(); + SDL_SetError("GetProcAddress() failed when loading XInput.", GetLastError()); + return (-1); + } + } - if (SYS_NumJoysticks >= MAX_JOYSTICKS) - return DIENUM_STOP; - return DIENUM_CONTINUE; + if ( !s_threadJoystick ) + { + s_bJoystickThreadQuit = SDL_FALSE; + /* spin up the thread to detect hotplug of devices */ +#if defined(__WIN32__) && !defined(HAVE_LIBC) +#undef SDL_CreateThread + s_threadJoystick= SDL_CreateThread( SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL ); +#else + s_threadJoystick = SDL_CreateThread( SDL_JoystickThread, "SDL_joystick", NULL ); +#endif + } + return SDL_SYS_NumJoysticks(); } /* Function to get the device-dependent name of a joystick */ const char * -SDL_SYS_JoystickName(int index) +SDL_SYS_JoystickNameForIndex(int index) { - return SYS_JoystickNames[index]; + JoyStick_DeviceData *device = SYS_Joystick; + + for (; index > 0; index--) + device = device->pNext; + + return device->joystickname; } /* Function to open a joystick for use. @@ -354,17 +692,20 @@ SDL_SYS_JoystickName(int index) It returns 0, or -1 if there is an error. */ int -SDL_SYS_JoystickOpen(SDL_Joystick * joystick) +SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) { HRESULT result; LPDIRECTINPUTDEVICE device; DIPROPDWORD dipdw; + JoyStick_DeviceData *joystickdevice = SYS_Joystick; + + for (; device_index > 0; device_index--) + joystickdevice = joystickdevice->pNext; SDL_memset(&dipdw, 0, sizeof(DIPROPDWORD)); dipdw.diph.dwSize = sizeof(DIPROPDWORD); dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); - /* allocate memory for system specific hardware data */ joystick->hwdata = (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata)); @@ -374,137 +715,195 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick) } SDL_memset(joystick->hwdata, 0, sizeof(struct joystick_hwdata)); joystick->hwdata->buffered = 1; + joystick->hwdata->removed = 0; + joystick->instance_id = joystickdevice->nInstanceID; joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS); + joystick->hwdata->guid = joystickdevice->guid; - result = - IDirectInput_CreateDevice(dinput, - &SYS_Joystick[joystick->index]. - guidInstance, &device, NULL); - if (FAILED(result)) { - SetDIerror("IDirectInput::CreateDevice", result); - return (-1); - } - - /* Now get the IDirectInputDevice2 interface, instead. */ - result = IDirectInputDevice_QueryInterface(device, - &IID_IDirectInputDevice2, - (LPVOID *) & joystick-> - hwdata->InputDevice); - /* We are done with this object. Use the stored one from now on. */ - IDirectInputDevice_Release(device); - - if (FAILED(result)) { - SetDIerror("IDirectInputDevice::QueryInterface", result); - return (-1); - } - - /* Aquire shared access. Exclusive access is required for forces, - * though. */ - result = - IDirectInputDevice2_SetCooperativeLevel(joystick->hwdata-> - InputDevice, SDL_HelperWindow, - DISCL_EXCLUSIVE | - DISCL_BACKGROUND); - if (FAILED(result)) { - SetDIerror("IDirectInputDevice2::SetCooperativeLevel", result); - return (-1); - } - - /* Use the extended data structure: DIJOYSTATE2. */ - result = - IDirectInputDevice2_SetDataFormat(joystick->hwdata->InputDevice, - &c_dfDIJoystick2); - if (FAILED(result)) { - SetDIerror("IDirectInputDevice2::SetDataFormat", result); - return (-1); - } - - /* Get device capabilities */ - result = - IDirectInputDevice2_GetCapabilities(joystick->hwdata->InputDevice, - &joystick->hwdata->Capabilities); - - if (FAILED(result)) { - SetDIerror("IDirectInputDevice2::GetCapabilities", result); - return (-1); - } - - /* Force capable? */ - if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) { - - result = IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice); - - if (FAILED(result)) { - SetDIerror("IDirectInputDevice2::Acquire", result); - return (-1); - } - - /* reset all accuators. */ - result = - IDirectInputDevice2_SendForceFeedbackCommand(joystick->hwdata-> - InputDevice, - DISFFC_RESET); - - /* Not necessarily supported, ignore if not supported. - if (FAILED(result)) { - SetDIerror("IDirectInputDevice2::SendForceFeedbackCommand", - result); - return (-1); - } - */ - - result = IDirectInputDevice2_Unacquire(joystick->hwdata->InputDevice); + if ( joystickdevice->bXInputDevice ) + { + XINPUT_CAPABILITIES capabilities; + Uint8 userId = 0; + DWORD result; + JoyStick_DeviceData *joysticklist = SYS_Joystick; + // scan the opened joysticks and pick the next free xinput userid for this one + for( ; joysticklist; joysticklist = joysticklist->pNext) + { + if ( joysticklist->bXInputDevice && joysticklist->XInputUserId == userId ) + userId++; + } - if (FAILED(result)) { - SetDIerror("IDirectInputDevice2::Unacquire", result); - return (-1); - } + if ( XINPUTGETCAPABILITIES ) + { + result = XINPUTGETCAPABILITIES( userId, XINPUT_FLAG_GAMEPAD, &capabilities ); + if ( result == ERROR_SUCCESS ) + { + SDL_bool bIsSupported = SDL_FALSE; + // Current version of XInput mistakenly returns 0 as the Type. Ignore it and ensure the subtype is a gamepad. + bIsSupported = ( capabilities.SubType == XINPUT_DEVSUBTYPE_GAMEPAD ); + + if ( !bIsSupported ) + { + joystickdevice->bXInputDevice = SDL_FALSE; + } + else + { + // valid + joystick->hwdata->bXInputDevice = SDL_TRUE; + SDL_memset( joystick->hwdata->XInputState, 0x0, sizeof(joystick->hwdata->XInputState) ); + joystickdevice->XInputUserId = userId; + joystick->hwdata->userid = userId; + joystick->hwdata->currentXInputSlot = 0; + // The XInput API has a hard coded button/axis mapping, so we just match it + joystick->naxes = 6; + joystick->nbuttons = 15; + joystick->nballs = 0; + joystick->nhats = 0; + } + } + else + { + joystickdevice->bXInputDevice = SDL_FALSE; + } + } + else + { + joystickdevice->bXInputDevice = SDL_FALSE; + } + } - /* Turn on auto-centering for a ForceFeedback device (until told - * otherwise). */ - dipdw.diph.dwObj = 0; - dipdw.diph.dwHow = DIPH_DEVICE; - dipdw.dwData = DIPROPAUTOCENTER_ON; + if ( joystickdevice->bXInputDevice == SDL_FALSE ) + { + joystick->hwdata->bXInputDevice = SDL_FALSE; + + result = + IDirectInput8_CreateDevice(dinput, + &(joystickdevice->dxdevice.guidInstance), &device, NULL); + if (FAILED(result)) { + SetDIerror("IDirectInput::CreateDevice", result); + return (-1); + } - result = - IDirectInputDevice2_SetProperty(joystick->hwdata->InputDevice, - DIPROP_AUTOCENTER, &dipdw.diph); + /* Now get the IDirectInputDevice2 interface, instead. */ + result = IDirectInputDevice8_QueryInterface(device, + &IID_IDirectInputDevice8, + (LPVOID *) & joystick-> + hwdata->InputDevice); + /* We are done with this object. Use the stored one from now on. */ + IDirectInputDevice8_Release(device); + + if (FAILED(result)) { + SetDIerror("IDirectInputDevice::QueryInterface", result); + return (-1); + } - /* Not necessarily supported, ignore if not supported. - if (FAILED(result)) { - SetDIerror("IDirectInputDevice2::SetProperty", result); - return (-1); - } - */ - } + /* Aquire shared access. Exclusive access is required for forces, + * though. */ + result = + IDirectInputDevice8_SetCooperativeLevel(joystick->hwdata-> + InputDevice, SDL_HelperWindow, + DISCL_NONEXCLUSIVE | + DISCL_BACKGROUND); + if (FAILED(result)) { + SetDIerror("IDirectInputDevice2::SetCooperativeLevel", result); + return (-1); + } - /* What buttons and axes does it have? */ - IDirectInputDevice2_EnumObjects(joystick->hwdata->InputDevice, - EnumDevObjectsCallback, joystick, - DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV); + /* Use the extended data structure: DIJOYSTATE2. */ + result = + IDirectInputDevice8_SetDataFormat(joystick->hwdata->InputDevice, + &c_dfDIJoystick2); + if (FAILED(result)) { + SetDIerror("IDirectInputDevice2::SetDataFormat", result); + return (-1); + } - /* Reorder the input objects. Some devices do not report the X axis as - * the first axis, for example. */ - SortDevObjects(joystick); + /* Get device capabilities */ + result = + IDirectInputDevice8_GetCapabilities(joystick->hwdata->InputDevice, + &joystick->hwdata->Capabilities); - dipdw.diph.dwObj = 0; - dipdw.diph.dwHow = DIPH_DEVICE; - dipdw.dwData = INPUT_QSIZE; + if (FAILED(result)) { + SetDIerror("IDirectInputDevice2::GetCapabilities", result); + return (-1); + } - /* Set the buffer size */ - result = - IDirectInputDevice2_SetProperty(joystick->hwdata->InputDevice, - DIPROP_BUFFERSIZE, &dipdw.diph); - - if (result == DI_POLLEDDEVICE) { - /* This device doesn't support buffering, so we're forced - * to use less reliable polling. */ - joystick->hwdata->buffered = 0; - } else if (FAILED(result)) { - SetDIerror("IDirectInputDevice2::SetProperty", result); - return (-1); - } + /* Force capable? */ + if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) { + + result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice); + + if (FAILED(result)) { + SetDIerror("IDirectInputDevice2::Acquire", result); + return (-1); + } + + /* reset all accuators. */ + result = + IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata-> + InputDevice, + DISFFC_RESET); + + /* Not necessarily supported, ignore if not supported. + if (FAILED(result)) { + SetDIerror("IDirectInputDevice2::SendForceFeedbackCommand", + result); + return (-1); + } + */ + + result = IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice); + + if (FAILED(result)) { + SetDIerror("IDirectInputDevice2::Unacquire", result); + return (-1); + } + + /* Turn on auto-centering for a ForceFeedback device (until told + * otherwise). */ + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = DIPROPAUTOCENTER_ON; + + result = + IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice, + DIPROP_AUTOCENTER, &dipdw.diph); + + /* Not necessarily supported, ignore if not supported. + if (FAILED(result)) { + SetDIerror("IDirectInputDevice2::SetProperty", result); + return (-1); + } + */ + } + /* What buttons and axes does it have? */ + IDirectInputDevice8_EnumObjects(joystick->hwdata->InputDevice, + EnumDevObjectsCallback, joystick, + DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV); + + /* Reorder the input objects. Some devices do not report the X axis as + * the first axis, for example. */ + SortDevObjects(joystick); + + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = INPUT_QSIZE; + + /* Set the buffer size */ + result = + IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice, + DIPROP_BUFFERSIZE, &dipdw.diph); + + if (result == DI_POLLEDDEVICE) { + /* This device doesn't support buffering, so we're forced + * to use less reliable polling. */ + joystick->hwdata->buffered = 0; + } else if (FAILED(result)) { + SetDIerror("IDirectInputDevice2::SetProperty", result); + return (-1); + } + } return (0); } @@ -564,15 +963,15 @@ EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef) HRESULT result; input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs]; - in->ofs = dev->dwOfs; - if (dev->dwType & DIDFT_BUTTON) { in->type = BUTTON; in->num = joystick->nbuttons; + in->ofs = DIJOFS_BUTTON( in->num ); joystick->nbuttons++; } else if (dev->dwType & DIDFT_POV) { in->type = HAT; in->num = joystick->nhats; + in->ofs = DIJOFS_POV( in->num ); joystick->nhats++; } else if (dev->dwType & DIDFT_AXIS) { DIPROPRANGE diprg; @@ -580,16 +979,38 @@ EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef) in->type = AXIS; in->num = joystick->naxes; + // work our the axis this guy maps too, thanks for the code icculus! + if ( !SDL_memcmp( &dev->guidType, &GUID_XAxis, sizeof(dev->guidType) ) ) + in->ofs = DIJOFS_X; + else if ( !SDL_memcmp( &dev->guidType, &GUID_YAxis, sizeof(dev->guidType) ) ) + in->ofs = DIJOFS_Y; + else if ( !SDL_memcmp( &dev->guidType, &GUID_ZAxis, sizeof(dev->guidType) ) ) + in->ofs = DIJOFS_Z; + else if ( !SDL_memcmp( &dev->guidType, &GUID_RxAxis, sizeof(dev->guidType) ) ) + in->ofs = DIJOFS_RX; + else if ( !SDL_memcmp( &dev->guidType, &GUID_RyAxis, sizeof(dev->guidType) ) ) + in->ofs = DIJOFS_RY; + else if ( !SDL_memcmp( &dev->guidType, &GUID_RzAxis, sizeof(dev->guidType) ) ) + in->ofs = DIJOFS_RZ; + else if ( !SDL_memcmp( &dev->guidType, &GUID_Slider, sizeof(dev->guidType) ) ) + { + in->ofs = DIJOFS_SLIDER( joystick->hwdata->NumSliders ); + ++joystick->hwdata->NumSliders; + } + else + { + return DIENUM_CONTINUE; // not an axis we can grok + } diprg.diph.dwSize = sizeof(diprg); diprg.diph.dwHeaderSize = sizeof(diprg.diph); - diprg.diph.dwObj = dev->dwOfs; - diprg.diph.dwHow = DIPH_BYOFFSET; + diprg.diph.dwObj = dev->dwType; + diprg.diph.dwHow = DIPH_BYID; diprg.lMin = AXIS_MIN; diprg.lMax = AXIS_MAX; result = - IDirectInputDevice2_SetProperty(joystick->hwdata->InputDevice, + IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice, DIPROP_RANGE, &diprg.diph); if (FAILED(result)) { return DIENUM_CONTINUE; /* don't use this axis */ @@ -598,11 +1019,11 @@ EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef) /* Set dead zone to 0. */ dilong.diph.dwSize = sizeof(dilong); dilong.diph.dwHeaderSize = sizeof(dilong.diph); - dilong.diph.dwObj = dev->dwOfs; - dilong.diph.dwHow = DIPH_BYOFFSET; + dilong.diph.dwObj = dev->dwType; + dilong.diph.dwHow = DIPH_BYID; dilong.dwData = 0; result = - IDirectInputDevice2_SetProperty(joystick->hwdata->InputDevice, + IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice, DIPROP_DEADZONE, &dilong.diph); if (FAILED(result)) { return DIENUM_CONTINUE; /* don't use this axis */ @@ -636,15 +1057,22 @@ SDL_SYS_JoystickUpdate_Polled(SDL_Joystick * joystick) int i; result = - IDirectInputDevice2_GetDeviceState(joystick->hwdata->InputDevice, + IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice, sizeof(DIJOYSTATE2), &state); if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) { - IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice); + IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice); result = - IDirectInputDevice2_GetDeviceState(joystick->hwdata->InputDevice, + IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice, sizeof(DIJOYSTATE2), &state); } + if ( result != DI_OK ) + { + joystick->hwdata->send_remove_event = 1; + joystick->hwdata->removed = 1; + return; + } + /* Set each known axis, button and POV. */ for (i = 0; i < joystick->hwdata->NumInputs; ++i) { const input_t *in = &joystick->hwdata->Inputs[i]; @@ -717,20 +1145,24 @@ SDL_SYS_JoystickUpdate_Buffered(SDL_Joystick * joystick) numevents = INPUT_QSIZE; result = - IDirectInputDevice2_GetDeviceData(joystick->hwdata->InputDevice, + IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice, sizeof(DIDEVICEOBJECTDATA), evtbuf, &numevents, 0); if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) { - IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice); + IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice); result = - IDirectInputDevice2_GetDeviceData(joystick->hwdata->InputDevice, + IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice, sizeof(DIDEVICEOBJECTDATA), evtbuf, &numevents, 0); } /* Handle the events or punt */ if (FAILED(result)) + { + joystick->hwdata->send_remove_event = 1; + joystick->hwdata->removed = 1; return; + } for (i = 0; i < (int) numevents; ++i) { int j; @@ -763,6 +1195,82 @@ SDL_SYS_JoystickUpdate_Buffered(SDL_Joystick * joystick) } +/* Function to return > 0 if a bit array of buttons differs after applying a mask +*/ +int ButtonChanged( int ButtonsNow, int ButtonsPrev, int ButtonMask ) +{ + return ( ButtonsNow & ButtonMask ) != ( ButtonsPrev & ButtonMask ); +} + +/* Function to update the state of a XInput style joystick. +*/ +void +SDL_SYS_JoystickUpdate_XInput(SDL_Joystick * joystick) +{ + HRESULT result; + + if ( !XINPUTGETSTATE ) + return; + + result = XINPUTGETSTATE( joystick->hwdata->userid, &joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot] ); + if ( result == ERROR_DEVICE_NOT_CONNECTED ) + { + joystick->hwdata->send_remove_event = 1; + joystick->hwdata->removed = 1; + return; + } + + // only fire events if the data changed from last time + if ( joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot].dwPacketNumber != 0 + && joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot].dwPacketNumber != joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot^1].dwPacketNumber ) + { + XINPUT_STATE_EX *pXInputState = &joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot]; + XINPUT_STATE_EX *pXInputStatePrev = &joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot ^ 1]; + + SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX ); + SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-1*pXInputState->Gamepad.sThumbLY-1) ); + SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX ); + SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-1*pXInputState->Gamepad.sThumbRY-1) ); + SDL_PrivateJoystickAxis(joystick, 4, (Sint16)((int)pXInputState->Gamepad.bLeftTrigger*32767/255) ); + SDL_PrivateJoystickAxis(joystick, 5, (Sint16)((int)pXInputState->Gamepad.bRightTrigger*32767/255) ); + + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP ) ) + SDL_PrivateJoystickButton(joystick, 0, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN ) ) + SDL_PrivateJoystickButton(joystick, 1, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT ) ) + SDL_PrivateJoystickButton(joystick, 2, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ) ) + SDL_PrivateJoystickButton(joystick, 3, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_START ) ) + SDL_PrivateJoystickButton(joystick, 4, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_START ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_BACK ) ) + SDL_PrivateJoystickButton(joystick, 5, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_BACK ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB ) ) + SDL_PrivateJoystickButton(joystick, 6, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB ) ) + SDL_PrivateJoystickButton(joystick, 7, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ) ) + SDL_PrivateJoystickButton(joystick, 8, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER ) ) + SDL_PrivateJoystickButton(joystick, 9, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_A ) ) + SDL_PrivateJoystickButton(joystick, 10, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_A ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_B ) ) + SDL_PrivateJoystickButton(joystick, 11, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_B ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_X ) ) + SDL_PrivateJoystickButton(joystick, 12, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_X ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_Y ) ) + SDL_PrivateJoystickButton(joystick, 13, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_Y ? SDL_PRESSED : SDL_RELEASED ); + if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, 0x400 ) ) + SDL_PrivateJoystickButton(joystick, 14, pXInputState->Gamepad.wButtons & 0x400 ? SDL_PRESSED : SDL_RELEASED ); // 0x400 is the undocumented code for the guide button + + joystick->hwdata->currentXInputSlot ^= 1; + + } +} + + static Uint8 TranslatePOV(DWORD value) { @@ -823,46 +1331,97 @@ SDL_SYS_JoystickUpdate(SDL_Joystick * joystick) { HRESULT result; - result = IDirectInputDevice2_Poll(joystick->hwdata->InputDevice); - if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) { - IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice); - IDirectInputDevice2_Poll(joystick->hwdata->InputDevice); - } + if ( joystick->closed || !joystick->hwdata ) + return; - if (joystick->hwdata->buffered) - SDL_SYS_JoystickUpdate_Buffered(joystick); - else - SDL_SYS_JoystickUpdate_Polled(joystick); + if (joystick->hwdata->bXInputDevice) + { + SDL_SYS_JoystickUpdate_XInput(joystick); + } + else + { + result = IDirectInputDevice8_Poll(joystick->hwdata->InputDevice); + if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) { + IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice); + IDirectInputDevice8_Poll(joystick->hwdata->InputDevice); + } + + if (joystick->hwdata->buffered) + SDL_SYS_JoystickUpdate_Buffered(joystick); + else + SDL_SYS_JoystickUpdate_Polled(joystick); + } + + if ( joystick->hwdata->removed ) + { + joystick->closed = 1; + joystick->uncentered = 1; + } } /* Function to close a joystick after use */ void SDL_SYS_JoystickClose(SDL_Joystick * joystick) { - IDirectInputDevice2_Unacquire(joystick->hwdata->InputDevice); - IDirectInputDevice2_Release(joystick->hwdata->InputDevice); + if ( joystick->hwdata->bXInputDevice ) + { + JoyStick_DeviceData *joysticklist = SYS_Joystick; + // scan the opened joysticks and clear the userid for this instance + for( ; joysticklist; joysticklist = joysticklist->pNext) + { + if ( joysticklist->bXInputDevice && joysticklist->nInstanceID == joystick->instance_id ) + { + joysticklist->XInputUserId = INVALID_XINPUT_USERID; + } + } + + } + else + { + IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice); + IDirectInputDevice8_Release(joystick->hwdata->InputDevice); + } if (joystick->hwdata != NULL) { /* free system specific hardware data */ SDL_free(joystick->hwdata); } + + joystick->closed = 1; } /* Function to perform any system-specific joystick related cleanup */ void SDL_SYS_JoystickQuit(void) { - int i; + JoyStick_DeviceData *device = SYS_Joystick; - for (i = 0; i < SDL_arraysize(SYS_JoystickNames); ++i) { - if (SYS_JoystickNames[i]) { - SDL_free(SYS_JoystickNames[i]); - SYS_JoystickNames[i] = NULL; - } - } + while ( device ) + { + JoyStick_DeviceData *device_next = device->pNext; + SDL_free(device->joystickname); + SDL_free(device); + device = device_next; + } + SYS_Joystick = NULL; + + if ( s_threadJoystick ) + { + SDL_LockMutex( s_mutexJoyStickEnum ); + s_bJoystickThreadQuit = SDL_TRUE; + SDL_CondBroadcast( s_condJoystickThread ); // signal the joystick thread to quit + SDL_UnlockMutex( s_mutexJoyStickEnum ); + SDL_WaitThread( s_threadJoystick, NULL ); // wait for it to bugger off + + SDL_DestroyMutex( s_mutexJoyStickEnum ); + SDL_DestroyCond( s_condJoystickThread ); + s_condJoystickThread= NULL; + s_mutexJoyStickEnum = NULL; + s_threadJoystick = NULL; + } if (dinput != NULL) { - IDirectInput_Release(dinput); + IDirectInput8_Release(dinput); dinput = NULL; } @@ -870,6 +1429,254 @@ SDL_SYS_JoystickQuit(void) WIN_CoUninitialize(); coinitialized = SDL_FALSE; } + + if ( s_pKnownJoystickGUIDs ) + { + SDL_free( s_pKnownJoystickGUIDs ); + s_pKnownJoystickGUIDs = NULL; + } + + if ( s_pXInputDLL ) + { + FreeLibrary( s_pXInputDLL ); + s_pXInputDLL = NULL; + } +} + + +/* Function to perform the mapping between current device instance and this joysticks instance id */ +SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index) +{ + JoyStick_DeviceData *device = SYS_Joystick; + int index; + + for (index = device_index; index > 0; index--) + device = device->pNext; + + return device->nInstanceID; +} + +/* return true if this joystick is plugged in right now */ +int SDL_SYS_JoystickAttached( SDL_Joystick * joystick ) +{ + return joystick->closed == 0 && joystick->hwdata->removed == 0; +} + + +/* return the number of joysticks that are connected right now */ +int SDL_SYS_NumJoysticks() +{ + int nJoysticks = 0; + JoyStick_DeviceData *device = SYS_Joystick; + while ( device ) + { + nJoysticks++; + device = device->pNext; + } + + return nJoysticks; +} + +static int s_iNewGUID = 0; + +/* helper function for direct input, gets called for each connected joystick */ +static BOOL CALLBACK + EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext) +{ + JoyStick_DeviceData *pNewJoystick; + SDL_bool bXInputDevice; + pNewJoystick = *(JoyStick_DeviceData **)pContext; + while ( pNewJoystick ) + { + if ( !SDL_memcmp( &pNewJoystick->dxdevice.guidInstance, &pdidInstance->guidInstance, sizeof(pNewJoystick->dxdevice.guidInstance) ) ) + { + if ( SYS_Joystick ) + { + pNewJoystick->pNext = SYS_Joystick; + } + SYS_Joystick = pNewJoystick; + /* if we are replacing the front of the list then update it */ + if ( pNewJoystick == *(JoyStick_DeviceData **)pContext ) + { + *(JoyStick_DeviceData **)pContext = pNewJoystick->pNext; + } + + s_pKnownJoystickGUIDs[ s_iNewGUID ] = pdidInstance->guidInstance; + s_iNewGUID++; + if ( s_iNewGUID < MAX_JOYSTICKS ) + return DIENUM_CONTINUE; // already have this joystick loaded, just keep going + else + return DIENUM_STOP; + } + + pNewJoystick = pNewJoystick->pNext; + } + + s_bDeviceAdded = SDL_TRUE; + + bXInputDevice = IsXInputDevice( &pdidInstance->guidProduct ); + + pNewJoystick = (JoyStick_DeviceData *)SDL_malloc( sizeof(JoyStick_DeviceData) ); + + if ( bXInputDevice ) + { + SDL_memset(&(pNewJoystick->dxdevice), 0x0, + sizeof(DIDEVICEINSTANCE)); + pNewJoystick->bXInputDevice = 1; + pNewJoystick->XInputUserId = INVALID_XINPUT_USERID; + } + else + { + pNewJoystick->bXInputDevice = 0; + SDL_memcpy(&(pNewJoystick->dxdevice), pdidInstance, + sizeof(DIDEVICEINSTANCE)); + } + pNewJoystick->joystickname = WIN_StringToUTF8(pdidInstance->tszProductName); + pNewJoystick->send_add_event = 1; + pNewJoystick->nInstanceID = ++s_nInstanceID; + SDL_memcpy( &pNewJoystick->guid, &pdidInstance->guidProduct, sizeof(pNewJoystick->guid) ); + pNewJoystick->pNext = NULL; + + if ( SYS_Joystick ) + { + pNewJoystick->pNext = SYS_Joystick; + } + SYS_Joystick = pNewJoystick; + + s_pKnownJoystickGUIDs[ s_iNewGUID ] = pdidInstance->guidInstance; + s_iNewGUID++; + + if ( s_iNewGUID < MAX_JOYSTICKS ) + return DIENUM_CONTINUE; // already have this joystick loaded, just keep going + else + return DIENUM_STOP; +} + + +/* detect any new joysticks being inserted into the system */ +void SDL_SYS_JoystickDetect() +{ + HRESULT result; + JoyStick_DeviceData *pCurList = NULL; + /* only enum the devices if the joystick thread told us something changed */ + if ( s_bDeviceAdded || s_bDeviceRemoved ) + { + s_bDeviceAdded = SDL_FALSE; + s_bDeviceRemoved = SDL_FALSE; + + pCurList = SYS_Joystick; + SYS_Joystick = NULL; + s_iNewGUID = 0; + SDL_mutexP( s_mutexJoyStickEnum ); + + if ( !s_pKnownJoystickGUIDs ) + s_pKnownJoystickGUIDs = SDL_malloc( sizeof(GUID)*MAX_JOYSTICKS ); + + SDL_memset( s_pKnownJoystickGUIDs, 0x0, sizeof(GUID)*MAX_JOYSTICKS ); + + /* Look for joysticks, wheels, head trackers, gamepads, etc.. */ + result = IDirectInput8_EnumDevices(dinput, + DI8DEVCLASS_GAMECTRL, + EnumJoysticksCallback, + &pCurList, DIEDFL_ATTACHEDONLY); + + SDL_mutexV( s_mutexJoyStickEnum ); + } + + if ( pCurList ) + { + while ( pCurList ) + { + JoyStick_DeviceData *pListNext = NULL; +#if !SDL_EVENTS_DISABLED + SDL_Event event; + event.type = SDL_JOYDEVICEREMOVED; + + if (SDL_GetEventState(event.type) == SDL_ENABLE) { + event.jdevice.which = pCurList->nInstanceID; + if ((SDL_EventOK == NULL) + || (*SDL_EventOK) (SDL_EventOKParam, &event)) { + SDL_PushEvent(&event); + } + } +#endif // !SDL_EVENTS_DISABLED + + pListNext = pCurList->pNext; + SDL_free(pCurList->joystickname); + SDL_free( pCurList ); + pCurList = pListNext; + } + + } + + if ( s_bDeviceAdded ) + { + JoyStick_DeviceData *pNewJoystick; + int device_index = 0; + s_bDeviceAdded = SDL_FALSE; + pNewJoystick = SYS_Joystick; + while ( pNewJoystick ) + { + if ( pNewJoystick->send_add_event ) + { +#if !SDL_EVENTS_DISABLED + SDL_Event event; + event.type = SDL_JOYDEVICEADDED; + + if (SDL_GetEventState(event.type) == SDL_ENABLE) { + event.jdevice.which = device_index; + if ((SDL_EventOK == NULL) + || (*SDL_EventOK) (SDL_EventOKParam, &event)) { + SDL_PushEvent(&event); + } + } +#endif /* !SDL_EVENTS_DISABLED */ + pNewJoystick->send_add_event = 0; + } + device_index++; + pNewJoystick = pNewJoystick->pNext; + } + } +} + + +/* we need to poll if we have pending hotplug device changes or connected devices */ +int SDL_SYS_JoystickNeedsPolling() +{ + /* we have a new device or one was pulled, we need to think this frame please */ + if ( s_bDeviceAdded || s_bDeviceRemoved ) + return 1; + + return 0; +} + +/* return the stable device guid for this device index */ +JoystickGUID SDL_SYS_PrivateJoystickGetDeviceID( int device_index ) +{ + JoyStick_DeviceData *device = SYS_Joystick; + int index; + + for (index = device_index; index > 0; index--) + device = device->pNext; + + return device->guid; +} + +/* return 1 if this device is using XInput */ +int SDL_SYS_IsXInputDeviceIndex( int device_index ) +{ + JoyStick_DeviceData *device = SYS_Joystick; + int index; + + for (index = device_index; index > 0; index--) + device = device->pNext; + + return device->bXInputDevice; +} + +JoystickGUID SDL_SYS_PrivateJoystickGetGUID(SDL_Joystick * joystick) +{ + return joystick->hwdata->guid; } #endif /* SDL_JOYSTICK_DINPUT */ diff --git a/src/joystick/windows/SDL_dxjoystick_c.h b/src/joystick/windows/SDL_dxjoystick_c.h index 1aa993486..391c2628f 100644 --- a/src/joystick/windows/SDL_dxjoystick_c.h +++ b/src/joystick/windows/SDL_dxjoystick_c.h @@ -34,9 +34,14 @@ #include "../../core/windows/SDL_windows.h" -#define DIRECTINPUT_VERSION 0x0700 /* Need version 7 for force feedback. */ +#define DIRECTINPUT_VERSION 0x0800 /* Need version 7 for force feedback. Need verison 8 so IDirectInput8_EnumDevices doesn't leak like a sieve... */ #include - +#define COBJMACROS +#include +#include +#include +#include +#include #define MAX_INPUTS 256 /* each joystick can have up to 256 inputs */ @@ -57,15 +62,42 @@ typedef struct input_t Uint8 num; } input_t; +/* typedef's for XInput structs we use */ +typedef struct +{ + WORD wButtons; + BYTE bLeftTrigger; + BYTE bRightTrigger; + SHORT sThumbLX; + SHORT sThumbLY; + SHORT sThumbRX; + SHORT sThumbRY; + DWORD dwPaddingReserved; +} XINPUT_GAMEPAD_EX; + +typedef struct +{ + DWORD dwPacketNumber; + XINPUT_GAMEPAD_EX Gamepad; +} XINPUT_STATE_EX; + /* The private structure used to keep track of a joystick */ struct joystick_hwdata { LPDIRECTINPUTDEVICE2 InputDevice; DIDEVCAPS Capabilities; int buffered; + JoystickGUID guid; input_t Inputs[MAX_INPUTS]; int NumInputs; + int NumSliders; + Uint8 removed; + Uint8 send_remove_event; + Uint8 bXInputDevice; // 1 if this device supports using the xinput API rather than DirectInput + Uint8 userid; // XInput userid index for this joystick + Uint8 currentXInputSlot; // the current position to write to in XInputState below, used so we can compare old and new values + XINPUT_STATE_EX XInputState[2]; }; #endif /* SDL_JOYSTICK_DINPUT_H */ diff --git a/test/Makefile.in b/test/Makefile.in index 7e33d3edf..3a04c8e58 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -14,6 +14,7 @@ TARGETS = \ testdraw2$(EXE) \ testerror$(EXE) \ testfile$(EXE) \ + testgamecontroller$(EXE) \ testgesture$(EXE) \ testgl2$(EXE) \ testgles$(EXE) \ @@ -87,6 +88,9 @@ testerror$(EXE): $(srcdir)/testerror.c testfile$(EXE): $(srcdir)/testfile.c $(CC) -o $@ $^ $(CFLAGS) $(LIBS) +testgamecontroller$(EXE): $(srcdir)/testgamecontroller.c + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + testgesture$(EXE): $(srcdir)/testgesture.c $(CC) -o $@ $^ $(CFLAGS) $(LIBS) @MATHLIB@ diff --git a/test/testgamecontroller.c b/test/testgamecontroller.c new file mode 100644 index 000000000..2afa9baea --- /dev/null +++ b/test/testgamecontroller.c @@ -0,0 +1,205 @@ +/* + Copyright (C) 1997-2011 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. +*/ + +/* Simple program to test the SDL game controller routines */ + +#include +#include +#include + +#include "SDL.h" + +#ifdef __IPHONEOS__ +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 480 +#else +#define SCREEN_WIDTH 640 +#define SCREEN_HEIGHT 480 +#endif + +#define MAX_NUM_AXES 6 +#define MAX_NUM_HATS 2 + +static SDL_bool s_ForceQuit = SDL_FALSE; + +static void +DrawRect(SDL_Renderer *r, const int x, const int y, const int w, const int h) +{ + const SDL_Rect area = { x, y, w, h }; + SDL_RenderFillRect(r, &area); +} + +void +WatchGameController(SDL_GameController * gamecontroller) +{ + SDL_Window *window = NULL; + SDL_Renderer *screen = NULL; + const char *name = NULL; + int done = 0; + SDL_Event event; + int i; + + /* Create a window to display controller axis position */ + window = SDL_CreateWindow("Game Controller Test", SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, + SCREEN_HEIGHT, SDL_WINDOW_SHOWN); + if (window == NULL) { + fprintf(stderr, "Couldn't create window: %s\n", SDL_GetError()); + return; + } + + screen = SDL_CreateRenderer(window, -1, 0); + if (screen == NULL) { + fprintf(stderr, "Couldn't create renderer: %s\n", SDL_GetError()); + SDL_DestroyWindow(window); + return; + } + + SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE); + SDL_RenderClear(screen); + SDL_RenderPresent(screen); + SDL_RaiseWindow(window); + + /* Print info about the controller we are watching */ + name = SDL_GameControllerName(gamecontroller); + printf("Watching controller %s\n", name ? name : "Unknown Controller"); + + /* Loop, getting controller events! */ + while (!done) { + /* blank screen, set up for drawing this frame. */ + SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE); + SDL_RenderClear(screen); + + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_CONTROLLERAXISMOTION: + printf("Controller %d axis %d value: %d\n", + event.caxis.which, + event.caxis.axis, event.caxis.value); + break; + case SDL_CONTROLLERBUTTONDOWN: + printf("Controller %d button %d down\n", + event.cbutton.which, event.cbutton.button); + break; + case SDL_CONTROLLERBUTTONUP: + printf("Controller %d button %d up\n", + event.cbutton.which, event.cbutton.button); + break; + case SDL_KEYDOWN: + if (event.key.keysym.sym != SDLK_ESCAPE) { + break; + } + /* Fall through to signal quit */ + case SDL_QUIT: + done = 1; + s_ForceQuit = SDL_TRUE; + break; + default: + break; + } + } + /* Update visual controller state */ + SDL_SetRenderDrawColor(screen, 0x00, 0xFF, 0x00, SDL_ALPHA_OPAQUE); + for (i = 0; i (SCREEN_WIDTH - 16)) { + x = SCREEN_WIDTH - 16; + } + y = (((int) SDL_GameControllerGetAxis(gamecontroller, i * 2 + 1)) + 32768); + y *= SCREEN_HEIGHT; + y /= 65535; + if (y < 0) { + y = 0; + } else if (y > (SCREEN_HEIGHT - 16)) { + y = SCREEN_HEIGHT - 16; + } + + DrawRect(screen, x, y, 16, 16); + } + + SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0xFF, SDL_ALPHA_OPAQUE); + + SDL_RenderPresent(screen); + + if ( !done ) + done = SDL_GameControllerGetAttached( gamecontroller ) == 0; + } + + SDL_DestroyRenderer(screen); + SDL_DestroyWindow(window); +} + +int +main(int argc, char *argv[]) +{ + const char *name; + int i; + int nController = 0; + SDL_GameController *gamecontroller; + + SDL_SetHint( SDL_HINT_GAMECONTROLLERCONFIG, "341a3608000000000000504944564944,Aferglow PS3 Controller,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7" ); + /* Initialize SDL (Note: video is required to start event loop) */ + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER ) < 0) { + fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError()); + exit(1); + } + + /* Print information about the controller */ + for (i = 0; i < SDL_NumJoysticks(); ++i) { + if ( SDL_IsGameController(i) ) + { + nController++; + name = SDL_GameControllerNameForIndex(i); + printf("Game Controller %d: %s\n", i, name ? name : "Unknown Controller"); + } + } + printf("There are %d game controllers attached\n", nController); + + if (argv[1]) { + int nreportederror = 0; + SDL_Event event; + gamecontroller = SDL_GameControllerOpen(atoi(argv[1])); + while ( s_ForceQuit == SDL_FALSE ) { + if (gamecontroller == NULL) { + if ( nreportederror == 0 ) { + printf("Couldn't open joystick %d: %s\n", atoi(argv[1]), SDL_GetError()); + nreportederror = 1; + } + } else { + nreportederror = 0; + WatchGameController(gamecontroller); + SDL_GameControllerClose(gamecontroller); + } + + gamecontroller = NULL; + SDL_WaitEvent( &event ); + if ( event.type == SDL_JOYDEVICEADDED ) + gamecontroller = SDL_GameControllerOpen(atoi(argv[1])); + } + } + SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER ); + + return (0); +} diff --git a/test/testjoystick.c b/test/testjoystick.c index a20c3dd1f..71c35baa4 100644 --- a/test/testjoystick.c +++ b/test/testjoystick.c @@ -17,10 +17,14 @@ #include #include "SDL.h" -#include "common.h" -static CommonState *state; -static SDL_BlendMode blendMode = SDL_BLENDMODE_NONE; +#ifdef __IPHONEOS__ +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 480 +#else +#define SCREEN_WIDTH 640 +#define SCREEN_HEIGHT 480 +#endif #define MAX_NUM_AXES 6 #define MAX_NUM_HATS 2 @@ -37,16 +41,35 @@ WatchJoystick(SDL_Joystick * joystick) { SDL_Window *window = NULL; SDL_Renderer *screen = NULL; - SDL_Rect viewport; - SDL_Event event; - const char *name = NULL; int done = 0; + SDL_Event event; int i; + /* Create a window to display joystick axis position */ + window = SDL_CreateWindow("Joystick Test", SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, + SCREEN_HEIGHT, SDL_WINDOW_SHOWN); + if (window == NULL) { + fprintf(stderr, "Couldn't create window: %s\n", SDL_GetError()); + return; + } + + screen = SDL_CreateRenderer(window, -1, 0); + if (screen == NULL) { + fprintf(stderr, "Couldn't create renderer: %s\n", SDL_GetError()); + SDL_DestroyWindow(window); + return; + } + + SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE); + SDL_RenderClear(screen); + SDL_RenderPresent(screen); + SDL_RaiseWindow(window); + /* Print info about the joystick we are watching */ - name = SDL_JoystickName(SDL_JoystickIndex(joystick)); - printf("Watching joystick %d: (%s)\n", SDL_JoystickIndex(joystick), + name = SDL_JoystickName(joystick); + printf("Watching joystick %d: (%s)\n", SDL_JoystickInstanceID(joystick), name ? name : "Unknown Joystick"); printf("Joystick has %d axes, %d hats, %d balls, and %d buttons\n", SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick), @@ -54,6 +77,10 @@ WatchJoystick(SDL_Joystick * joystick) /* Loop, getting joystick events! */ while (!done) { + /* blank screen, set up for drawing this frame. */ + SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE); + SDL_RenderClear(screen); + while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_JOYAXISMOTION: @@ -101,100 +128,86 @@ WatchJoystick(SDL_Joystick * joystick) break; } } - /* Update visual joystick state */ - for (i = 0; i < state->num_windows; ++i) { - screen = state->renderers[i]; - - /* Erase previous axes */ - SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE); - SDL_RenderClear(screen); - - /* Query the sizes */ - SDL_RenderGetViewport(screen, &viewport); - - SDL_SetRenderDrawColor(screen, 0x00, 0xFF, 0x00, SDL_ALPHA_OPAQUE); - for (i = 0; i < SDL_JoystickNumButtons(joystick); ++i) { - if (SDL_JoystickGetButton(joystick, i) == SDL_PRESSED) { - DrawRect(screen, i * 34, viewport.h - 34, 32, 32); - } + SDL_SetRenderDrawColor(screen, 0x00, 0xFF, 0x00, SDL_ALPHA_OPAQUE); + for (i = 0; i < SDL_JoystickNumButtons(joystick); ++i) { + if (SDL_JoystickGetButton(joystick, i) == SDL_PRESSED) { + DrawRect(screen, i * 34, SCREEN_HEIGHT - 34, 32, 32); } + } - SDL_SetRenderDrawColor(screen, 0xFF, 0x00, 0x00, SDL_ALPHA_OPAQUE); - for (i = 0; i < SDL_JoystickNumAxes(joystick) / 2; ++i) { - /* Draw the X/Y axis */ - int x, y; - x = (((int) SDL_JoystickGetAxis(joystick, i * 2 + 0)) + 32768); - x *= viewport.w ; - x /= 65535; - if (x < 0) { - x = 0; - } else if (x > (viewport.w - 16)) { - x = viewport.w - 16; - } - y = (((int) SDL_JoystickGetAxis(joystick, i * 2 + 1)) + 32768); - y *= viewport.h; - y /= 65535; - if (y < 0) { - y = 0; - } else if (y > (viewport.h - 16)) { - y = viewport.h - 16; - } - - DrawRect(screen, x, y, 16, 16); + SDL_SetRenderDrawColor(screen, 0xFF, 0x00, 0x00, SDL_ALPHA_OPAQUE); + for (i = 0; i < SDL_JoystickNumAxes(joystick) / 2; ++i) { + /* Draw the X/Y axis */ + int x, y; + x = (((int) SDL_JoystickGetAxis(joystick, i * 2 + 0)) + 32768); + x *= SCREEN_WIDTH; + x /= 65535; + if (x < 0) { + x = 0; + } else if (x > (SCREEN_WIDTH - 16)) { + x = SCREEN_WIDTH - 16; + } + y = (((int) SDL_JoystickGetAxis(joystick, i * 2 + 1)) + 32768); + y *= SCREEN_HEIGHT; + y /= 65535; + if (y < 0) { + y = 0; + } else if (y > (SCREEN_HEIGHT - 16)) { + y = SCREEN_HEIGHT - 16; } - SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0xFF, SDL_ALPHA_OPAQUE); - for (i = 0; i < SDL_JoystickNumHats(joystick); ++i) { - /* Derive the new position */ - int x = viewport.w/2; - int y = viewport.h/2; - const Uint8 hat_pos = SDL_JoystickGetHat(joystick, i); - - if (hat_pos & SDL_HAT_UP) { - y = 0; - } else if (hat_pos & SDL_HAT_DOWN) { - y = viewport.h-8; - } + DrawRect(screen, x, y, 16, 16); + } - if (hat_pos & SDL_HAT_LEFT) { - x = 0; - } else if (hat_pos & SDL_HAT_RIGHT) { - x = viewport.w-8; - } + SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0xFF, SDL_ALPHA_OPAQUE); + for (i = 0; i < SDL_JoystickNumHats(joystick); ++i) { + /* Derive the new position */ + int x = SCREEN_WIDTH/2; + int y = SCREEN_HEIGHT/2; + const Uint8 hat_pos = SDL_JoystickGetHat(joystick, i); + + if (hat_pos & SDL_HAT_UP) { + y = 0; + } else if (hat_pos & SDL_HAT_DOWN) { + y = SCREEN_HEIGHT-8; + } - DrawRect(screen, x, y, 8, 8); + if (hat_pos & SDL_HAT_LEFT) { + x = 0; + } else if (hat_pos & SDL_HAT_RIGHT) { + x = SCREEN_WIDTH-8; } - SDL_RenderPresent(screen); + DrawRect(screen, x, y, 8, 8); } + + SDL_RenderPresent(screen); + + done = SDL_JoystickGetAttached( joystick ) == 0; } + + SDL_DestroyRenderer(screen); + SDL_DestroyWindow(window); } int main(int argc, char *argv[]) { const char *name; - int i, joy=-1; + int i; SDL_Joystick *joystick; /* Initialize SDL (Note: video is required to start event loop) */ - if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError()); exit(1); } - /* Initialize test framework */ - state = CommonCreateState(argv, SDL_INIT_VIDEO | SDL_INIT_JOYSTICK); - if (!state) { - fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError()); - return 1; - } - /* Print information about the joysticks */ printf("There are %d joysticks attached\n", SDL_NumJoysticks()); for (i = 0; i < SDL_NumJoysticks(); ++i) { - name = SDL_JoystickName(i); + name = SDL_JoystickNameForIndex(i); printf("Joystick %d: %s\n", i, name ? name : "Unknown Joystick"); joystick = SDL_JoystickOpen(i); if (joystick == NULL) { @@ -205,42 +218,34 @@ main(int argc, char *argv[]) printf(" balls: %d\n", SDL_JoystickNumBalls(joystick)); printf(" hats: %d\n", SDL_JoystickNumHats(joystick)); printf(" buttons: %d\n", SDL_JoystickNumButtons(joystick)); + printf("instance id: %d\n", SDL_JoystickInstanceID(joystick)); SDL_JoystickClose(joystick); } } - for (i = 1; i < argc;) { - int consumed; - - consumed = CommonArg(state, i); - if (consumed == 0) { - consumed = -1; - if (SDL_isdigit(*argv[i])) { - joy = SDL_atoi(argv[i]); - consumed = 1; - } - } - if (consumed < 0) { - return 1; - } - i += consumed; - } - if (!CommonInit(state)) { - return 2; - } - - if (joy > -1) { - joystick = SDL_JoystickOpen(joy); - if (joystick == NULL) { - printf("Couldn't open joystick %d: %s\n", joy, - SDL_GetError()); - } else { - WatchJoystick(joystick); - SDL_JoystickClose(joystick); - } - } - SDL_QuitSubSystem(SDL_INIT_JOYSTICK); - CommonQuit(state); + if (argv[1]) { + int nreportederror = 0; + SDL_Event event; + joystick = SDL_JoystickOpen(atoi(argv[1])); + while ( 1 ) { + if (joystick == NULL) { + if ( nreportederror == 0 ) { + printf("Couldn't open joystick %d: %s\n", atoi(argv[1]), SDL_GetError()); + nreportederror = 1; + } + } else { + nreportederror = 0; + WatchJoystick(joystick); + SDL_JoystickClose(joystick); + } + + joystick = NULL; + SDL_WaitEvent( &event ); + if ( event.type == SDL_JOYDEVICEADDED ) + joystick = SDL_JoystickOpen(atoi(argv[1])); + } + } + SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK); return (0); }