From b79e7f32cc4e0dcf52ba7580416f97602bd91332 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 3 Jul 2014 15:39:55 -0700 Subject: [PATCH] Split the XInput and DirectInput code so Windows RT can use the existing XInput support. --- .../WinRT80_VS2012/SDL-WinRT80.vcxproj | 16 +- .../SDL-WinRT80.vcxproj.filters | 48 +- .../WinRT81_VS2013/SDL-WinRT81.vcxproj | 16 +- .../SDL-WinRT81.vcxproj.filters | 36 +- VisualC/SDL/SDL_VS2008.vcproj | 160 +- VisualC/SDL/SDL_VS2010.vcxproj | 43 +- VisualC/SDL/SDL_VS2012.vcxproj | 43 +- VisualC/SDL/SDL_VS2013.vcxproj | 43 +- configure | 66 +- configure.in | 45 +- include/SDL_config.h.in | 6 + include/SDL_config_windows.h | 6 + include/SDL_config_winrt.h | 6 +- src/audio/directsound/SDL_directsound.h | 2 +- .../directx.h => core/windows/SDL_directx.h} | 19 +- src/core/windows/SDL_xinput.c | 94 + .../windows/SDL_xinput.h} | 70 +- .../{SDL_syshaptic.c => SDL_dinputhaptic.c} | 1045 +++-------- src/haptic/windows/SDL_dinputhaptic_c.h | 47 + src/haptic/windows/SDL_windowshaptic.c | 445 +++++ src/haptic/windows/SDL_windowshaptic_c.h | 88 + src/haptic/windows/SDL_xinputhaptic.c | 491 +++++ src/haptic/windows/SDL_xinputhaptic_c.h | 47 + src/joystick/SDL_gamecontroller.c | 10 - src/joystick/SDL_gamecontrollerdb.h | 17 +- src/joystick/SDL_sysjoystick.h | 9 +- src/joystick/windows/SDL_dinputjoystick.c | 900 +++++++++ .../windows/SDL_dinputjoystick_c.h} | 12 +- src/joystick/windows/SDL_dxjoystick.c | 1661 ----------------- src/joystick/windows/SDL_windowsjoystick.c | 569 ++++++ src/joystick/windows/SDL_windowsjoystick_c.h | 89 + src/joystick/windows/SDL_xinputjoystick.c | 390 ++++ src/joystick/windows/SDL_xinputjoystick_c.h | 33 + src/joystick/winrt/SDL_xinputjoystick.c | 551 ------ 34 files changed, 3967 insertions(+), 3156 deletions(-) rename src/{audio/directsound/directx.h => core/windows/SDL_directx.h} (86%) create mode 100644 src/core/windows/SDL_xinput.c rename src/{joystick/windows/SDL_dxjoystick_c.h => core/windows/SDL_xinput.h} (67%) rename src/haptic/windows/{SDL_syshaptic.c => SDL_dinputhaptic.c} (58%) create mode 100644 src/haptic/windows/SDL_dinputhaptic_c.h create mode 100644 src/haptic/windows/SDL_windowshaptic.c create mode 100644 src/haptic/windows/SDL_windowshaptic_c.h create mode 100644 src/haptic/windows/SDL_xinputhaptic.c create mode 100644 src/haptic/windows/SDL_xinputhaptic_c.h create mode 100644 src/joystick/windows/SDL_dinputjoystick.c rename src/{haptic/windows/SDL_syshaptic_c.h => joystick/windows/SDL_dinputjoystick_c.h} (70%) delete mode 100644 src/joystick/windows/SDL_dxjoystick.c create mode 100644 src/joystick/windows/SDL_windowsjoystick.c create mode 100644 src/joystick/windows/SDL_windowsjoystick_c.h create mode 100644 src/joystick/windows/SDL_xinputjoystick.c create mode 100644 src/joystick/windows/SDL_xinputjoystick_c.h delete mode 100644 src/joystick/winrt/SDL_xinputjoystick.c diff --git a/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj b/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj index 292a02866d004..fbcd94b6b6c28 100644 --- a/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj +++ b/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj @@ -47,6 +47,7 @@ true + true true @@ -93,9 +94,14 @@ + + + - + + + @@ -278,7 +284,9 @@ + + @@ -297,9 +305,15 @@ + + + + + + diff --git a/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj.filters b/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj.filters index a536f33a085a4..aa539d37e160b 100644 --- a/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj.filters +++ b/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj.filters @@ -247,9 +247,6 @@ Source Files - - Source Files - Source Files @@ -298,6 +295,27 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -672,6 +690,30 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + diff --git a/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj b/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj index 1607182ac9aa0..7665cc4739c75 100644 --- a/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj +++ b/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj @@ -86,7 +86,9 @@ + + @@ -105,9 +107,15 @@ + + + + + + @@ -173,6 +181,7 @@ true + true true @@ -219,10 +228,15 @@ + + + - + + + diff --git a/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters b/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters index fa973a05725bc..502f92cf65aa9 100644 --- a/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters +++ b/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters @@ -381,6 +381,24 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + @@ -485,9 +503,6 @@ Source Files - - Source Files - Source Files @@ -683,5 +698,20 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + \ No newline at end of file diff --git a/VisualC/SDL/SDL_VS2008.vcproj b/VisualC/SDL/SDL_VS2008.vcproj index 5abfa6560b45f..53db16dd513c7 100644 --- a/VisualC/SDL/SDL_VS2008.vcproj +++ b/VisualC/SDL/SDL_VS2008.vcproj @@ -371,6 +371,14 @@ + + + + @@ -419,10 +427,6 @@ RelativePath="..\..\include\SDL_egl.h" > - - @@ -499,10 +503,34 @@ RelativePath="..\..\include\SDL_opengl.h" > + + + + + + + + + + + + @@ -559,6 +587,54 @@ RelativePath="..\..\include\SDL_syswm.h" > + + + + + + + + + + + + + + + + + + + + + + + + @@ -583,10 +659,6 @@ RelativePath="..\..\include\SDL_video.h" > - - - - @@ -824,6 +892,22 @@ RelativePath="..\..\src\render\SDL_d3dmath.h" > + + + + + + + + @@ -832,6 +916,10 @@ RelativePath="..\..\src\audio\directsound\SDL_directsound.h" > + + @@ -876,10 +964,6 @@ RelativePath="..\..\src\audio\dummy\SDL_dummyaudio.h" > - - @@ -1156,10 +1240,6 @@ RelativePath="..\..\src\filesystem\windows\SDL_sysfilesystem.c" > - - @@ -1296,6 +1376,22 @@ RelativePath="..\..\src\video\windows\SDL_windowsframebuffer.h" > + + + + + + + + @@ -1376,6 +1472,30 @@ RelativePath="..\..\src\audio\xaudio2\SDL_xaudio2.c" > + + + + + + + + + + + + diff --git a/VisualC/SDL/SDL_VS2010.vcxproj b/VisualC/SDL/SDL_VS2010.vcxproj index dd368bbee840b..ba4137e3f270b 100644 --- a/VisualC/SDL/SDL_VS2010.vcxproj +++ b/VisualC/SDL/SDL_VS2010.vcxproj @@ -219,6 +219,8 @@ + + @@ -235,6 +237,7 @@ + @@ -244,11 +247,18 @@ + + + + + + + @@ -263,23 +273,42 @@ + + + + + + + + + + + + - + + - + + + + + + + @@ -322,7 +351,6 @@ - @@ -348,10 +376,17 @@ + + + + + + + @@ -416,7 +451,6 @@ - @@ -444,7 +478,6 @@ - diff --git a/VisualC/SDL/SDL_VS2012.vcxproj b/VisualC/SDL/SDL_VS2012.vcxproj index 97dedf68c78f3..1a1350890cb8a 100644 --- a/VisualC/SDL/SDL_VS2012.vcxproj +++ b/VisualC/SDL/SDL_VS2012.vcxproj @@ -223,6 +223,8 @@ + + @@ -239,6 +241,7 @@ + @@ -248,11 +251,18 @@ + + + + + + + @@ -267,23 +277,42 @@ + + + + + + + + + + + + - + + - + + + + + + + @@ -326,7 +355,6 @@ - @@ -352,10 +380,17 @@ + + + + + + + @@ -420,7 +455,6 @@ - @@ -448,7 +482,6 @@ - diff --git a/VisualC/SDL/SDL_VS2013.vcxproj b/VisualC/SDL/SDL_VS2013.vcxproj index fa3d3346b0768..1ef810e4978a1 100644 --- a/VisualC/SDL/SDL_VS2013.vcxproj +++ b/VisualC/SDL/SDL_VS2013.vcxproj @@ -223,6 +223,8 @@ + + @@ -239,6 +241,7 @@ + @@ -248,11 +251,18 @@ + + + + + + + @@ -267,23 +277,42 @@ + + + + + + + + + + + + - + + - + + + + + + + @@ -326,7 +355,6 @@ - @@ -352,10 +380,17 @@ + + + + + + + @@ -420,7 +455,6 @@ - @@ -448,7 +482,6 @@ - diff --git a/configure b/configure index fb81a2db1142b..a598f36bfb15b 100755 --- a/configure +++ b/configure @@ -22112,19 +22112,51 @@ if test "x$ac_cv_header_dinput_h" = xyes; then : fi + ac_fn_c_check_header_mongrel "$LINENO" "dxgi.h" "ac_cv_header_dxgi_h" "$ac_includes_default" +if test "x$ac_cv_header_dxgi_h" = xyes; then : + have_dxgi=yes +fi + + ac_fn_c_check_header_mongrel "$LINENO" "xaudio2.h" "ac_cv_header_xaudio2_h" "$ac_includes_default" if test "x$ac_cv_header_xaudio2_h" = xyes; then : have_xaudio2=yes fi - ac_fn_c_check_header_mongrel "$LINENO" "dxgi.h" "ac_cv_header_dxgi_h" "$ac_includes_default" -if test "x$ac_cv_header_dxgi_h" = xyes; then : - have_dxgi=yes + ac_fn_c_check_header_mongrel "$LINENO" "xinput.h" "ac_cv_header_xinput_h" "$ac_includes_default" +if test "x$ac_cv_header_xinput_h" = xyes; then : + have_xinput=yes fi + if test x$have_ddraw = xyes; then + +$as_echo "#define HAVE_DDRAW_H 1" >>confdefs.h + + fi + if test x$have_dinput = xyes; then + +$as_echo "#define HAVE_DINPUT_H 1" >>confdefs.h + + fi + if test x$have_dsound = xyes; then + +$as_echo "#define HAVE_DSOUND_H 1" >>confdefs.h + + fi + if test x$have_dxgi = xyes; then + +$as_echo "#define HAVE_DXGI_H 1" >>confdefs.h + + fi + if test x$have_xinput = xyes; then + +$as_echo "#define HAVE_XINPUT_H 1" >>confdefs.h + + fi + SUMMARY_video="${SUMMARY_video} directx" SUMMARY_audio="${SUMMARY_audio} directx" @@ -22981,11 +23013,6 @@ $as_echo "#define SDL_VIDEO_RENDER_D3D11 1" >>confdefs.h fi fi - if test x$have_dxgi = xyes; then - -$as_echo "#define HAVE_DXGI_H 1" >>confdefs.h - - fi # Set up files for the audio library if test x$enable_audio = xyes; then @@ -23008,25 +23035,38 @@ $as_echo "#define SDL_AUDIO_DRIVER_XAUDIO2 1" >>confdefs.h fi # Set up files for the joystick library if test x$enable_joystick = xyes; then - if test x$have_dinput = xyes; then + if test x$have_dinput = xyes -o x$have_xinput = xyes; then + if test x$have_xinput = xyes; then + +$as_echo "#define SDL_JOYSTICK_XINPUT 1" >>confdefs.h + + fi + if test x$have_dinput = xyes; then $as_echo "#define SDL_JOYSTICK_DINPUT 1" >>confdefs.h - SOURCES="$SOURCES $srcdir/src/joystick/windows/SDL_dxjoystick.c" - EXTRA_LDFLAGS="$EXTRA_LDFLAGS -ldinput8 -ldxguid -ldxerr8" + EXTRA_LDFLAGS="$EXTRA_LDFLAGS -ldinput8 -ldxguid -ldxerr8" + fi else $as_echo "#define SDL_JOYSTICK_WINMM 1" >>confdefs.h - SOURCES="$SOURCES $srcdir/src/joystick/windows/SDL_mmjoystick.c" fi + SOURCES="$SOURCES $srcdir/src/joystick/windows/*.c" have_joystick=yes fi if test x$enable_haptic = xyes; then - if test x$have_dinput = xyes; then + if test x$have_dinput = xyes -o x$have_xinput = xyes; then + if test x$have_xinput = xyes; then + +$as_echo "#define SDL_HAPTIC_XINPUT 1" >>confdefs.h + + fi + if test x$have_dinput = xyes; then $as_echo "#define SDL_HAPTIC_DINPUT 1" >>confdefs.h + fi SOURCES="$SOURCES $srcdir/src/haptic/windows/SDL_syshaptic.c" have_haptic=yes fi diff --git a/configure.in b/configure.in index efb371a2b2397..42aec1d3fb39d 100644 --- a/configure.in +++ b/configure.in @@ -2496,8 +2496,25 @@ AC_HELP_STRING([--enable-directx], [use DirectX for Windows audio/video [[defaul AC_CHECK_HEADER(ddraw.h, have_ddraw=yes) AC_CHECK_HEADER(dsound.h, have_dsound=yes) AC_CHECK_HEADER(dinput.h, have_dinput=yes) - AC_CHECK_HEADER(xaudio2.h, have_xaudio2=yes) AC_CHECK_HEADER(dxgi.h, have_dxgi=yes) + AC_CHECK_HEADER(xaudio2.h, have_xaudio2=yes) + AC_CHECK_HEADER(xinput.h, have_xinput=yes) + + if test x$have_ddraw = xyes; then + AC_DEFINE(HAVE_DDRAW_H, 1, [ ]) + fi + if test x$have_dinput = xyes; then + AC_DEFINE(HAVE_DINPUT_H, 1, [ ]) + fi + if test x$have_dsound = xyes; then + AC_DEFINE(HAVE_DSOUND_H, 1, [ ]) + fi + if test x$have_dxgi = xyes; then + AC_DEFINE(HAVE_DXGI_H, 1, [ ]) + fi + if test x$have_xinput = xyes; then + AC_DEFINE(HAVE_XINPUT_H, 1, [ ]) + fi SUMMARY_video="${SUMMARY_video} directx" SUMMARY_audio="${SUMMARY_audio} directx" @@ -2927,9 +2944,6 @@ AC_HELP_STRING([--enable-render-d3d], [enable the Direct3D render driver [[defau AC_DEFINE(SDL_VIDEO_RENDER_D3D11, 1, [ ]) fi fi - if test x$have_dxgi = xyes; then - AC_DEFINE(HAVE_DXGI_H, 1, [ ]) - fi # Set up files for the audio library if test x$enable_audio = xyes; then AC_DEFINE(SDL_AUDIO_DRIVER_WINMM, 1, [ ]) @@ -2946,19 +2960,28 @@ AC_HELP_STRING([--enable-render-d3d], [enable the Direct3D render driver [[defau fi # Set up files for the joystick library if test x$enable_joystick = xyes; then - if test x$have_dinput = xyes; then - AC_DEFINE(SDL_JOYSTICK_DINPUT, 1, [ ]) - SOURCES="$SOURCES $srcdir/src/joystick/windows/SDL_dxjoystick.c" - EXTRA_LDFLAGS="$EXTRA_LDFLAGS -ldinput8 -ldxguid -ldxerr8" + if test x$have_dinput = xyes -o x$have_xinput = xyes; then + if test x$have_xinput = xyes; then + AC_DEFINE(SDL_JOYSTICK_XINPUT, 1, [ ]) + fi + if test x$have_dinput = xyes; then + AC_DEFINE(SDL_JOYSTICK_DINPUT, 1, [ ]) + EXTRA_LDFLAGS="$EXTRA_LDFLAGS -ldinput8 -ldxguid -ldxerr8" + fi else AC_DEFINE(SDL_JOYSTICK_WINMM, 1, [ ]) - SOURCES="$SOURCES $srcdir/src/joystick/windows/SDL_mmjoystick.c" fi + SOURCES="$SOURCES $srcdir/src/joystick/windows/*.c" have_joystick=yes fi if test x$enable_haptic = xyes; then - if test x$have_dinput = xyes; then - AC_DEFINE(SDL_HAPTIC_DINPUT, 1, [ ]) + if test x$have_dinput = xyes -o x$have_xinput = xyes; then + if test x$have_xinput = xyes; then + AC_DEFINE(SDL_HAPTIC_XINPUT, 1, [ ]) + fi + if test x$have_dinput = xyes; then + AC_DEFINE(SDL_HAPTIC_DINPUT, 1, [ ]) + fi SOURCES="$SOURCES $srcdir/src/haptic/windows/SDL_syshaptic.c" have_haptic=yes fi diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in index 6d2ac9d1c3b74..ea25249ac5e86 100644 --- a/include/SDL_config.h.in +++ b/include/SDL_config.h.in @@ -51,7 +51,11 @@ #undef HAVE_GCC_SYNC_LOCK_TEST_AND_SET #undef HAVE_PTHREAD_SPINLOCK +#undef HAVE_DDRAW_H +#undef HAVE_DINPUT_H +#undef HAVE_DSOUND_H #undef HAVE_DXGI_H +#undef HAVE_XINPUT_H /* Comment this if you want to build without any C library requirements */ #undef HAVE_LIBC @@ -232,6 +236,7 @@ #undef SDL_INPUT_TSLIB #undef SDL_JOYSTICK_HAIKU #undef SDL_JOYSTICK_DINPUT +#undef SDL_JOYSTICK_XINPUT #undef SDL_JOYSTICK_DUMMY #undef SDL_JOYSTICK_IOKIT #undef SDL_JOYSTICK_LINUX @@ -243,6 +248,7 @@ #undef SDL_HAPTIC_LINUX #undef SDL_HAPTIC_IOKIT #undef SDL_HAPTIC_DINPUT +#undef SDL_HAPTIC_XINPUT /* Enable various shared object loading systems */ #undef SDL_LOADSO_HAIKU diff --git a/include/SDL_config_windows.h b/include/SDL_config_windows.h index 29e5a47fe4bc5..8bc7ffe09485a 100644 --- a/include/SDL_config_windows.h +++ b/include/SDL_config_windows.h @@ -76,7 +76,11 @@ typedef unsigned int uintptr_t; # define SIZEOF_VOIDP 4 #endif +#define HAVE_DDRAW_H 1 +#define HAVE_DINPUT_H 1 +#define HAVE_DSOUND_H 1 #define HAVE_DXGI_H 1 +#define HAVE_XINPUT_H 1 /* This is disabled by default to avoid C runtime dependencies and manifest requirements */ #ifdef HAVE_LIBC @@ -158,7 +162,9 @@ typedef unsigned int uintptr_t; /* Enable various input drivers */ #define SDL_JOYSTICK_DINPUT 1 +#define SDL_JOYSTICK_XINPUT 1 #define SDL_HAPTIC_DINPUT 1 +#define SDL_HAPTIC_XINPUT 1 /* Enable various shared object loading systems */ #define SDL_LOADSO_WINDOWS 1 diff --git a/include/SDL_config_winrt.h b/include/SDL_config_winrt.h index 97c51405f7f39..74ebfbcf209af 100644 --- a/include/SDL_config_winrt.h +++ b/include/SDL_config_winrt.h @@ -78,6 +78,7 @@ typedef unsigned int uintptr_t; /* Useful headers */ #define HAVE_DXGI_H 1 +#define HAVE_XINPUT_H 1 #define HAVE_LIBC 1 #define HAVE_STDIO_H 1 #define STDC_HEADERS 1 @@ -148,13 +149,12 @@ typedef unsigned int uintptr_t; #define SDL_AUDIO_DRIVER_DUMMY 1 /* Enable various input drivers */ -// TODO, WinRT: Get haptic support working -#define SDL_HAPTIC_DISABLED 1 - #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP #define SDL_JOYSTICK_DISABLED 1 +#define SDL_HAPTIC_DISABLED 1 #else #define SDL_JOYSTICK_XINPUT 1 +#define SDL_HAPTIC_XINPUT 1 #endif /* Enable various shared object loading systems */ diff --git a/src/audio/directsound/SDL_directsound.h b/src/audio/directsound/SDL_directsound.h index b873391fb7e6d..ae7faa4511f09 100644 --- a/src/audio/directsound/SDL_directsound.h +++ b/src/audio/directsound/SDL_directsound.h @@ -23,7 +23,7 @@ #ifndef _SDL_directsound_h #define _SDL_directsound_h -#include "directx.h" +#include "../../core/windows/SDL_directx.h" #include "../SDL_sysaudio.h" diff --git a/src/audio/directsound/directx.h b/src/core/windows/SDL_directx.h similarity index 86% rename from src/audio/directsound/directx.h rename to src/core/windows/SDL_directx.h index 472a0ee0ed9bf..f37bccef2f2f9 100644 --- a/src/audio/directsound/directx.h +++ b/src/core/windows/SDL_directx.h @@ -18,13 +18,14 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ +#include "../../SDL_internal.h" -#ifndef _directx_h -#define _directx_h +#ifndef _SDL_directx_h +#define _SDL_directx_h /* Include all of the DirectX 8.0 headers and adds any necessary tweaks */ -#include "../../core/windows/SDL_windows.h" +#include "SDL_windows.h" #include #ifndef WIN32 #define WIN32 @@ -91,12 +92,20 @@ /* We need these defines to mark what version of DirectX API we use */ #define DIRECTDRAW_VERSION 0x0700 #define DIRECTSOUND_VERSION 0x0800 -#define DIRECTINPUT_VERSION 0x0500 +#define DIRECTINPUT_VERSION 0x0800 /* Need version 7 for force feedback. Need version 8 so IDirectInput8_EnumDevices doesn't leak like a sieve... */ +#ifdef HAVE_DDRAW_H #include +#endif +#ifdef HAVE_DSOUND_H #include +#endif +#ifdef HAVE_DINPUT_H #include +#else +typedef struct { int unused; } DIDEVICEINSTANCE; +#endif -#endif /* _directx_h */ +#endif /* _SDL_directx_h */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/core/windows/SDL_xinput.c b/src/core/windows/SDL_xinput.c new file mode 100644 index 0000000000000..9b2a54fef6a6c --- /dev/null +++ b/src/core/windows/SDL_xinput.c @@ -0,0 +1,94 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2014 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#include "SDL_assert.h" +#include "SDL_xinput.h" + + +#ifdef HAVE_XINPUT_H + +XInputGetState_t SDL_XInputGetState = NULL; +XInputSetState_t SDL_XInputSetState = NULL; +XInputGetCapabilities_t SDL_XInputGetCapabilities = NULL; +DWORD SDL_XInputVersion = 0; + +static HANDLE s_pXInputDLL = 0; +static int s_XInputDLLRefCount = 0; + + +int +WIN_LoadXInputDLL(void) +{ + DWORD version = 0; + + if (s_pXInputDLL) { + SDL_assert(s_XInputDLLRefCount > 0); + s_XInputDLLRefCount++; + return 0; /* already loaded */ + } + + version = (1 << 16) | 4; + s_pXInputDLL = LoadLibrary(L"XInput1_4.dll"); /* 1.4 Ships with Windows 8. */ + if (!s_pXInputDLL) { + version = (1 << 16) | 3; + s_pXInputDLL = LoadLibrary(L"XInput1_3.dll"); /* 1.3 Ships with Vista and Win7, can be installed as a redistributable component. */ + } + if (!s_pXInputDLL) { + s_pXInputDLL = LoadLibrary(L"bin\\XInput1_3.dll"); + } + if (!s_pXInputDLL) { + return -1; + } + + SDL_assert(s_XInputDLLRefCount == 0); + SDL_XInputVersion = version; + s_XInputDLLRefCount = 1; + + /* 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... */ + SDL_XInputGetState = (XInputGetState_t)GetProcAddress((HMODULE)s_pXInputDLL, (LPCSTR)100); + SDL_XInputSetState = (XInputSetState_t)GetProcAddress((HMODULE)s_pXInputDLL, "XInputSetState"); + SDL_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress((HMODULE)s_pXInputDLL, "XInputGetCapabilities"); + if (!SDL_XInputGetState || !SDL_XInputSetState || !SDL_XInputGetCapabilities) { + WIN_UnloadXInputDLL(); + return -1; + } + + return 0; +} + +void +WIN_UnloadXInputDLL(void) +{ + if (s_pXInputDLL) { + SDL_assert(s_XInputDLLRefCount > 0); + if (--s_XInputDLLRefCount == 0) { + FreeLibrary(s_pXInputDLL); + s_pXInputDLL = NULL; + } + } else { + SDL_assert(s_XInputDLLRefCount == 0); + } +} + +#endif /* HAVE_XINPUT_H */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/windows/SDL_dxjoystick_c.h b/src/core/windows/SDL_xinput.h similarity index 67% rename from src/joystick/windows/SDL_dxjoystick_c.h rename to src/core/windows/SDL_xinput.h index e0739587ca24a..721c3811b0629 100644 --- a/src/joystick/windows/SDL_dxjoystick_c.h +++ b/src/core/windows/SDL_xinput.h @@ -20,29 +20,13 @@ */ #include "../../SDL_internal.h" -#ifndef SDL_JOYSTICK_DINPUT_H - -/* DirectInput joystick driver; written by Glenn Maynard, based on Andrei de - * A. Formiga's WINMM driver. - * - * Hats and sliders are completely untested; the app I'm writing this for mostly - * 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 - * let it return 0 events. */ - -#include "../../core/windows/SDL_windows.h" - -#define DIRECTINPUT_VERSION 0x0800 /* Need version 7 for force feedback. Need version 8 so IDirectInput8_EnumDevices doesn't leak like a sieve... */ -#include -#define COBJMACROS -#include -#include -#include -#include -#include +#ifndef _SDL_xinput_h +#define _SDL_xinput_h + +#ifdef HAVE_XINPUT_H +#include "SDL_windows.h" +#include #ifndef XUSER_MAX_COUNT #define XUSER_MAX_COUNT 4 @@ -142,45 +126,9 @@ extern DWORD SDL_XInputVersion; /* ((major << 16) & 0xFF00) | (minor & 0xFF) */ #define XINPUTGETSTATE SDL_XInputGetState #define XINPUTSETSTATE SDL_XInputSetState #define XINPUTGETCAPABILITIES SDL_XInputGetCapabilities -#define INVALID_XINPUT_USERID XUSER_INDEX_ANY -#define SDL_XINPUT_MAX_DEVICES XUSER_MAX_COUNT -#define MAX_INPUTS 256 /* each joystick can have up to 256 inputs */ +#endif /* HAVE_XINPUT_H */ +#endif /* _SDL_xinput_h */ -/* local types */ -typedef enum Type -{ BUTTON, AXIS, HAT } Type; - -typedef struct input_t -{ - /* DirectInput offset for this input type: */ - DWORD ofs; - - /* Button, axis or hat: */ - Type type; - - /* SDL input offset: */ - Uint8 num; -} input_t; - -/* The private structure used to keep track of a joystick */ -struct joystick_hwdata -{ - LPDIRECTINPUTDEVICE8 InputDevice; - DIDEVCAPS Capabilities; - int buffered; - SDL_JoystickGUID guid; - - input_t Inputs[MAX_INPUTS]; - int NumInputs; - int NumSliders; - SDL_bool removed; - SDL_bool send_remove_event; - SDL_bool bXInputDevice; /* SDL_TRUE if this device supports using the xinput API rather than DirectInput */ - SDL_bool bXInputHaptic; /* Supports force feedback via XInput. */ - Uint8 userid; /* XInput userid index for this joystick */ - DWORD dwPacketNumber; -}; - -#endif /* SDL_JOYSTICK_DINPUT_H */ +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/haptic/windows/SDL_syshaptic.c b/src/haptic/windows/SDL_dinputhaptic.c similarity index 58% rename from src/haptic/windows/SDL_syshaptic.c rename to src/haptic/windows/SDL_dinputhaptic.c index c5a9011499d6c..e96fe1bea270b 100644 --- a/src/haptic/windows/SDL_syshaptic.c +++ b/src/haptic/windows/SDL_dinputhaptic.c @@ -20,62 +20,21 @@ */ #include "../../SDL_internal.h" -#ifdef SDL_HAPTIC_DINPUT - -#include "SDL_assert.h" -#include "SDL_thread.h" -#include "SDL_mutex.h" -#include "SDL_timer.h" -#include "SDL_hints.h" +#include "SDL_error.h" #include "SDL_haptic.h" +#include "SDL_timer.h" +#include "SDL_windowshaptic_c.h" +#include "SDL_dinputhaptic_c.h" #include "../SDL_syshaptic.h" -#include "SDL_joystick.h" -#include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */ -#include "../../joystick/windows/SDL_dxjoystick_c.h" /* For joystick hwdata */ - -#include "SDL_syshaptic_c.h" - -/* - * List of available haptic devices. - */ -typedef struct SDL_hapticlist_item -{ - DIDEVICEINSTANCE instance; - char *name; - SDL_Haptic *haptic; - DIDEVCAPS capabilities; - Uint8 bXInputHaptic; /* Supports force feedback via XInput. */ - Uint8 userid; /* XInput userid index for this joystick */ - struct SDL_hapticlist_item *next; -} SDL_hapticlist_item; - +#include "../../joystick/windows/SDL_windowsjoystick_c.h" -/* - * Haptic system hardware data. - */ -struct haptic_hwdata -{ - LPDIRECTINPUTDEVICE8 device; - DWORD axes[3]; /* Axes to use. */ - SDL_bool is_joystick; /* Device is loaded as joystick. */ - Uint8 bXInputHaptic; /* Supports force feedback via XInput. */ - Uint8 userid; /* XInput userid index for this joystick */ - SDL_Thread *thread; - SDL_mutex *mutex; - volatile Uint32 stopTicks; - volatile int stopThread; -}; +#if SDL_HAPTIC_DINPUT /* - * Haptic system effect data. + * External stuff. */ -struct haptic_hweffect -{ - DIEFFECT effect; - LPDIRECTINPUTEFFECT ref; - XINPUT_VIBRATION vibration; -}; +extern HWND SDL_HelperWindow; /* @@ -83,41 +42,6 @@ struct haptic_hweffect */ static SDL_bool coinitialized = SDL_FALSE; static LPDIRECTINPUT8 dinput = NULL; -static SDL_bool loaded_xinput = SDL_FALSE; -static SDL_hapticlist_item *SDL_hapticlist = NULL; -static SDL_hapticlist_item *SDL_hapticlist_tail = NULL; -static int numhaptics = 0; - -/* - * External stuff. - */ -extern HWND SDL_HelperWindow; - - -/* - * Prototypes. - */ -static int DI_SetError(const char *str, HRESULT err); -static int DI_GUIDIsSame(const GUID * a, const GUID * b); -static int SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic, - DIDEVICEINSTANCE instance); -static int SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic, - LPDIRECTINPUTDEVICE8 device8, - SDL_bool is_joystick); -static int SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, const Uint8 userid); -static DWORD DIGetTriggerButton(Uint16 button); -static int SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir, - int naxes); -static int SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest, - SDL_HapticEffect * src); -static void SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type); -static REFGUID SDL_SYS_HapticEffectType(SDL_HapticEffect * effect); -static int SDLCALL SDL_RunXInputHaptic(void *arg); - -/* Callbacks. */ -static BOOL CALLBACK EnumHapticsCallback(const DIDEVICEINSTANCE * - pdidInstance, VOID * pContext); -static BOOL CALLBACK DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv); /* @@ -133,7 +57,6 @@ DI_SetError(const char *str, HRESULT err) return SDL_SetError("Haptic error %s", str); } - /* * Checks to see if two GUID are the same. */ @@ -143,14 +66,20 @@ DI_GUIDIsSame(const GUID * a, const GUID * b) return (SDL_memcmp(a, b, sizeof (GUID)) == 0); } - /* - * Initializes the haptic subsystem. + * Callback to find the haptic devices. */ +static BOOL CALLBACK +EnumHapticsCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext) +{ + (void) pContext; + SDL_DINPUT_MaybeAddDevice(pdidInstance); + return DIENUM_CONTINUE; /* continue enumerating */ +} + int -SDL_SYS_HapticInit(void) +SDL_DINPUT_HapticInit(void) { - const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED); HRESULT ret; HINSTANCE instance; @@ -166,7 +95,7 @@ SDL_SYS_HapticInit(void) coinitialized = SDL_TRUE; ret = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER, - &IID_IDirectInput8, (LPVOID) & dinput); + &IID_IDirectInput8, (LPVOID)& dinput); if (FAILED(ret)) { SDL_SYS_HapticQuit(); return DI_SetError("CoCreateInstance", ret); @@ -177,7 +106,7 @@ SDL_SYS_HapticInit(void) if (instance == NULL) { SDL_SYS_HapticQuit(); return SDL_SetError("GetModuleHandle() failed with error code %d.", - GetLastError()); + GetLastError()); } ret = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION); if (FAILED(ret)) { @@ -187,33 +116,20 @@ SDL_SYS_HapticInit(void) /* Look for haptic devices. */ ret = IDirectInput8_EnumDevices(dinput, - 0, - EnumHapticsCallback, - NULL, - DIEDFL_FORCEFEEDBACK | - DIEDFL_ATTACHEDONLY); + 0, + EnumHapticsCallback, + NULL, + DIEDFL_FORCEFEEDBACK | + DIEDFL_ATTACHEDONLY); if (FAILED(ret)) { SDL_SYS_HapticQuit(); return DI_SetError("Enumerating DirectInput devices", ret); } - - if (!env || SDL_atoi(env)) { - loaded_xinput = (WIN_LoadXInputDLL() == 0); - } - - if (loaded_xinput) { - DWORD i; - for (i = 0; i < SDL_XINPUT_MAX_DEVICES; i++) { - XInputHaptic_MaybeAddDevice(i); - } - } - - return numhaptics; + return 0; } - int -DirectInputHaptic_MaybeAddDevice(const DIDEVICEINSTANCE * pdidInstance) +SDL_DINPUT_MaybeAddDevice(const DIDEVICEINSTANCE * pdidInstance) { HRESULT ret; LPDIRECTINPUTDEVICE8 device; @@ -227,7 +143,7 @@ DirectInputHaptic_MaybeAddDevice(const DIDEVICEINSTANCE * pdidInstance) /* Make sure we don't already have it */ for (item = SDL_hapticlist; item; item = item->next) { - if ( (!item->bXInputHaptic) && (SDL_memcmp(&item->instance, pdidInstance, sizeof (*pdidInstance)) == 0) ) { + if ((!item->bXInputHaptic) && (SDL_memcmp(&item->instance, pdidInstance, sizeof(*pdidInstance)) == 0)) { return -1; /* Already added */ } } @@ -241,7 +157,7 @@ DirectInputHaptic_MaybeAddDevice(const DIDEVICEINSTANCE * pdidInstance) /* Get capabilities. */ SDL_zero(capabilities); - capabilities.dwSize = sizeof (DIDEVCAPS); + capabilities.dwSize = sizeof(DIDEVCAPS); ret = IDirectInputDevice8_GetCapabilities(device, &capabilities); IDirectInputDevice8_Release(device); if (FAILED(ret)) { @@ -265,25 +181,14 @@ DirectInputHaptic_MaybeAddDevice(const DIDEVICEINSTANCE * pdidInstance) } /* Copy the instance over, useful for creating devices. */ - SDL_memcpy(&item->instance, pdidInstance, sizeof (DIDEVICEINSTANCE)); - SDL_memcpy(&item->capabilities, &capabilities, sizeof (capabilities)); - - if (SDL_hapticlist_tail == NULL) { - SDL_hapticlist = SDL_hapticlist_tail = item; - } else { - SDL_hapticlist_tail->next = item; - SDL_hapticlist_tail = item; - } - - /* Device has been added. */ - ++numhaptics; + SDL_memcpy(&item->instance, pdidInstance, sizeof(DIDEVICEINSTANCE)); + SDL_memcpy(&item->capabilities, &capabilities, sizeof(capabilities)); - return numhaptics; + return SDL_SYS_AddHapticDevice(item); } - int -DirectInputHaptic_MaybeRemoveDevice(const DIDEVICEINSTANCE * pdidInstance) +SDL_DINPUT_MaybeRemoveDevice(const DIDEVICEINSTANCE * pdidInstance) { SDL_hapticlist_item *item; SDL_hapticlist_item *prev = NULL; @@ -293,205 +198,15 @@ DirectInputHaptic_MaybeRemoveDevice(const DIDEVICEINSTANCE * pdidInstance) } for (item = SDL_hapticlist; item != NULL; item = item->next) { - if ( (!item->bXInputHaptic) && (SDL_memcmp(&item->instance, pdidInstance, sizeof (*pdidInstance)) == 0) ) { + if (!item->bXInputHaptic && SDL_memcmp(&item->instance, pdidInstance, sizeof(*pdidInstance)) == 0) { /* found it, remove it. */ - const int retval = item->haptic ? item->haptic->index : -1; - if (prev != NULL) { - prev->next = item->next; - } else { - SDL_assert(SDL_hapticlist == item); - SDL_hapticlist = item->next; - } - if (item == SDL_hapticlist_tail) { - SDL_hapticlist_tail = prev; - } - --numhaptics; - /* !!! TODO: Send a haptic remove event? */ - SDL_free(item); - return retval; + return SDL_SYS_RemoveHapticDevice(prev, item); } prev = item; } - return -1; } - -int -XInputHaptic_MaybeAddDevice(const DWORD dwUserid) -{ - const Uint8 userid = (Uint8) dwUserid; - SDL_hapticlist_item *item; - XINPUT_VIBRATION state; - - if ((!loaded_xinput) || (dwUserid >= SDL_XINPUT_MAX_DEVICES)) { - return -1; - } - - /* Make sure we don't already have it */ - for (item = SDL_hapticlist; item; item = item->next) { - if ((item->bXInputHaptic) && (item->userid == userid)) { - return -1; /* Already added */ - } - } - - SDL_zero(state); - if (XINPUTSETSTATE(dwUserid, &state) != ERROR_SUCCESS) { - return -1; /* no force feedback on this device. */ - } - - item = (SDL_hapticlist_item *)SDL_malloc( sizeof(SDL_hapticlist_item)); - if (item == NULL) { - return SDL_OutOfMemory(); - } - - SDL_zerop(item); - - /* !!! FIXME: I'm not bothering to query for a real name right now (can we even?) */ - { - char buf[64]; - SDL_snprintf(buf, sizeof (buf), "XInput Controller #%u", (unsigned int) (userid+1)); - item->name = SDL_strdup(buf); - } - - if (!item->name) { - SDL_free(item); - return -1; - } - - /* Copy the instance over, useful for creating devices. */ - item->bXInputHaptic = 1; - item->userid = userid; - - if (SDL_hapticlist_tail == NULL) { - SDL_hapticlist = SDL_hapticlist_tail = item; - } else { - SDL_hapticlist_tail->next = item; - SDL_hapticlist_tail = item; - } - - /* Device has been added. */ - ++numhaptics; - - return numhaptics; -} - - -int -XInputHaptic_MaybeRemoveDevice(const DWORD dwUserid) -{ - const Uint8 userid = (Uint8) dwUserid; - SDL_hapticlist_item *item; - SDL_hapticlist_item *prev = NULL; - - if ((!loaded_xinput) || (dwUserid >= SDL_XINPUT_MAX_DEVICES)) { - return -1; - } - - for (item = SDL_hapticlist; item != NULL; item = item->next) { - if ((item->bXInputHaptic) && (item->userid == userid)) { - /* found it, remove it. */ - const int retval = item->haptic ? item->haptic->index : -1; - if (prev != NULL) { - prev->next = item->next; - } else { - SDL_assert(SDL_hapticlist == item); - SDL_hapticlist = item->next; - } - if (item == SDL_hapticlist_tail) { - SDL_hapticlist_tail = prev; - } - --numhaptics; - /* !!! TODO: Send a haptic remove event? */ - SDL_free(item); - return retval; - } - prev = item; - } - - return -1; -} - - -/* - * Callback to find the haptic devices. - */ -static BOOL CALLBACK -EnumHapticsCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext) -{ - (void) pContext; - DirectInputHaptic_MaybeAddDevice(pdidInstance); - return DIENUM_CONTINUE; /* continue enumerating */ -} - - -int -SDL_SYS_NumHaptics() -{ - return numhaptics; -} - -static SDL_hapticlist_item * -HapticByDevIndex(int device_index) -{ - SDL_hapticlist_item *item = SDL_hapticlist; - - if ((device_index < 0) || (device_index >= numhaptics)) { - return NULL; - } - - while (device_index > 0) { - SDL_assert(item != NULL); - --device_index; - item = item->next; - } - - return item; -} - -/* - * Return the name of a haptic device, does not need to be opened. - */ -const char * -SDL_SYS_HapticName(int index) -{ - SDL_hapticlist_item *item = HapticByDevIndex(index); - return item->name; -} - - -/* - * Callback to get all supported effects. - */ -#define EFFECT_TEST(e,s) \ -if (DI_GUIDIsSame(&pei->guid, &(e))) \ - haptic->supported |= (s) -static BOOL CALLBACK -DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv) -{ - /* Prepare the haptic device. */ - SDL_Haptic *haptic = (SDL_Haptic *) pv; - - /* Get supported. */ - EFFECT_TEST(GUID_Spring, SDL_HAPTIC_SPRING); - EFFECT_TEST(GUID_Damper, SDL_HAPTIC_DAMPER); - EFFECT_TEST(GUID_Inertia, SDL_HAPTIC_INERTIA); - EFFECT_TEST(GUID_Friction, SDL_HAPTIC_FRICTION); - EFFECT_TEST(GUID_ConstantForce, SDL_HAPTIC_CONSTANT); - EFFECT_TEST(GUID_CustomForce, SDL_HAPTIC_CUSTOM); - EFFECT_TEST(GUID_Sine, SDL_HAPTIC_SINE); - /* !!! FIXME: put this back when we have more bits in 2.1 */ - /* EFFECT_TEST(GUID_Square, SDL_HAPTIC_SQUARE); */ - EFFECT_TEST(GUID_Triangle, SDL_HAPTIC_TRIANGLE); - EFFECT_TEST(GUID_SawtoothUp, SDL_HAPTIC_SAWTOOTHUP); - EFFECT_TEST(GUID_SawtoothDown, SDL_HAPTIC_SAWTOOTHDOWN); - EFFECT_TEST(GUID_RampForce, SDL_HAPTIC_RAMP); - - /* Check for more. */ - return DIENUM_CONTINUE; -} - - /* * Callback to get supported axes. */ @@ -531,118 +246,39 @@ DI_DeviceObjectCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef) return DIENUM_CONTINUE; } - /* - * Opens the haptic device from the file descriptor. - * - * Steps: - * - Open temporary DirectInputDevice interface. - * - Create DirectInputDevice8 interface. - * - Release DirectInputDevice interface. - * - Call SDL_SYS_HapticOpenFromDevice8 + * Callback to get all supported effects. */ -static int -SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic, DIDEVICEINSTANCE instance) +#define EFFECT_TEST(e,s) \ +if (DI_GUIDIsSame(&pei->guid, &(e))) \ + haptic->supported |= (s) +static BOOL CALLBACK +DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv) { - HRESULT ret; - int ret2; - LPDIRECTINPUTDEVICE8 device; - LPDIRECTINPUTDEVICE8 device8; - - /* Open the device */ - ret = IDirectInput8_CreateDevice(dinput, &instance.guidInstance, - &device, NULL); - if (FAILED(ret)) { - DI_SetError("Creating DirectInput device", ret); - return -1; - } - - /* Now get the IDirectInputDevice8 interface, instead. */ - ret = IDirectInputDevice8_QueryInterface(device, - &IID_IDirectInputDevice8, - (LPVOID *) &device8); - /* Done with the temporary one now. */ - IDirectInputDevice8_Release(device); - if (FAILED(ret)) { - DI_SetError("Querying DirectInput interface", ret); - return -1; - } + /* Prepare the haptic device. */ + SDL_Haptic *haptic = (SDL_Haptic *) pv; - ret2 = SDL_SYS_HapticOpenFromDevice8(haptic, device8, SDL_FALSE); - if (ret2 < 0) { - IDirectInputDevice8_Release(device8); - return -1; - } + /* Get supported. */ + EFFECT_TEST(GUID_Spring, SDL_HAPTIC_SPRING); + EFFECT_TEST(GUID_Damper, SDL_HAPTIC_DAMPER); + EFFECT_TEST(GUID_Inertia, SDL_HAPTIC_INERTIA); + EFFECT_TEST(GUID_Friction, SDL_HAPTIC_FRICTION); + EFFECT_TEST(GUID_ConstantForce, SDL_HAPTIC_CONSTANT); + EFFECT_TEST(GUID_CustomForce, SDL_HAPTIC_CUSTOM); + EFFECT_TEST(GUID_Sine, SDL_HAPTIC_SINE); + /* !!! FIXME: put this back when we have more bits in 2.1 */ + /* EFFECT_TEST(GUID_Square, SDL_HAPTIC_SQUARE); */ + EFFECT_TEST(GUID_Triangle, SDL_HAPTIC_TRIANGLE); + EFFECT_TEST(GUID_SawtoothUp, SDL_HAPTIC_SAWTOOTHUP); + EFFECT_TEST(GUID_SawtoothDown, SDL_HAPTIC_SAWTOOTHDOWN); + EFFECT_TEST(GUID_RampForce, SDL_HAPTIC_RAMP); - return 0; + /* Check for more. */ + return DIENUM_CONTINUE; } -static int -SDL_SYS_HapticOpenFromXInput(SDL_Haptic *haptic, const Uint8 userid) -{ - char threadName[32]; - XINPUT_VIBRATION vibration = { 0, 0 }; /* stop any current vibration */ - XINPUTSETSTATE(userid, &vibration); - - haptic->supported = SDL_HAPTIC_LEFTRIGHT; - - haptic->neffects = 1; - haptic->nplaying = 1; - - /* Prepare effects memory. */ - haptic->effects = (struct haptic_effect *) - SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); - if (haptic->effects == NULL) { - return SDL_OutOfMemory(); - } - /* Clear the memory */ - SDL_memset(haptic->effects, 0, - sizeof(struct haptic_effect) * haptic->neffects); - - haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata)); - if (haptic->hwdata == NULL) { - SDL_free(haptic->effects); - haptic->effects = NULL; - return SDL_OutOfMemory(); - } - SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); - - haptic->hwdata->bXInputHaptic = 1; - haptic->hwdata->userid = userid; - - haptic->hwdata->mutex = SDL_CreateMutex(); - if (haptic->hwdata->mutex == NULL) { - SDL_free(haptic->effects); - SDL_free(haptic->hwdata); - haptic->effects = NULL; - return SDL_SetError("Couldn't create XInput haptic mutex"); - } - - SDL_snprintf(threadName, sizeof (threadName), "SDLXInputDev%d", (int) userid); - -#if defined(__WIN32__) && !defined(HAVE_LIBC) /* !!! FIXME: this is nasty. */ - #undef SDL_CreateThread - #if SDL_DYNAMIC_API - haptic->hwdata->thread = SDL_CreateThread_REAL(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL); - #else - haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL); - #endif -#else - haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata); -#endif - if (haptic->hwdata->thread == NULL) { - SDL_DestroyMutex(haptic->hwdata->mutex); - SDL_free(haptic->effects); - SDL_free(haptic->hwdata); - haptic->effects = NULL; - return SDL_SetError("Couldn't create XInput haptic thread"); - } - - return 0; - } - /* - * Opens the haptic device from the file descriptor. + * Opens the haptic device. * * Steps: * - Set cooperative level. @@ -652,8 +288,7 @@ SDL_SYS_HapticOpenFromXInput(SDL_Haptic *haptic, const Uint8 userid) * - Get supported features. */ static int -SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic, - LPDIRECTINPUTDEVICE8 device8, SDL_bool is_joystick) +SDL_DINPUT_HapticOpenFromDevice(SDL_Haptic * haptic, LPDIRECTINPUTDEVICE8 device8, SDL_bool is_joystick) { HRESULT ret; DIPROPDWORD dipdw; @@ -788,203 +423,106 @@ SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic, acquire_err: IDirectInputDevice8_Unacquire(haptic->hwdata->device); return -1; - } - -/* - * Opens a haptic device for usage. - */ int -SDL_SYS_HapticOpen(SDL_Haptic * haptic) +SDL_DINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item) { - SDL_hapticlist_item *item = HapticByDevIndex(haptic->index); - return (item->bXInputHaptic) ? SDL_SYS_HapticOpenFromXInput(haptic, item->userid) : SDL_SYS_HapticOpenFromInstance(haptic, item->instance); -} - + HRESULT ret; + LPDIRECTINPUTDEVICE8 device; + LPDIRECTINPUTDEVICE8 device8; -/* - * Opens a haptic device from first mouse it finds for usage. - */ -int -SDL_SYS_HapticMouse(void) -{ - SDL_hapticlist_item *item; - int index = 0; + /* Open the device */ + ret = IDirectInput8_CreateDevice(dinput, &item->instance.guidInstance, + &device, NULL); + if (FAILED(ret)) { + DI_SetError("Creating DirectInput device", ret); + return -1; + } - /* Grab the first mouse haptic device we find. */ - for (item = SDL_hapticlist; item != NULL; item = item->next) { - SDL_assert(index >= 0); - if (item->capabilities.dwDevType == DI8DEVCLASS_POINTER ) { - return index; - } - ++index; + /* Now get the IDirectInputDevice8 interface, instead. */ + ret = IDirectInputDevice8_QueryInterface(device, + &IID_IDirectInputDevice8, + (LPVOID *)&device8); + /* Done with the temporary one now. */ + IDirectInputDevice8_Release(device); + if (FAILED(ret)) { + DI_SetError("Querying DirectInput interface", ret); + return -1; } - return -1; + if (SDL_DINPUT_HapticOpenFromDevice(haptic, device8, SDL_FALSE) < 0) { + IDirectInputDevice8_Release(device8); + return -1; + } + return 0; } - -/* - * Checks to see if a joystick has haptic features. - */ int -SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick) +SDL_DINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) { - const struct joystick_hwdata *hwdata = joystick->hwdata; - return ( (hwdata->bXInputHaptic) || - ((hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) != 0) ); -} - + HRESULT ret; + DIDEVICEINSTANCE hap_instance, joy_instance; -/* - * Checks to see if the haptic device and joystick are in reality the same. - */ -int -SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) -{ - if (joystick->hwdata->bXInputHaptic != haptic->hwdata->bXInputHaptic) { - return 0; /* one is XInput, one is not; not the same device. */ - } else if (joystick->hwdata->bXInputHaptic) { /* XInput */ - return (haptic->hwdata->userid == joystick->hwdata->userid); - } else { /* DirectInput */ - HRESULT ret; - DIDEVICEINSTANCE hap_instance, joy_instance; - - hap_instance.dwSize = sizeof(DIDEVICEINSTANCE); - joy_instance.dwSize = sizeof(DIDEVICEINSTANCE); - - /* Get the device instances. */ - ret = IDirectInputDevice8_GetDeviceInfo(haptic->hwdata->device, - &hap_instance); - if (FAILED(ret)) { - return 0; - } - ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, - &joy_instance); - if (FAILED(ret)) { - return 0; - } + hap_instance.dwSize = sizeof(DIDEVICEINSTANCE); + joy_instance.dwSize = sizeof(DIDEVICEINSTANCE); - if (DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance)) - return 1; + /* Get the device instances. */ + ret = IDirectInputDevice8_GetDeviceInfo(haptic->hwdata->device, + &hap_instance); + if (FAILED(ret)) { + return 0; + } + ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, + &joy_instance); + if (FAILED(ret)) { + return 0; } - return 0; + return DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance); } - -/* - * Opens a SDL_Haptic from a SDL_Joystick. - */ int -SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +SDL_DINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) { SDL_hapticlist_item *item; int index = 0; + HRESULT ret; + DIDEVICEINSTANCE joy_instance; - /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */ - if (joystick->hwdata->bXInputDevice) { - const Uint8 userid = joystick->hwdata->userid; - for (item = SDL_hapticlist; item != NULL; item = item->next) { - if ((item->bXInputHaptic) && (item->userid == userid)) { - SDL_assert(joystick->hwdata->bXInputHaptic); - haptic->index = index; - return SDL_SYS_HapticOpenFromXInput(haptic, userid); - } - ++index; - } - } else { - HRESULT idret; - DIDEVICEINSTANCE joy_instance; - - joy_instance.dwSize = sizeof(DIDEVICEINSTANCE); - idret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, &joy_instance); - if (FAILED(idret)) { - return -1; - } + joy_instance.dwSize = sizeof(DIDEVICEINSTANCE); + ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, &joy_instance); + if (FAILED(ret)) { + return -1; + } - for (item = SDL_hapticlist; item != NULL; item = item->next) { - if (DI_GUIDIsSame(&item->instance.guidInstance, &joy_instance.guidInstance)) { - haptic->index = index; - return SDL_SYS_HapticOpenFromDevice8(haptic, joystick->hwdata->InputDevice, SDL_TRUE); - } - ++index; + /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */ + for (item = SDL_hapticlist; item != NULL; item = item->next) { + if (!item->bXInputHaptic && DI_GUIDIsSame(&item->instance.guidInstance, &joy_instance.guidInstance)) { + haptic->index = index; + return SDL_DINPUT_HapticOpenFromDevice(haptic, joystick->hwdata->InputDevice, SDL_TRUE); } + ++index; } - /* No match to our haptic list */ + SDL_SetError("Couldn't find joystick in haptic device list"); return -1; } - -/* - * Closes the haptic device. - */ void -SDL_SYS_HapticClose(SDL_Haptic * haptic) +SDL_DINPUT_HapticClose(SDL_Haptic * haptic) { - if (haptic->hwdata) { - - /* Free effects. */ - SDL_free(haptic->effects); - haptic->effects = NULL; - haptic->neffects = 0; - - /* Clean up */ - if (haptic->hwdata->bXInputHaptic) { - haptic->hwdata->stopThread = 1; - SDL_WaitThread(haptic->hwdata->thread, NULL); - SDL_DestroyMutex(haptic->hwdata->mutex); - } else { - IDirectInputDevice8_Unacquire(haptic->hwdata->device); - /* Only release if isn't grabbed by a joystick. */ - if (haptic->hwdata->is_joystick == 0) { - IDirectInputDevice8_Release(haptic->hwdata->device); - } - } + IDirectInputDevice8_Unacquire(haptic->hwdata->device); - /* Free */ - SDL_free(haptic->hwdata); - haptic->hwdata = NULL; + /* Only release if isn't grabbed by a joystick. */ + if (haptic->hwdata->is_joystick == 0) { + IDirectInputDevice8_Release(haptic->hwdata->device); } } - -/* - * Clean up after system specific haptic stuff - */ void -SDL_SYS_HapticQuit(void) +SDL_DINPUT_HapticQuit(void) { - SDL_hapticlist_item *item; - SDL_hapticlist_item *next = NULL; - SDL_Haptic *hapticitem = NULL; - - extern SDL_Haptic *SDL_haptics; - for (hapticitem = SDL_haptics; hapticitem; hapticitem = hapticitem->next) { - if ((hapticitem->hwdata->bXInputHaptic) && (hapticitem->hwdata->thread)) { - /* we _have_ to stop the thread before we free the XInput DLL! */ - hapticitem->hwdata->stopThread = 1; - SDL_WaitThread(hapticitem->hwdata->thread, NULL); - hapticitem->hwdata->thread = NULL; - } - } - - for (item = SDL_hapticlist; item; item = next) { - /* Opened and not closed haptics are leaked, this is on purpose. - * Close your haptic devices after usage. */ - /* !!! FIXME: (...is leaking on purpose a good idea?) */ - next = item->next; - SDL_free(item->name); - SDL_free(item); - } - - if (loaded_xinput) { - WIN_UnloadXInputDLL(); - loaded_xinput = SDL_FALSE; - } - if (dinput != NULL) { IDirectInput8_Release(dinput); dinput = NULL; @@ -996,7 +534,6 @@ SDL_SYS_HapticQuit(void) } } - /* * Converts an SDL trigger button to an DIEFFECT trigger button. */ @@ -1120,8 +657,7 @@ SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest, dest->rgdwAxes = axes; } - - /* The big type handling switch, even bigger then Linux's version. */ + /* The big type handling switch, even bigger than Linux's version. */ switch (src->type) { case SDL_HAPTIC_CONSTANT: hap_constant = &src->constant; @@ -1143,8 +679,7 @@ SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest, dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */ /* Direction. */ - if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes) - < 0) { + if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes) < 0) { return -1; } @@ -1319,8 +854,7 @@ SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest, dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */ /* Direction. */ - if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) < - 0) { + if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) < 0) { return -1; } @@ -1338,7 +872,6 @@ SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest, break; - default: return SDL_SetError("Haptic: Unknown effect type."); } @@ -1372,7 +905,6 @@ SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type) effect->rglDirection = NULL; } - /* * Gets the effect type from the generic SDL haptic effect wrapper. */ @@ -1421,36 +953,15 @@ SDL_SYS_HapticEffectType(SDL_HapticEffect * effect) return NULL; } } - - -/* - * Creates a new haptic effect. - */ int -SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, - SDL_HapticEffect * base) +SDL_DINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base) { HRESULT ret; REFGUID type = SDL_SYS_HapticEffectType(base); - if ((type == NULL) && (!haptic->hwdata->bXInputHaptic)) { + if (type == NULL) { SDL_SetError("Haptic: Unknown effect type."); - goto err_hweffect; - } - - /* Alloc the effect. */ - effect->hweffect = (struct haptic_hweffect *) - SDL_malloc(sizeof(struct haptic_hweffect)); - if (effect->hweffect == NULL) { - SDL_OutOfMemory(); - goto err_hweffect; - } - - SDL_zerop(effect->hweffect); - - if (haptic->hwdata->bXInputHaptic) { - SDL_assert(base->type == SDL_HAPTIC_LEFTRIGHT); /* should catch this at higher level */ - return SDL_SYS_HapticUpdateEffect(haptic, effect, base); + return -1; } /* Get the effect. */ @@ -1460,8 +971,8 @@ SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, /* Create the actual effect. */ ret = IDirectInputDevice8_CreateEffect(haptic->hwdata->device, type, - &effect->hweffect->effect, - &effect->hweffect->ref, NULL); + &effect->hweffect->effect, + &effect->hweffect->ref, NULL); if (FAILED(ret)) { DI_SetError("Unable to create effect", ret); goto err_effectdone; @@ -1469,40 +980,18 @@ SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, return 0; - err_effectdone: +err_effectdone: SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, base->type); - err_hweffect: - SDL_free(effect->hweffect); - effect->hweffect = NULL; return -1; } - -/* - * Updates an effect. - */ int -SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, - struct haptic_effect *effect, - SDL_HapticEffect * data) +SDL_DINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data) { HRESULT ret; DWORD flags; DIEFFECT temp; - if (haptic->hwdata->bXInputHaptic) { - XINPUT_VIBRATION *vib = &effect->hweffect->vibration; - SDL_assert(data->type == SDL_HAPTIC_LEFTRIGHT); - vib->wLeftMotorSpeed = data->leftright.large_magnitude; - vib->wRightMotorSpeed = data->leftright.small_magnitude; - SDL_LockMutex(haptic->hwdata->mutex); - if (haptic->hwdata->stopTicks) { /* running right now? Update it. */ - XINPUTSETSTATE(haptic->hwdata->userid, vib); - } - SDL_UnlockMutex(haptic->hwdata->mutex); - return 0; - } - /* Get the effect. */ SDL_memset(&temp, 0, sizeof(DIEFFECT)); if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) { @@ -1510,7 +999,7 @@ SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, } /* Set the flags. Might be worthwhile to diff temp with loaded effect and - * only change those parameters. */ + * only change those parameters. */ flags = DIEP_DIRECTION | DIEP_DURATION | DIEP_ENVELOPE | @@ -1532,110 +1021,58 @@ SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, return 0; - err_update: +err_update: SDL_SYS_HapticFreeDIEFFECT(&temp, data->type); return -1; } - -/* - * Runs an effect. - */ int -SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, - Uint32 iterations) +SDL_DINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations) { HRESULT ret; DWORD iter; - if (haptic->hwdata->bXInputHaptic) { - XINPUT_VIBRATION *vib = &effect->hweffect->vibration; - SDL_assert(effect->effect.type == SDL_HAPTIC_LEFTRIGHT); /* should catch this at higher level */ - SDL_LockMutex(haptic->hwdata->mutex); - if(effect->effect.leftright.length == SDL_HAPTIC_INFINITY || iterations == SDL_HAPTIC_INFINITY) { - haptic->hwdata->stopTicks = SDL_HAPTIC_INFINITY; - } else if ((!effect->effect.leftright.length) || (!iterations)) { - /* do nothing. Effect runs for zero milliseconds. */ - } else { - haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations); - if ((haptic->hwdata->stopTicks == SDL_HAPTIC_INFINITY) || (haptic->hwdata->stopTicks == 0)) { - haptic->hwdata->stopTicks = 1; /* fix edge cases. */ - } - } - SDL_UnlockMutex(haptic->hwdata->mutex); - return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1; - } - /* Check if it's infinite. */ if (iterations == SDL_HAPTIC_INFINITY) { iter = INFINITE; - } else + } else { iter = iterations; + } /* Run the effect. */ ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0); if (FAILED(ret)) { return DI_SetError("Running the effect", ret); } - return 0; } - -/* - * Stops an effect. - */ int -SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +SDL_DINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) { HRESULT ret; - if (haptic->hwdata->bXInputHaptic) { - XINPUT_VIBRATION vibration = { 0, 0 }; - SDL_LockMutex(haptic->hwdata->mutex); - haptic->hwdata->stopTicks = 0; - SDL_UnlockMutex(haptic->hwdata->mutex); - return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1; - } - ret = IDirectInputEffect_Stop(effect->hweffect->ref); if (FAILED(ret)) { return DI_SetError("Unable to stop effect", ret); } - return 0; } - -/* - * Frees the effect. - */ void -SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +SDL_DINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) { HRESULT ret; - if (haptic->hwdata->bXInputHaptic) { - SDL_SYS_HapticStopEffect(haptic, effect); - } else { - ret = IDirectInputEffect_Unload(effect->hweffect->ref); - if (FAILED(ret)) { - DI_SetError("Removing effect from the device", ret); - } - SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, - effect->effect.type); + ret = IDirectInputEffect_Unload(effect->hweffect->ref); + if (FAILED(ret)) { + DI_SetError("Removing effect from the device", ret); } - SDL_free(effect->hweffect); - effect->hweffect = NULL; + SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, effect->effect.type); } - -/* - * Gets the status of a haptic effect. - */ int -SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, - struct haptic_effect *effect) +SDL_DINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect) { HRESULT ret; DWORD status; @@ -1650,12 +1087,8 @@ SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, return SDL_TRUE; } - -/* - * Sets the gain. - */ int -SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain) +SDL_DINPUT_HapticSetGain(SDL_Haptic * haptic, int gain) { HRESULT ret; DIPROPDWORD dipdw; @@ -1669,20 +1102,15 @@ SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain) /* Try to set the autocenter. */ ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device, - DIPROP_FFGAIN, &dipdw.diph); + DIPROP_FFGAIN, &dipdw.diph); if (FAILED(ret)) { return DI_SetError("Setting gain", ret); } - return 0; } - -/* - * Sets the autocentering. - */ int -SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +SDL_DINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) { HRESULT ret; DIPROPDWORD dipdw; @@ -1697,114 +1125,167 @@ SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) /* Try to set the autocenter. */ ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device, - DIPROP_AUTOCENTER, &dipdw.diph); + DIPROP_AUTOCENTER, &dipdw.diph); if (FAILED(ret)) { return DI_SetError("Setting autocenter", ret); } - return 0; } - -/* - * Pauses the device. - */ int -SDL_SYS_HapticPause(SDL_Haptic * haptic) +SDL_DINPUT_HapticPause(SDL_Haptic * haptic) { HRESULT ret; /* Pause the device. */ ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, - DISFFC_PAUSE); + DISFFC_PAUSE); if (FAILED(ret)) { return DI_SetError("Pausing the device", ret); } - return 0; } - -/* - * Pauses the device. - */ int -SDL_SYS_HapticUnpause(SDL_Haptic * haptic) +SDL_DINPUT_HapticUnpause(SDL_Haptic * haptic) { HRESULT ret; /* Unpause the device. */ ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, - DISFFC_CONTINUE); + DISFFC_CONTINUE); if (FAILED(ret)) { return DI_SetError("Pausing the device", ret); } - return 0; } - -/* - * Stops all the playing effects on the device. - */ int -SDL_SYS_HapticStopAll(SDL_Haptic * haptic) +SDL_DINPUT_HapticStopAll(SDL_Haptic * haptic) { HRESULT ret; - if (haptic->hwdata->bXInputHaptic) { - XINPUT_VIBRATION vibration = { 0, 0 }; - SDL_LockMutex(haptic->hwdata->mutex); - haptic->hwdata->stopTicks = 0; - SDL_UnlockMutex(haptic->hwdata->mutex); - return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1; - } - /* Try to stop the effects. */ ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device, - DISFFC_STOPALL); + DISFFC_STOPALL); if (FAILED(ret)) { return DI_SetError("Stopping the device", ret); } + return 0; +} + +#else /* !SDL_HAPTIC_DINPUT */ + +int +SDL_DINPUT_HapticInit(void) +{ return 0; } +int +SDL_DINPUT_MaybeAddDevice(const DIDEVICEINSTANCE * pdidInstance) +{ + return SDL_Unsupported(); +} -/* !!! FIXME: this is a hack, remove this later. */ -/* Since XInput doesn't offer a way to vibrate for X time, we hook into - * SDL_PumpEvents() to check if it's time to stop vibrating with some - * frequency. - * In practice, this works for 99% of use cases. But in an ideal world, - * we do this in a separate thread so that: - * - we aren't bound to when the app chooses to pump the event queue. - * - we aren't adding more polling to the event queue - * - we can emulate all the haptic effects correctly (start on a delay, - * mix multiple effects, etc). - * - * Mostly, this is here to get rumbling to work, and all the other features - * are absent in the XInput path for now. :( - */ -static int SDLCALL -SDL_RunXInputHaptic(void *arg) +int +SDL_DINPUT_MaybeRemoveDevice(const DIDEVICEINSTANCE * pdidInstance) { - struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg; - - while (!hwdata->stopThread) { - SDL_Delay(50); - SDL_LockMutex(hwdata->mutex); - /* If we're currently running and need to stop... */ - if (hwdata->stopTicks) { - if ((hwdata->stopTicks != SDL_HAPTIC_INFINITY) && SDL_TICKS_PASSED(SDL_GetTicks(), hwdata->stopTicks)) { - XINPUT_VIBRATION vibration = { 0, 0 }; - hwdata->stopTicks = 0; - XINPUTSETSTATE(hwdata->userid, &vibration); - } - } - SDL_UnlockMutex(hwdata->mutex); - } + return SDL_Unsupported(); +} - return 0; +int +SDL_DINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + return SDL_Unsupported(); +} + +void +SDL_DINPUT_HapticClose(SDL_Haptic * haptic) +{ +} + +void +SDL_DINPUT_HapticQuit(void) +{ +} + +int +SDL_DINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + return SDL_Unsupported(); +} + +void +SDL_DINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ +} + +int +SDL_DINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticPause(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticUnpause(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); +} + +int +SDL_DINPUT_HapticStopAll(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); } #endif /* SDL_HAPTIC_DINPUT */ diff --git a/src/haptic/windows/SDL_dinputhaptic_c.h b/src/haptic/windows/SDL_dinputhaptic_c.h new file mode 100644 index 0000000000000..9801fe272bce1 --- /dev/null +++ b/src/haptic/windows/SDL_dinputhaptic_c.h @@ -0,0 +1,47 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2014 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#include "SDL_haptic.h" +#include "SDL_windowshaptic_c.h" + + +extern int SDL_DINPUT_HapticInit(void); +extern int SDL_DINPUT_MaybeAddDevice(const DIDEVICEINSTANCE *pdidInstance); +extern int SDL_DINPUT_MaybeRemoveDevice(const DIDEVICEINSTANCE *pdidInstance); +extern int SDL_DINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item); +extern int SDL_DINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick); +extern int SDL_DINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick); +extern void SDL_DINPUT_HapticClose(SDL_Haptic * haptic); +extern void SDL_DINPUT_HapticQuit(void); +extern int SDL_DINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base); +extern int SDL_DINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data); +extern int SDL_DINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations); +extern int SDL_DINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect); +extern void SDL_DINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect); +extern int SDL_DINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect); +extern int SDL_DINPUT_HapticSetGain(SDL_Haptic * haptic, int gain); +extern int SDL_DINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter); +extern int SDL_DINPUT_HapticPause(SDL_Haptic * haptic); +extern int SDL_DINPUT_HapticUnpause(SDL_Haptic * haptic); +extern int SDL_DINPUT_HapticStopAll(SDL_Haptic * haptic); + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/haptic/windows/SDL_windowshaptic.c b/src/haptic/windows/SDL_windowshaptic.c new file mode 100644 index 0000000000000..26095dc5806fa --- /dev/null +++ b/src/haptic/windows/SDL_windowshaptic.c @@ -0,0 +1,445 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2014 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_HAPTIC_DINPUT || SDL_HAPTIC_XINPUT + +#include "SDL_assert.h" +#include "SDL_thread.h" +#include "SDL_mutex.h" +#include "SDL_timer.h" +#include "SDL_hints.h" +#include "SDL_haptic.h" +#include "../SDL_syshaptic.h" +#include "SDL_joystick.h" +#include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */ +#include "../../joystick/windows/SDL_windowsjoystick_c.h" /* For joystick hwdata */ +#include "../../joystick/windows/SDL_xinputjoystick_c.h" /* For xinput rumble */ + +#include "SDL_windowshaptic_c.h" +#include "SDL_dinputhaptic_c.h" +#include "SDL_xinputhaptic_c.h" + + +/* + * Internal stuff. + */ +SDL_hapticlist_item *SDL_hapticlist = NULL; +static SDL_hapticlist_item *SDL_hapticlist_tail = NULL; +static int numhaptics = 0; + + +/* + * Initializes the haptic subsystem. + */ +int +SDL_SYS_HapticInit(void) +{ + if (SDL_DINPUT_HapticInit() < 0) { + return -1; + } + if (SDL_XINPUT_HapticInit() < 0) { + return -1; + } + return numhaptics; +} + +int +SDL_SYS_AddHapticDevice(SDL_hapticlist_item *item) +{ + if (SDL_hapticlist_tail == NULL) { + SDL_hapticlist = SDL_hapticlist_tail = item; + } else { + SDL_hapticlist_tail->next = item; + SDL_hapticlist_tail = item; + } + + /* Device has been added. */ + ++numhaptics; + + return numhaptics; +} + +int +SDL_SYS_RemoveHapticDevice(SDL_hapticlist_item *prev, SDL_hapticlist_item *item) +{ + const int retval = item->haptic ? item->haptic->index : -1; + if (prev != NULL) { + prev->next = item->next; + } else { + SDL_assert(SDL_hapticlist == item); + SDL_hapticlist = item->next; + } + if (item == SDL_hapticlist_tail) { + SDL_hapticlist_tail = prev; + } + --numhaptics; + /* !!! TODO: Send a haptic remove event? */ + SDL_free(item); + return retval; +} + +int +SDL_SYS_NumHaptics() +{ + return numhaptics; +} + +static SDL_hapticlist_item * +HapticByDevIndex(int device_index) +{ + SDL_hapticlist_item *item = SDL_hapticlist; + + if ((device_index < 0) || (device_index >= numhaptics)) { + return NULL; + } + + while (device_index > 0) { + SDL_assert(item != NULL); + --device_index; + item = item->next; + } + return item; +} + +/* + * Return the name of a haptic device, does not need to be opened. + */ +const char * +SDL_SYS_HapticName(int index) +{ + SDL_hapticlist_item *item = HapticByDevIndex(index); + return item->name; +} + +/* + * Opens a haptic device for usage. + */ +int +SDL_SYS_HapticOpen(SDL_Haptic * haptic) +{ + SDL_hapticlist_item *item = HapticByDevIndex(haptic->index); + if (item->bXInputHaptic) { + return SDL_XINPUT_HapticOpen(haptic, item); + } else { + return SDL_DINPUT_HapticOpen(haptic, item); + } +} + + +/* + * Opens a haptic device from first mouse it finds for usage. + */ +int +SDL_SYS_HapticMouse(void) +{ +#if SDL_HAPTIC_DINPUT + SDL_hapticlist_item *item; + int index = 0; + + /* Grab the first mouse haptic device we find. */ + for (item = SDL_hapticlist; item != NULL; item = item->next) { + if (item->capabilities.dwDevType == DI8DEVCLASS_POINTER ) { + return index; + } + ++index; + } +#endif /* SDL_HAPTIC_DINPUT */ + return -1; +} + + +/* + * Checks to see if a joystick has haptic features. + */ +int +SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick) +{ + const struct joystick_hwdata *hwdata = joystick->hwdata; +#if SDL_HAPTIC_XINPUT + if (hwdata->bXInputHaptic) { + return 1; + } +#endif +#if SDL_HAPTIC_DINPUT + if (hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) { + return 1; + } +#endif + return 0; +} + +/* + * Checks to see if the haptic device and joystick are in reality the same. + */ +int +SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + if (joystick->hwdata->bXInputHaptic != haptic->hwdata->bXInputHaptic) { + return 0; /* one is XInput, one is not; not the same device. */ + } else if (joystick->hwdata->bXInputHaptic) { + return SDL_XINPUT_JoystickSameHaptic(haptic, joystick); + } else { + return SDL_DINPUT_JoystickSameHaptic(haptic, joystick); + } +} + +/* + * Opens a SDL_Haptic from a SDL_Joystick. + */ +int +SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + if (joystick->hwdata->bXInputDevice) { + return SDL_XINPUT_HapticOpenFromJoystick(haptic, joystick); + } else { + return SDL_DINPUT_HapticOpenFromJoystick(haptic, joystick); + } +} + +/* + * Closes the haptic device. + */ +void +SDL_SYS_HapticClose(SDL_Haptic * haptic) +{ + if (haptic->hwdata) { + + /* Free effects. */ + SDL_free(haptic->effects); + haptic->effects = NULL; + haptic->neffects = 0; + + /* Clean up */ + if (haptic->hwdata->bXInputHaptic) { + SDL_XINPUT_HapticClose(haptic); + } else { + SDL_DINPUT_HapticClose(haptic); + } + + /* Free */ + SDL_free(haptic->hwdata); + haptic->hwdata = NULL; + } +} + +/* + * Clean up after system specific haptic stuff + */ +void +SDL_SYS_HapticQuit(void) +{ + SDL_hapticlist_item *item; + SDL_hapticlist_item *next = NULL; + SDL_Haptic *hapticitem = NULL; + + extern SDL_Haptic *SDL_haptics; + for (hapticitem = SDL_haptics; hapticitem; hapticitem = hapticitem->next) { + if ((hapticitem->hwdata->bXInputHaptic) && (hapticitem->hwdata->thread)) { + /* we _have_ to stop the thread before we free the XInput DLL! */ + hapticitem->hwdata->stopThread = 1; + SDL_WaitThread(hapticitem->hwdata->thread, NULL); + hapticitem->hwdata->thread = NULL; + } + } + + for (item = SDL_hapticlist; item; item = next) { + /* Opened and not closed haptics are leaked, this is on purpose. + * Close your haptic devices after usage. */ + /* !!! FIXME: (...is leaking on purpose a good idea?) - No, of course not. */ + next = item->next; + SDL_free(item->name); + SDL_free(item); + } + + SDL_XINPUT_HapticQuit(); + SDL_DINPUT_HapticQuit(); +} + +/* + * Creates a new haptic effect. + */ +int +SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + SDL_HapticEffect * base) +{ + int result; + + /* Alloc the effect. */ + effect->hweffect = (struct haptic_hweffect *) + SDL_malloc(sizeof(struct haptic_hweffect)); + if (effect->hweffect == NULL) { + SDL_OutOfMemory(); + return -1; + } + SDL_zerop(effect->hweffect); + + if (haptic->hwdata->bXInputHaptic) { + result = SDL_XINPUT_HapticNewEffect(haptic, effect, base); + } else { + result = SDL_DINPUT_HapticNewEffect(haptic, effect, base); + } + if (result < 0) { + SDL_free(effect->hweffect); + effect->hweffect = NULL; + } + return result; +} + +/* + * Updates an effect. + */ +int +SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, + struct haptic_effect *effect, + SDL_HapticEffect * data) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticUpdateEffect(haptic, effect, data); + } else { + return SDL_DINPUT_HapticUpdateEffect(haptic, effect, data); + } +} + +/* + * Runs an effect. + */ +int +SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, + Uint32 iterations) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticRunEffect(haptic, effect, iterations); + } else { + return SDL_DINPUT_HapticRunEffect(haptic, effect, iterations); + } +} + +/* + * Stops an effect. + */ +int +SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticStopEffect(haptic, effect); + } else { + return SDL_DINPUT_HapticStopEffect(haptic, effect); + } +} + +/* + * Frees the effect. + */ +void +SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + if (haptic->hwdata->bXInputHaptic) { + SDL_XINPUT_HapticDestroyEffect(haptic, effect); + } else { + SDL_DINPUT_HapticDestroyEffect(haptic, effect); + } + SDL_free(effect->hweffect); + effect->hweffect = NULL; +} + +/* + * Gets the status of a haptic effect. + */ +int +SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, + struct haptic_effect *effect) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticGetEffectStatus(haptic, effect); + } else { + return SDL_DINPUT_HapticGetEffectStatus(haptic, effect); + } +} + +/* + * Sets the gain. + */ +int +SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticSetGain(haptic, gain); + } else { + return SDL_DINPUT_HapticSetGain(haptic, gain); + } +} + +/* + * Sets the autocentering. + */ +int +SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticSetAutocenter(haptic, autocenter); + } else { + return SDL_DINPUT_HapticSetAutocenter(haptic, autocenter); + } +} + +/* + * Pauses the device. + */ +int +SDL_SYS_HapticPause(SDL_Haptic * haptic) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticPause(haptic); + } else { + return SDL_DINPUT_HapticPause(haptic); + } +} + +/* + * Pauses the device. + */ +int +SDL_SYS_HapticUnpause(SDL_Haptic * haptic) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticUnpause(haptic); + } else { + return SDL_DINPUT_HapticUnpause(haptic); + } +} + +/* + * Stops all the playing effects on the device. + */ +int +SDL_SYS_HapticStopAll(SDL_Haptic * haptic) +{ + if (haptic->hwdata->bXInputHaptic) { + return SDL_XINPUT_HapticStopAll(haptic); + } else { + return SDL_DINPUT_HapticStopAll(haptic); + } +} + +#endif /* SDL_HAPTIC_DINPUT || SDL_HAPTIC_XINPUT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/haptic/windows/SDL_windowshaptic_c.h b/src/haptic/windows/SDL_windowshaptic_c.h new file mode 100644 index 0000000000000..a978c24485508 --- /dev/null +++ b/src/haptic/windows/SDL_windowshaptic_c.h @@ -0,0 +1,88 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2014 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifndef _SDL_windowshaptic_c_h +#define _SDL_windowshaptic_c_h + +#include "SDL_thread.h" + +#include "../../core/windows/SDL_directx.h" +#include "../../core/windows/SDL_xinput.h" + +/* + * Haptic system hardware data. + */ +struct haptic_hwdata +{ +#if SDL_HAPTIC_DINPUT + LPDIRECTINPUTDEVICE8 device; +#endif + DWORD axes[3]; /* Axes to use. */ + SDL_bool is_joystick; /* Device is loaded as joystick. */ + Uint8 bXInputHaptic; /* Supports force feedback via XInput. */ + Uint8 userid; /* XInput userid index for this joystick */ + SDL_Thread *thread; + SDL_mutex *mutex; + volatile Uint32 stopTicks; + volatile int stopThread; +}; + + +/* + * Haptic system effect data. + */ +struct haptic_hweffect +{ +#if SDL_HAPTIC_DINPUT + DIEFFECT effect; + LPDIRECTINPUTEFFECT ref; +#endif +#if SDL_HAPTIC_XINPUT + XINPUT_VIBRATION vibration; +#endif +}; + +/* +* List of available haptic devices. +*/ +typedef struct SDL_hapticlist_item +{ + char *name; + SDL_Haptic *haptic; +#if SDL_HAPTIC_DINPUT + DIDEVICEINSTANCE instance; + DIDEVCAPS capabilities; +#endif + SDL_bool bXInputHaptic; /* Supports force feedback via XInput. */ + Uint8 userid; /* XInput userid index for this joystick */ + struct SDL_hapticlist_item *next; +} SDL_hapticlist_item; + +extern SDL_hapticlist_item *SDL_hapticlist; + +extern int SDL_SYS_AddHapticDevice(SDL_hapticlist_item *item); +extern int SDL_SYS_RemoveHapticDevice(SDL_hapticlist_item *prev, SDL_hapticlist_item *item); + +#endif /* _SDL_windowshaptic_c_h */ + +/* vi: set ts=4 sw=4 expandtab: */ + diff --git a/src/haptic/windows/SDL_xinputhaptic.c b/src/haptic/windows/SDL_xinputhaptic.c new file mode 100644 index 0000000000000..2e76a9592bf3c --- /dev/null +++ b/src/haptic/windows/SDL_xinputhaptic.c @@ -0,0 +1,491 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2014 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#include "SDL_assert.h" +#include "SDL_error.h" +#include "SDL_haptic.h" +#include "SDL_hints.h" +#include "SDL_timer.h" +#include "SDL_windowshaptic_c.h" +#include "SDL_xinputhaptic_c.h" +#include "../SDL_syshaptic.h" +#include "../../core/windows/SDL_xinput.h" +#include "../../joystick/windows/SDL_windowsjoystick_c.h" + + +#if SDL_HAPTIC_XINPUT + +/* + * Internal stuff. + */ +static SDL_bool loaded_xinput = SDL_FALSE; + + +int +SDL_XINPUT_HapticInit(void) +{ + const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED); + if (!env || SDL_atoi(env)) { + loaded_xinput = (WIN_LoadXInputDLL() == 0); + } + + if (loaded_xinput) { + DWORD i; + for (i = 0; i < XUSER_MAX_COUNT; i++) { + SDL_XINPUT_MaybeAddDevice(i); + } + } + return 0; +} + +int +SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid) +{ + const Uint8 userid = (Uint8)dwUserid; + SDL_hapticlist_item *item; + XINPUT_VIBRATION state; + + if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) { + return -1; + } + + /* Make sure we don't already have it */ + for (item = SDL_hapticlist; item; item = item->next) { + if (item->bXInputHaptic && item->userid == userid) { + return -1; /* Already added */ + } + } + + SDL_zero(state); + if (XINPUTSETSTATE(dwUserid, &state) != ERROR_SUCCESS) { + return -1; /* no force feedback on this device. */ + } + + item = (SDL_hapticlist_item *)SDL_malloc(sizeof(SDL_hapticlist_item)); + if (item == NULL) { + return SDL_OutOfMemory(); + } + + SDL_zerop(item); + + /* !!! FIXME: I'm not bothering to query for a real name right now (can we even?) */ + { + char buf[64]; + SDL_snprintf(buf, sizeof(buf), "XInput Controller #%u", (unsigned int)(userid + 1)); + item->name = SDL_strdup(buf); + } + + if (!item->name) { + SDL_free(item); + return -1; + } + + /* Copy the instance over, useful for creating devices. */ + item->bXInputHaptic = SDL_TRUE; + item->userid = userid; + + return SDL_SYS_AddHapticDevice(item); +} + +int +SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid) +{ + const Uint8 userid = (Uint8)dwUserid; + SDL_hapticlist_item *item; + SDL_hapticlist_item *prev = NULL; + + if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) { + return -1; + } + + for (item = SDL_hapticlist; item != NULL; item = item->next) { + if (item->bXInputHaptic && item->userid == userid) { + /* found it, remove it. */ + return SDL_SYS_RemoveHapticDevice(prev, item); + } + prev = item; + } + return -1; +} + +/* !!! FIXME: this is a hack, remove this later. */ +/* Since XInput doesn't offer a way to vibrate for X time, we hook into + * SDL_PumpEvents() to check if it's time to stop vibrating with some + * frequency. + * In practice, this works for 99% of use cases. But in an ideal world, + * we do this in a separate thread so that: + * - we aren't bound to when the app chooses to pump the event queue. + * - we aren't adding more polling to the event queue + * - we can emulate all the haptic effects correctly (start on a delay, + * mix multiple effects, etc). + * + * Mostly, this is here to get rumbling to work, and all the other features + * are absent in the XInput path for now. :( + */ +static int SDLCALL +SDL_RunXInputHaptic(void *arg) +{ + struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg; + + while (!hwdata->stopThread) { + SDL_Delay(50); + SDL_LockMutex(hwdata->mutex); + /* If we're currently running and need to stop... */ + if (hwdata->stopTicks) { + if ((hwdata->stopTicks != SDL_HAPTIC_INFINITY) && SDL_TICKS_PASSED(SDL_GetTicks(), hwdata->stopTicks)) { + XINPUT_VIBRATION vibration = { 0, 0 }; + hwdata->stopTicks = 0; + XINPUTSETSTATE(hwdata->userid, &vibration); + } + } + SDL_UnlockMutex(hwdata->mutex); + } + + return 0; +} + +static int +SDL_XINPUT_HapticOpenFromUserIndex(SDL_Haptic *haptic, const Uint8 userid) +{ + char threadName[32]; + XINPUT_VIBRATION vibration = { 0, 0 }; /* stop any current vibration */ + XINPUTSETSTATE(userid, &vibration); + + haptic->supported = SDL_HAPTIC_LEFTRIGHT; + + haptic->neffects = 1; + haptic->nplaying = 1; + + /* Prepare effects memory. */ + haptic->effects = (struct haptic_effect *) + SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); + if (haptic->effects == NULL) { + return SDL_OutOfMemory(); + } + /* Clear the memory */ + SDL_memset(haptic->effects, 0, + sizeof(struct haptic_effect) * haptic->neffects); + + haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata)); + if (haptic->hwdata == NULL) { + SDL_free(haptic->effects); + haptic->effects = NULL; + return SDL_OutOfMemory(); + } + SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); + + haptic->hwdata->bXInputHaptic = 1; + haptic->hwdata->userid = userid; + + haptic->hwdata->mutex = SDL_CreateMutex(); + if (haptic->hwdata->mutex == NULL) { + SDL_free(haptic->effects); + SDL_free(haptic->hwdata); + haptic->effects = NULL; + return SDL_SetError("Couldn't create XInput haptic mutex"); + } + + SDL_snprintf(threadName, sizeof(threadName), "SDLXInputDev%d", (int)userid); + +#if defined(__WIN32__) && !defined(HAVE_LIBC) /* !!! FIXME: this is nasty. */ +#undef SDL_CreateThread +#if SDL_DYNAMIC_API + haptic->hwdata->thread = SDL_CreateThread_REAL(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL); +#else + haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL); +#endif +#else + haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata); +#endif + if (haptic->hwdata->thread == NULL) { + SDL_DestroyMutex(haptic->hwdata->mutex); + SDL_free(haptic->effects); + SDL_free(haptic->hwdata); + haptic->effects = NULL; + return SDL_SetError("Couldn't create XInput haptic thread"); + } + + return 0; +} + +int +SDL_XINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item) +{ + return SDL_XINPUT_HapticOpenFromUserIndex(haptic, item->userid); +} + +int +SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + return (haptic->hwdata->userid == joystick->hwdata->userid); +} + +int +SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + SDL_hapticlist_item *item; + int index = 0; + + /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */ + for (item = SDL_hapticlist; item != NULL; item = item->next) { + if (item->bXInputHaptic && item->userid == joystick->hwdata->userid) { + haptic->index = index; + return SDL_XINPUT_HapticOpenFromUserIndex(haptic, joystick->hwdata->userid); + } + ++index; + } + + SDL_SetError("Couldn't find joystick in haptic device list"); + return -1; +} + +void +SDL_XINPUT_HapticClose(SDL_Haptic * haptic) +{ + haptic->hwdata->stopThread = 1; + SDL_WaitThread(haptic->hwdata->thread, NULL); + SDL_DestroyMutex(haptic->hwdata->mutex); +} + +void +SDL_XINPUT_HapticQuit(void) +{ + if (loaded_xinput) { + WIN_UnloadXInputDLL(); + loaded_xinput = SDL_FALSE; + } +} + +int +SDL_XINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base) +{ + SDL_assert(base->type == SDL_HAPTIC_LEFTRIGHT); /* should catch this at higher level */ + return SDL_XINPUT_HapticUpdateEffect(haptic, effect, base); +} + +int +SDL_XINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data) +{ + XINPUT_VIBRATION *vib = &effect->hweffect->vibration; + SDL_assert(data->type == SDL_HAPTIC_LEFTRIGHT); + vib->wLeftMotorSpeed = data->leftright.large_magnitude; + vib->wRightMotorSpeed = data->leftright.small_magnitude; + SDL_LockMutex(haptic->hwdata->mutex); + if (haptic->hwdata->stopTicks) { /* running right now? Update it. */ + XINPUTSETSTATE(haptic->hwdata->userid, vib); + } + SDL_UnlockMutex(haptic->hwdata->mutex); + return 0; +} + +int +SDL_XINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations) +{ + XINPUT_VIBRATION *vib = &effect->hweffect->vibration; + SDL_assert(effect->effect.type == SDL_HAPTIC_LEFTRIGHT); /* should catch this at higher level */ + SDL_LockMutex(haptic->hwdata->mutex); + if (effect->effect.leftright.length == SDL_HAPTIC_INFINITY || iterations == SDL_HAPTIC_INFINITY) { + haptic->hwdata->stopTicks = SDL_HAPTIC_INFINITY; + } else if ((!effect->effect.leftright.length) || (!iterations)) { + /* do nothing. Effect runs for zero milliseconds. */ + } else { + haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations); + if ((haptic->hwdata->stopTicks == SDL_HAPTIC_INFINITY) || (haptic->hwdata->stopTicks == 0)) { + haptic->hwdata->stopTicks = 1; /* fix edge cases. */ + } + } + SDL_UnlockMutex(haptic->hwdata->mutex); + return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1; +} + +int +SDL_XINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + XINPUT_VIBRATION vibration = { 0, 0 }; + SDL_LockMutex(haptic->hwdata->mutex); + haptic->hwdata->stopTicks = 0; + SDL_UnlockMutex(haptic->hwdata->mutex); + return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1; +} + +void +SDL_XINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + SDL_XINPUT_HapticStopEffect(haptic, effect); +} + +int +SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticPause(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticUnpause(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticStopAll(SDL_Haptic * haptic) +{ + XINPUT_VIBRATION vibration = { 0, 0 }; + SDL_LockMutex(haptic->hwdata->mutex); + haptic->hwdata->stopTicks = 0; + SDL_UnlockMutex(haptic->hwdata->mutex); + return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1; +} + +#else /* !SDL_HAPTIC_XINPUT */ + + +int +SDL_XINPUT_HapticInit(void) +{ + return 0; +} + +int +SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) +{ + return SDL_Unsupported(); +} + +void +SDL_XINPUT_HapticClose(SDL_Haptic * haptic) +{ +} + +void +SDL_XINPUT_HapticQuit(void) +{ +} + +int +SDL_XINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + return SDL_Unsupported(); +} + +void +SDL_XINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) +{ +} + +int +SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticPause(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticUnpause(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); +} + +int +SDL_XINPUT_HapticStopAll(SDL_Haptic * haptic) +{ + return SDL_Unsupported(); +} + +#endif /* SDL_HAPTIC_XINPUT */ +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/haptic/windows/SDL_xinputhaptic_c.h b/src/haptic/windows/SDL_xinputhaptic_c.h new file mode 100644 index 0000000000000..c3062b6ea00f8 --- /dev/null +++ b/src/haptic/windows/SDL_xinputhaptic_c.h @@ -0,0 +1,47 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2014 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#include "SDL_haptic.h" +#include "SDL_windowshaptic_c.h" + + +extern int SDL_XINPUT_HapticInit(void); +extern int SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid); +extern int SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid); +extern int SDL_XINPUT_HapticOpen(SDL_Haptic * haptic, SDL_hapticlist_item *item); +extern int SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick); +extern int SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick); +extern void SDL_XINPUT_HapticClose(SDL_Haptic * haptic); +extern void SDL_XINPUT_HapticQuit(void); +extern int SDL_XINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base); +extern int SDL_XINPUT_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data); +extern int SDL_XINPUT_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations); +extern int SDL_XINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect); +extern void SDL_XINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect); +extern int SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect); +extern int SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain); +extern int SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter); +extern int SDL_XINPUT_HapticPause(SDL_Haptic * haptic); +extern int SDL_XINPUT_HapticUnpause(SDL_Haptic * haptic); +extern int SDL_XINPUT_HapticStopAll(SDL_Haptic * haptic); + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c index 950cb3601015e..d53acdfb97da3 100644 --- a/src/joystick/SDL_gamecontroller.c +++ b/src/joystick/SDL_gamecontroller.c @@ -88,9 +88,7 @@ typedef struct _ControllerMapping_t } ControllerMapping_t; static ControllerMapping_t *s_pSupportedControllers = NULL; -#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) static ControllerMapping_t *s_pXInputMapping = NULL; -#endif /* The SDL game controller structure */ struct _SDL_GameController @@ -260,12 +258,10 @@ ControllerMapping_t *SDL_PrivateGetControllerMappingForGUID(SDL_JoystickGUID *gu */ ControllerMapping_t *SDL_PrivateGetControllerMapping(int device_index) { -#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) if (SDL_SYS_IsXInputGamepad_DeviceIndex(device_index) && s_pXInputMapping) { return s_pXInputMapping; } else -#endif { SDL_JoystickGUID jGUID = SDL_JoystickGetDeviceGUID(device_index); return SDL_PrivateGetControllerMappingForGUID(&jGUID); @@ -669,19 +665,15 @@ SDL_GameControllerAddMapping(const char *mappingString) char *pchMapping; SDL_JoystickGUID jGUID; ControllerMapping_t *pControllerMapping; -#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) SDL_bool is_xinput_mapping = SDL_FALSE; -#endif pchGUID = SDL_PrivateGetControllerGUIDFromMappingString(mappingString); if (!pchGUID) { return SDL_SetError("Couldn't parse GUID from %s", mappingString); } -#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) if (!SDL_strcasecmp(pchGUID, "xinput")) { is_xinput_mapping = SDL_TRUE; } -#endif jGUID = SDL_JoystickGetGUIDFromString(pchGUID); SDL_free(pchGUID); @@ -714,11 +706,9 @@ SDL_GameControllerAddMapping(const char *mappingString) SDL_free(pchMapping); return SDL_OutOfMemory(); } -#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) if (is_xinput_mapping) { s_pXInputMapping = pControllerMapping; } -#endif pControllerMapping->guid = jGUID; pControllerMapping->name = pchName; pControllerMapping->mapping = pchMapping; diff --git a/src/joystick/SDL_gamecontrollerdb.h b/src/joystick/SDL_gamecontrollerdb.h index 161594f924595..89837176c1bc1 100644 --- a/src/joystick/SDL_gamecontrollerdb.h +++ b/src/joystick/SDL_gamecontrollerdb.h @@ -31,7 +31,10 @@ */ static const char *s_ControllerMappings [] = { -#ifdef SDL_JOYSTICK_DINPUT +#if SDL_JOYSTICK_XINPUT + "xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,", +#endif +#if SDL_JOYSTICK_DINPUT "341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", "ffff0000000000000000504944564944,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,", "6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", @@ -41,10 +44,8 @@ static const char *s_ControllerMappings [] = "4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,", "25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,", "4c05c405000000000000504944564944,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,", - "xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,", -#elif defined(SDL_JOYSTICK_XINPUT) - "xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,", -#elif defined(__MACOSX__) +#endif +#if defined(__MACOSX__) "0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,", "6d0400000000000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", /* Guide button doesn't seem to be sent in DInput mode. */ "6d0400000000000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", @@ -53,7 +54,8 @@ static const char *s_ControllerMappings [] = "4c050000000000006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,", "4c05000000000000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,", "5e040000000000008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,", -#elif defined(__LINUX__) +#endif +#if defined(__LINUX__) "0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,", "03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,", "030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", @@ -70,7 +72,8 @@ static const char *s_ControllerMappings [] = "030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,", "030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,", "030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,", -#elif defined(__ANDROID__) +#endif +#if defined(__ANDROID__) "4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", #endif NULL diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h index d5ef3a5295435..84efe69aa879c 100644 --- a/src/joystick/SDL_sysjoystick.h +++ b/src/joystick/SDL_sysjoystick.h @@ -20,6 +20,9 @@ */ #include "../SDL_internal.h" +#ifndef _SDL_sysjoystick_h +#define _SDL_sysjoystick_h + /* This is the system specific header for the SDL joystick API */ #include "SDL_joystick.h" @@ -105,9 +108,9 @@ extern SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index); /* Function to return the stable GUID for a opened joystick */ extern SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick); -#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) /* Function returns SDL_TRUE if this device is an XInput gamepad */ -extern SDL_bool SDL_SYS_IsXInputGamepad_DeviceIndex( int device_index ); -#endif +extern SDL_bool SDL_SYS_IsXInputGamepad_DeviceIndex(int device_index); + +#endif /* _SDL_sysjoystick_h */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/windows/SDL_dinputjoystick.c b/src/joystick/windows/SDL_dinputjoystick.c new file mode 100644 index 0000000000000..c5816e99783e2 --- /dev/null +++ b/src/joystick/windows/SDL_dinputjoystick.c @@ -0,0 +1,900 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2014 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#include "../SDL_sysjoystick.h" +#include "SDL_windowsjoystick_c.h" +#include "SDL_dinputjoystick_c.h" +#include "SDL_xinputjoystick_c.h" + + +#if SDL_JOYSTICK_DINPUT + +#ifndef DIDFT_OPTIONAL +#define DIDFT_OPTIONAL 0x80000000 +#endif + +#define INPUT_QSIZE 32 /* Buffer up to 32 input messages */ +#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 */ + +/* external variables referenced. */ +extern HWND SDL_HelperWindow; + +/* local variables */ +static SDL_bool coinitialized = SDL_FALSE; +static LPDIRECTINPUT8 dinput = NULL; +static PRAWINPUTDEVICELIST SDL_RawDevList = NULL; +static UINT SDL_RawDevListCount = 0; + +/* Taken from Wine - Thanks! */ +static DIOBJECTDATAFORMAT dfDIJoystick2[] = { + { &GUID_XAxis, DIJOFS_X, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_YAxis, DIJOFS_Y, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_ZAxis, DIJOFS_Z, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RxAxis, DIJOFS_RX, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RyAxis, DIJOFS_RY, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RzAxis, DIJOFS_RZ, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_POV, DIJOFS_POV(0), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 }, + { &GUID_POV, DIJOFS_POV(1), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 }, + { &GUID_POV, DIJOFS_POV(2), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 }, + { &GUID_POV, DIJOFS_POV(3), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(0), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(1), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(2), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(3), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(4), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(5), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(6), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(7), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(8), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(9), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(10), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(11), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(12), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(13), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(14), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(15), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(16), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(17), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(18), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(19), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(20), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(21), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(22), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(23), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(24), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(25), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(26), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(27), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(28), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(29), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(30), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(31), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(32), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(33), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(34), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(35), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(36), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(37), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(38), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(39), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(40), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(41), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(42), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(43), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(44), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(45), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(46), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(47), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(48), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(49), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(50), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(51), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(52), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(53), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(54), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(55), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(56), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(57), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(58), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(59), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(60), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(61), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(62), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(63), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(64), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(65), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(66), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(67), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(68), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(69), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(70), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(71), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(72), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(73), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(74), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(75), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(76), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(77), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(78), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(79), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(80), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(81), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(82), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(83), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(84), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(85), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(86), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(87), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(88), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(89), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(90), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(91), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(92), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(93), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(94), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(95), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(96), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(97), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(98), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(99), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(100), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(101), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(102), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(103), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(104), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(105), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(106), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(107), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(108), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(109), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(110), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(111), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(112), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(113), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(114), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(115), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(116), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(117), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(118), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(119), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(120), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(121), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(122), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(123), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(124), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(125), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(126), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { NULL, DIJOFS_BUTTON(127), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 }, + { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lVX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lVY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lVZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lVRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lVRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lVRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lAX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lAY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lAZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lARx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lARy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lARz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lFX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lFY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lFZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lFRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lFRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lFRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, + { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 }, +}; + +const DIDATAFORMAT c_dfDIJoystick2 = { + sizeof(DIDATAFORMAT), + sizeof(DIOBJECTDATAFORMAT), + DIDF_ABSAXIS, + sizeof(DIJOYSTATE2), + SDL_arraysize(dfDIJoystick2), + dfDIJoystick2 +}; + +/* Convert a DirectInput return code to a text message */ +static int +SetDIerror(const char *function, HRESULT code) +{ + /* + return SDL_SetError("%s() [%s]: %s", function, + DXGetErrorString9A(code), DXGetErrorDescription9A(code)); + */ + return SDL_SetError("%s() DirectX error %d", function, code); +} + +static SDL_bool +SDL_IsXInputDevice(const GUID* pGuidProductFromDirectInput) +{ + static GUID IID_ValveStreamingGamepad = { MAKELONG(0x28DE, 0x11FF), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }; + static GUID IID_X360WiredGamepad = { MAKELONG(0x045E, 0x02A1), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }; + static GUID IID_X360WirelessGamepad = { MAKELONG(0x045E, 0x028E), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }; + + static const GUID *s_XInputProductGUID[] = { + &IID_ValveStreamingGamepad, + &IID_X360WiredGamepad, /* Microsoft's wired X360 controller for Windows. */ + &IID_X360WirelessGamepad /* Microsoft's wireless X360 controller for Windows. */ + }; + + size_t iDevice; + UINT i; + + if (!SDL_XINPUT_Enabled()) { + return SDL_FALSE; + } + + /* Check for well known XInput device GUIDs */ + /* This lets us skip RAWINPUT for popular devices. Also, we need to do this for the Valve Streaming Gamepad because it's virtualized and doesn't show up in the device list. */ + for (iDevice = 0; iDevice < SDL_arraysize(s_XInputProductGUID); ++iDevice) { + if (SDL_memcmp(pGuidProductFromDirectInput, s_XInputProductGUID[iDevice], sizeof(GUID)) == 0) { + return SDL_TRUE; + } + } + + /* Go through RAWINPUT (WinXP and later) to find HID devices. */ + /* Cache this if we end up using it. */ + if (SDL_RawDevList == NULL) { + if ((GetRawInputDeviceList(NULL, &SDL_RawDevListCount, sizeof(RAWINPUTDEVICELIST)) == -1) || (!SDL_RawDevListCount)) { + return SDL_FALSE; /* oh well. */ + } + + SDL_RawDevList = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * SDL_RawDevListCount); + if (SDL_RawDevList == NULL) { + SDL_OutOfMemory(); + return SDL_FALSE; + } + + if (GetRawInputDeviceList(SDL_RawDevList, &SDL_RawDevListCount, sizeof(RAWINPUTDEVICELIST)) == -1) { + SDL_free(SDL_RawDevList); + SDL_RawDevList = NULL; + return SDL_FALSE; /* oh well. */ + } + } + + for (i = 0; i < SDL_RawDevListCount; i++) { + RID_DEVICE_INFO rdi; + char devName[128]; + UINT rdiSize = sizeof(rdi); + UINT nameSize = SDL_arraysize(devName); + + rdi.cbSize = sizeof(rdi); + if ((SDL_RawDevList[i].dwType == RIM_TYPEHID) && + (GetRawInputDeviceInfoA(SDL_RawDevList[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != ((UINT)-1)) && + (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == ((LONG)pGuidProductFromDirectInput->Data1)) && + (GetRawInputDeviceInfoA(SDL_RawDevList[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != ((UINT)-1)) && + (SDL_strstr(devName, "IG_") != NULL)) { + return SDL_TRUE; + } + } + + return SDL_FALSE; +} + +int +SDL_DINPUT_JoystickInit(void) +{ + HRESULT result; + HINSTANCE instance; + + result = WIN_CoInitialize(); + if (FAILED(result)) { + return SetDIerror("CoInitialize", result); + } + + coinitialized = SDL_TRUE; + + result = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectInput8, (LPVOID)&dinput); + + if (FAILED(result)) { + return SetDIerror("CoCreateInstance", result); + } + + /* Because we used CoCreateInstance, we need to Initialize it, first. */ + instance = GetModuleHandle(NULL); + if (instance == NULL) { + return SDL_SetError("GetModuleHandle() failed with error code %d.", GetLastError()); + } + result = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION); + + if (FAILED(result)) { + return SetDIerror("IDirectInput::Initialize", result); + } + return 0; +} + +/* helper function for direct input, gets called for each connected joystick */ +static BOOL CALLBACK +EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext) +{ + JoyStick_DeviceData *pNewJoystick; + JoyStick_DeviceData *pPrevJoystick = NULL; + + if (SDL_IsXInputDevice(&pdidInstance->guidProduct)) { + return DIENUM_CONTINUE; /* ignore XInput devices here, keep going. */ + } + + pNewJoystick = *(JoyStick_DeviceData **)pContext; + while (pNewJoystick) { + if (!SDL_memcmp(&pNewJoystick->dxdevice.guidInstance, &pdidInstance->guidInstance, sizeof(pNewJoystick->dxdevice.guidInstance))) { + /* if we are replacing the front of the list then update it */ + if (pNewJoystick == *(JoyStick_DeviceData **)pContext) { + *(JoyStick_DeviceData **)pContext = pNewJoystick->pNext; + } else if (pPrevJoystick) { + pPrevJoystick->pNext = pNewJoystick->pNext; + } + + pNewJoystick->pNext = SYS_Joystick; + SYS_Joystick = pNewJoystick; + + return DIENUM_CONTINUE; /* already have this joystick loaded, just keep going */ + } + + pPrevJoystick = pNewJoystick; + pNewJoystick = pNewJoystick->pNext; + } + + pNewJoystick = (JoyStick_DeviceData *)SDL_malloc(sizeof(JoyStick_DeviceData)); + if (!pNewJoystick) { + return DIENUM_CONTINUE; /* better luck next time? */ + } + + SDL_zerop(pNewJoystick); + pNewJoystick->joystickname = WIN_StringToUTF8(pdidInstance->tszProductName); + if (!pNewJoystick->joystickname) { + SDL_free(pNewJoystick); + return DIENUM_CONTINUE; /* better luck next time? */ + } + + SDL_memcpy(&(pNewJoystick->dxdevice), pdidInstance, + sizeof(DIDEVICEINSTANCE)); + + SDL_memcpy(&pNewJoystick->guid, &pdidInstance->guidProduct, sizeof(pNewJoystick->guid)); + SDL_SYS_AddJoystickDevice(pNewJoystick); + + return DIENUM_CONTINUE; /* get next device, please */ +} + +void +SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext) +{ + IDirectInput8_EnumDevices(dinput, DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, pContext, DIEDFL_ATTACHEDONLY); + + if (SDL_RawDevList) { + SDL_free(SDL_RawDevList); /* in case we used this in DirectInput detection */ + SDL_RawDevList = NULL; + } + SDL_RawDevListCount = 0; +} + +static BOOL CALLBACK +EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef) +{ + SDL_Joystick *joystick = (SDL_Joystick *)pvRef; + HRESULT result; + input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs]; + + 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; + DIPROPDWORD dilong; + + in->type = AXIS; + in->num = joystick->naxes; + 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->dwType; + diprg.diph.dwHow = DIPH_BYID; + diprg.lMin = AXIS_MIN; + diprg.lMax = AXIS_MAX; + + result = + IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice, + DIPROP_RANGE, &diprg.diph); + if (FAILED(result)) { + return DIENUM_CONTINUE; /* don't use this axis */ + } + + /* Set dead zone to 0. */ + dilong.diph.dwSize = sizeof(dilong); + dilong.diph.dwHeaderSize = sizeof(dilong.diph); + dilong.diph.dwObj = dev->dwType; + dilong.diph.dwHow = DIPH_BYID; + dilong.dwData = 0; + result = + IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice, + DIPROP_DEADZONE, &dilong.diph); + if (FAILED(result)) { + return DIENUM_CONTINUE; /* don't use this axis */ + } + + joystick->naxes++; + } else { + /* not supported at this time */ + return DIENUM_CONTINUE; + } + + joystick->hwdata->NumInputs++; + + if (joystick->hwdata->NumInputs == MAX_INPUTS) { + return DIENUM_STOP; /* too many */ + } + + return DIENUM_CONTINUE; +} + +/* Sort using the data offset into the DInput struct. + * This gives a reasonable ordering for the inputs. + */ +static int +SortDevFunc(const void *a, const void *b) +{ + const input_t *inputA = (const input_t*)a; + const input_t *inputB = (const input_t*)b; + + if (inputA->ofs < inputB->ofs) + return -1; + if (inputA->ofs > inputB->ofs) + return 1; + return 0; +} + +/* Sort the input objects and recalculate the indices for each input. */ +static void +SortDevObjects(SDL_Joystick *joystick) +{ + input_t *inputs = joystick->hwdata->Inputs; + int nButtons = 0; + int nHats = 0; + int nAxis = 0; + int n; + + SDL_qsort(inputs, joystick->hwdata->NumInputs, sizeof(input_t), SortDevFunc); + + for (n = 0; n < joystick->hwdata->NumInputs; n++) { + switch (inputs[n].type) { + case BUTTON: + inputs[n].num = nButtons; + nButtons++; + break; + + case HAT: + inputs[n].num = nHats; + nHats++; + break; + + case AXIS: + inputs[n].num = nAxis; + nAxis++; + break; + } + } +} + +int +SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice) +{ + HRESULT result; + LPDIRECTINPUTDEVICE8 device; + DIPROPDWORD dipdw; + + joystick->hwdata->buffered = SDL_TRUE; + joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS); + + SDL_zero(dipdw); + dipdw.diph.dwSize = sizeof(DIPROPDWORD); + dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); + + result = + IDirectInput8_CreateDevice(dinput, + &(joystickdevice->dxdevice.guidInstance), &device, NULL); + if (FAILED(result)) { + return SetDIerror("IDirectInput::CreateDevice", result); + } + + /* Now get the IDirectInputDevice8 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)) { + return SetDIerror("IDirectInputDevice8::QueryInterface", result); + } + + /* Acquire shared access. Exclusive access is required for forces, + * though. */ + result = + IDirectInputDevice8_SetCooperativeLevel(joystick->hwdata-> + InputDevice, SDL_HelperWindow, + DISCL_EXCLUSIVE | + DISCL_BACKGROUND); + if (FAILED(result)) { + return SetDIerror("IDirectInputDevice8::SetCooperativeLevel", result); + } + + /* Use the extended data structure: DIJOYSTATE2. */ + result = + IDirectInputDevice8_SetDataFormat(joystick->hwdata->InputDevice, + &c_dfDIJoystick2); + if (FAILED(result)) { + return SetDIerror("IDirectInputDevice8::SetDataFormat", result); + } + + /* Get device capabilities */ + result = + IDirectInputDevice8_GetCapabilities(joystick->hwdata->InputDevice, + &joystick->hwdata->Capabilities); + if (FAILED(result)) { + return SetDIerror("IDirectInputDevice8::GetCapabilities", result); + } + + /* Force capable? */ + if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) { + + result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice); + if (FAILED(result)) { + return SetDIerror("IDirectInputDevice8::Acquire", result); + } + + /* reset all actuators. */ + result = + IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata-> + InputDevice, + DISFFC_RESET); + + /* Not necessarily supported, ignore if not supported. + if (FAILED(result)) { + return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand", result); + } + */ + + result = IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice); + + if (FAILED(result)) { + return SetDIerror("IDirectInputDevice8::Unacquire", result); + } + + /* 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)) { + return SetDIerror("IDirectInputDevice8::SetProperty", result); + } + */ + } + + /* 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 = SDL_FALSE; + } else if (FAILED(result)) { + return SetDIerror("IDirectInputDevice8::SetProperty", result); + } + return 0; +} + +static Uint8 +TranslatePOV(DWORD value) +{ + const int HAT_VALS[] = { + SDL_HAT_UP, + SDL_HAT_UP | SDL_HAT_RIGHT, + SDL_HAT_RIGHT, + SDL_HAT_DOWN | SDL_HAT_RIGHT, + SDL_HAT_DOWN, + SDL_HAT_DOWN | SDL_HAT_LEFT, + SDL_HAT_LEFT, + SDL_HAT_UP | SDL_HAT_LEFT + }; + + if (LOWORD(value) == 0xFFFF) + return SDL_HAT_CENTERED; + + /* Round the value up: */ + value += 4500 / 2; + value %= 36000; + value /= 4500; + + if (value >= 8) + return SDL_HAT_CENTERED; /* shouldn't happen */ + + return HAT_VALS[value]; +} + +static void +UpdateDINPUTJoystickState_Buffered(SDL_Joystick * joystick) +{ + int i; + HRESULT result; + DWORD numevents; + DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE]; + + numevents = INPUT_QSIZE; + result = + IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice, + sizeof(DIDEVICEOBJECTDATA), evtbuf, + &numevents, 0); + if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) { + IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice); + result = + IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice, + sizeof(DIDEVICEOBJECTDATA), + evtbuf, &numevents, 0); + } + + /* Handle the events or punt */ + if (FAILED(result)) { + joystick->hwdata->send_remove_event = SDL_TRUE; + joystick->hwdata->removed = SDL_TRUE; + return; + } + + for (i = 0; i < (int)numevents; ++i) { + int j; + + for (j = 0; j < joystick->hwdata->NumInputs; ++j) { + const input_t *in = &joystick->hwdata->Inputs[j]; + + if (evtbuf[i].dwOfs != in->ofs) + continue; + + switch (in->type) { + case AXIS: + SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)evtbuf[i].dwData); + break; + case BUTTON: + SDL_PrivateJoystickButton(joystick, in->num, + (Uint8)(evtbuf[i].dwData ? SDL_PRESSED : SDL_RELEASED)); + break; + case HAT: + { + Uint8 pos = TranslatePOV(evtbuf[i].dwData); + SDL_PrivateJoystickHat(joystick, in->num, pos); + } + break; + } + } + } +} + +/* Function to update the state of a joystick - called as a device poll. + * This function shouldn't update the joystick structure directly, + * but instead should call SDL_PrivateJoystick*() to deliver events + * and update joystick device state. + */ +static void +UpdateDINPUTJoystickState_Polled(SDL_Joystick * joystick) +{ + DIJOYSTATE2 state; + HRESULT result; + int i; + + result = + IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice, + sizeof(DIJOYSTATE2), &state); + if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) { + IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice); + result = + IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice, + sizeof(DIJOYSTATE2), &state); + } + + if (result != DI_OK) { + joystick->hwdata->send_remove_event = SDL_TRUE; + joystick->hwdata->removed = SDL_TRUE; + return; + } + + /* Set each known axis, button and POV. */ + for (i = 0; i < joystick->hwdata->NumInputs; ++i) { + const input_t *in = &joystick->hwdata->Inputs[i]; + + switch (in->type) { + case AXIS: + switch (in->ofs) { + case DIJOFS_X: + SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lX); + break; + case DIJOFS_Y: + SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lY); + break; + case DIJOFS_Z: + SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lZ); + break; + case DIJOFS_RX: + SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lRx); + break; + case DIJOFS_RY: + SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lRy); + break; + case DIJOFS_RZ: + SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lRz); + break; + case DIJOFS_SLIDER(0): + SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.rglSlider[0]); + break; + case DIJOFS_SLIDER(1): + SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.rglSlider[1]); + break; + } + break; + + case BUTTON: + SDL_PrivateJoystickButton(joystick, in->num, + (Uint8)(state.rgbButtons[in->ofs - DIJOFS_BUTTON0] ? SDL_PRESSED : SDL_RELEASED)); + break; + case HAT: + { + Uint8 pos = TranslatePOV(state.rgdwPOV[in->ofs - DIJOFS_POV(0)]); + SDL_PrivateJoystickHat(joystick, in->num, pos); + break; + } + } + } +} + +void +SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick) +{ + HRESULT result; + + 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) { + UpdateDINPUTJoystickState_Buffered(joystick); + } else { + UpdateDINPUTJoystickState_Polled(joystick); + } +} + +void +SDL_DINPUT_JoystickClose(SDL_Joystick * joystick) +{ + IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice); + IDirectInputDevice8_Release(joystick->hwdata->InputDevice); +} + +void +SDL_DINPUT_JoystickQuit(void) +{ + if (dinput != NULL) { + IDirectInput8_Release(dinput); + dinput = NULL; + } + + if (coinitialized) { + WIN_CoUninitialize(); + coinitialized = SDL_FALSE; + } +} + +#else /* !SDL_JOYSTICK_DINPUT */ + + +int +SDL_DINPUT_JoystickInit(void) +{ + return 0; +} + +void +SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext) +{ +} + +int +SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice) +{ + return SDL_Unsupported(); +} + +void +SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick) +{ +} + +void +SDL_DINPUT_JoystickClose(SDL_Joystick * joystick) +{ +} + +void +SDL_DINPUT_JoystickQuit(void) +{ +} + +#endif /* SDL_JOYSTICK_DINPUT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/haptic/windows/SDL_syshaptic_c.h b/src/joystick/windows/SDL_dinputjoystick_c.h similarity index 70% rename from src/haptic/windows/SDL_syshaptic_c.h rename to src/joystick/windows/SDL_dinputjoystick_c.h index 1e0fa190d74af..3facf0e5d1aa9 100644 --- a/src/haptic/windows/SDL_syshaptic_c.h +++ b/src/joystick/windows/SDL_dinputjoystick_c.h @@ -18,11 +18,13 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ +#include "../../SDL_internal.h" -extern int DirectInputHaptic_MaybeAddDevice(const DIDEVICEINSTANCE *pdidInstance); -extern int DirectInputHaptic_MaybeRemoveDevice(const DIDEVICEINSTANCE *pdidInstance); -extern int XInputHaptic_MaybeAddDevice(const DWORD dwUserid); -extern int XInputHaptic_MaybeRemoveDevice(const DWORD dwUserid); +extern int SDL_DINPUT_JoystickInit(void); +extern void SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext); +extern int SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice); +extern void SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick); +extern void SDL_DINPUT_JoystickClose(SDL_Joystick * joystick); +extern void SDL_DINPUT_JoystickQuit(void); /* vi: set ts=4 sw=4 expandtab: */ - diff --git a/src/joystick/windows/SDL_dxjoystick.c b/src/joystick/windows/SDL_dxjoystick.c deleted file mode 100644 index e8a4f3beceb04..0000000000000 --- a/src/joystick/windows/SDL_dxjoystick.c +++ /dev/null @@ -1,1661 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2014 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "../../SDL_internal.h" - -#ifdef SDL_JOYSTICK_DINPUT - -/* DirectInput joystick driver; written by Glenn Maynard, based on Andrei de - * A. Formiga's WINMM driver. - * - * Hats and sliders are completely untested; the app I'm writing this for mostly - * 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 IDirectInputDevice8_GetDeviceData and - * let it return 0 events. */ - -#include "SDL_error.h" -#include "SDL_assert.h" -#include "SDL_events.h" -#include "SDL_thread.h" -#include "SDL_timer.h" -#include "SDL_mutex.h" -#include "SDL_events.h" -#include "SDL_hints.h" -#include "SDL_joystick.h" -#include "../SDL_sysjoystick.h" -#if !SDL_EVENTS_DISABLED -#include "../../events/SDL_events_c.h" -#endif -#include "../../core/windows/SDL_windows.h" - -#define INITGUID /* Only set here, if set twice will cause mingw32 to break. */ -#include "SDL_dxjoystick_c.h" - -#if SDL_HAPTIC_DINPUT -#include "../../haptic/windows/SDL_syshaptic_c.h" /* For haptic hot plugging */ -#endif - -#ifndef DIDFT_OPTIONAL -#define DIDFT_OPTIONAL 0x80000000 -#endif - -DEFINE_GUID(GUID_DEVINTERFACE_HID, 0x4D1E55B2L, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30); - - -#define INPUT_QSIZE 32 /* Buffer up to 32 input messages */ -#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 */ - -/* external variables referenced. */ -extern HWND SDL_HelperWindow; - - -/* local variables */ -static SDL_bool coinitialized = SDL_FALSE; -static LPDIRECTINPUT8 dinput = NULL; -static SDL_bool s_bDeviceAdded = SDL_FALSE; -static SDL_bool s_bDeviceRemoved = SDL_FALSE; -static SDL_JoystickID s_nInstanceID = -1; -static SDL_cond *s_condJoystickThread = NULL; -static SDL_mutex *s_mutexJoyStickEnum = NULL; -static SDL_Thread *s_threadJoystick = NULL; -static SDL_bool s_bJoystickThreadQuit = SDL_FALSE; -static SDL_bool s_bXInputEnabled = SDL_TRUE; - -XInputGetState_t SDL_XInputGetState = NULL; -XInputSetState_t SDL_XInputSetState = NULL; -XInputGetCapabilities_t SDL_XInputGetCapabilities = NULL; -DWORD SDL_XInputVersion = 0; - -static HANDLE s_pXInputDLL = 0; -static int s_XInputDLLRefCount = 0; - -int -WIN_LoadXInputDLL(void) -{ - DWORD version = 0; - - if (s_pXInputDLL) { - SDL_assert(s_XInputDLLRefCount > 0); - s_XInputDLLRefCount++; - return 0; /* already loaded */ - } - - version = (1 << 16) | 4; - s_pXInputDLL = LoadLibrary(L"XInput1_4.dll"); /* 1.4 Ships with Windows 8. */ - if (!s_pXInputDLL) { - version = (1 << 16) | 3; - s_pXInputDLL = LoadLibrary(L"XInput1_3.dll"); /* 1.3 Ships with Vista and Win7, can be installed as a redistributable component. */ - } - if (!s_pXInputDLL) { - s_pXInputDLL = LoadLibrary(L"bin\\XInput1_3.dll"); - } - if (!s_pXInputDLL) { - return -1; - } - - SDL_assert(s_XInputDLLRefCount == 0); - SDL_XInputVersion = version; - s_XInputDLLRefCount = 1; - - /* 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... */ - SDL_XInputGetState = (XInputGetState_t)GetProcAddress((HMODULE)s_pXInputDLL, (LPCSTR)100); - SDL_XInputSetState = (XInputSetState_t)GetProcAddress((HMODULE)s_pXInputDLL, "XInputSetState"); - SDL_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress((HMODULE)s_pXInputDLL, "XInputGetCapabilities"); - if (!SDL_XInputGetState || !SDL_XInputSetState || !SDL_XInputGetCapabilities) { - WIN_UnloadXInputDLL(); - return -1; - } - - return 0; -} - -void -WIN_UnloadXInputDLL(void) -{ - if (s_pXInputDLL) { - SDL_assert(s_XInputDLLRefCount > 0); - if (--s_XInputDLLRefCount == 0) { - FreeLibrary(s_pXInputDLL); - s_pXInputDLL = NULL; - } - } else { - SDL_assert(s_XInputDLLRefCount == 0); - } -} - - -extern HRESULT(WINAPI * DInputCreate) (HINSTANCE hinst, DWORD dwVersion, - LPDIRECTINPUT * ppDI, - LPUNKNOWN punkOuter); -struct JoyStick_DeviceData_ -{ - SDL_JoystickGUID guid; - DIDEVICEINSTANCE dxdevice; - char *joystickname; - Uint8 send_add_event; - SDL_JoystickID nInstanceID; - SDL_bool bXInputDevice; - BYTE SubType; - Uint8 XInputUserId; - struct JoyStick_DeviceData_ *pNext; -}; - -typedef struct JoyStick_DeviceData_ JoyStick_DeviceData; - -static JoyStick_DeviceData *SYS_Joystick; /* array to hold joystick ID values */ - -/* local prototypes */ -static int SetDIerror(const char *function, HRESULT code); -static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE * - pdidInstance, VOID * pContext); -static BOOL CALLBACK EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev, - LPVOID pvRef); -static void SortDevObjects(SDL_Joystick *joystick); -static Uint8 TranslatePOV(DWORD value); - -/* 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}, - { &GUID_ZAxis,DIJOFS_Z,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_RxAxis,DIJOFS_RX,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_RyAxis,DIJOFS_RY,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_RzAxis,DIJOFS_RZ,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_Slider,DIJOFS_SLIDER(0),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_Slider,DIJOFS_SLIDER(1),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_POV,DIJOFS_POV(0),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE,0}, - { &GUID_POV,DIJOFS_POV(1),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE,0}, - { &GUID_POV,DIJOFS_POV(2),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE,0}, - { &GUID_POV,DIJOFS_POV(3),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(0),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(1),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(2),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(3),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(4),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(5),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(6),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(7),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(8),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(9),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(10),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(11),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(12),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(13),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(14),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(15),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(16),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(17),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(18),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(19),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(20),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(21),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(22),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(23),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(24),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(25),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(26),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(27),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(28),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(29),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(30),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(31),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(32),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(33),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(34),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(35),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(36),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(37),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(38),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(39),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(40),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(41),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(42),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(43),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(44),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(45),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(46),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(47),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(48),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(49),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(50),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(51),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(52),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(53),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(54),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(55),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(56),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(57),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(58),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(59),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(60),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(61),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(62),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(63),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(64),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(65),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(66),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(67),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(68),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(69),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(70),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(71),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(72),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(73),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(74),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(75),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(76),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(77),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(78),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(79),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(80),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(81),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(82),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(83),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(84),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(85),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(86),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(87),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(88),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(89),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(90),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(91),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(92),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(93),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(94),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(95),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(96),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(97),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(98),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(99),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(100),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(101),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(102),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(103),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(104),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(105),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(106),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(107),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(108),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(109),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(110),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(111),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(112),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(113),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(114),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(115),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(116),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(117),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(118),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(119),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(120),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(121),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(122),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(123),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(124),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(125),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(126),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { NULL,DIJOFS_BUTTON(127),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0}, - { &GUID_XAxis,FIELD_OFFSET(DIJOYSTATE2,lVX),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_YAxis,FIELD_OFFSET(DIJOYSTATE2,lVY),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_ZAxis,FIELD_OFFSET(DIJOYSTATE2,lVZ),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_RxAxis,FIELD_OFFSET(DIJOYSTATE2,lVRx),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_RyAxis,FIELD_OFFSET(DIJOYSTATE2,lVRy),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_RzAxis,FIELD_OFFSET(DIJOYSTATE2,lVRz),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglVSlider[0]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglVSlider[1]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_XAxis,FIELD_OFFSET(DIJOYSTATE2,lAX),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_YAxis,FIELD_OFFSET(DIJOYSTATE2,lAY),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_ZAxis,FIELD_OFFSET(DIJOYSTATE2,lAZ),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_RxAxis,FIELD_OFFSET(DIJOYSTATE2,lARx),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_RyAxis,FIELD_OFFSET(DIJOYSTATE2,lARy),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_RzAxis,FIELD_OFFSET(DIJOYSTATE2,lARz),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglASlider[0]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglASlider[1]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_XAxis,FIELD_OFFSET(DIJOYSTATE2,lFX),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_YAxis,FIELD_OFFSET(DIJOYSTATE2,lFY),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_ZAxis,FIELD_OFFSET(DIJOYSTATE2,lFZ),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_RxAxis,FIELD_OFFSET(DIJOYSTATE2,lFRx),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_RyAxis,FIELD_OFFSET(DIJOYSTATE2,lFRy),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_RzAxis,FIELD_OFFSET(DIJOYSTATE2,lFRz),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglFSlider[0]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, - { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglFSlider[1]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0}, -}; - -const DIDATAFORMAT c_dfDIJoystick2 = { - sizeof(DIDATAFORMAT), - sizeof(DIOBJECTDATAFORMAT), - DIDF_ABSAXIS, - sizeof(DIJOYSTATE2), - SDL_arraysize(dfDIJoystick2), - dfDIJoystick2 -}; - - -/* Convert a DirectInput return code to a text message */ -static int -SetDIerror(const char *function, HRESULT code) -{ - /* - return SDL_SetError("%s() [%s]: %s", function, - DXGetErrorString9A(code), DXGetErrorDescription9A(code)); - */ - return SDL_SetError("%s() DirectX error %d", function, code); -} - - -#define SAFE_RELEASE(p) \ -{ \ - if (p) { \ - (p)->lpVtbl->Release((p)); \ - (p) = 0; \ - } \ -} - -DEFINE_GUID(IID_ValveStreamingGamepad, MAKELONG(0x28DE, 0x11FF),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44); -DEFINE_GUID(IID_X360WiredGamepad, MAKELONG(0x045E, 0x02A1),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44); -DEFINE_GUID(IID_X360WirelessGamepad, MAKELONG(0x045E, 0x028E),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44); - -static PRAWINPUTDEVICELIST SDL_RawDevList = NULL; -static UINT SDL_RawDevListCount = 0; - -static SDL_bool -SDL_XInputUseOldJoystickMapping() -{ - static int s_XInputUseOldJoystickMapping = -1; - if (s_XInputUseOldJoystickMapping < 0) { - const char *hint = SDL_GetHint(SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING); - s_XInputUseOldJoystickMapping = (hint && *hint == '1') ? 1 : 0; - } - return (s_XInputUseOldJoystickMapping > 0); -} - -static SDL_bool -SDL_IsXInputDevice(const GUID* pGuidProductFromDirectInput) -{ - static const GUID *s_XInputProductGUID[] = { - &IID_ValveStreamingGamepad, - &IID_X360WiredGamepad, /* Microsoft's wired X360 controller for Windows. */ - &IID_X360WirelessGamepad /* Microsoft's wireless X360 controller for Windows. */ - }; - - size_t iDevice; - UINT i; - - if (!s_bXInputEnabled) { - return SDL_FALSE; - } - - /* Check for well known XInput device GUIDs */ - /* This lets us skip RAWINPUT for popular devices. Also, we need to do this for the Valve Streaming Gamepad because it's virtualized and doesn't show up in the device list. */ - for (iDevice = 0; iDevice < SDL_arraysize(s_XInputProductGUID); ++iDevice) { - if (SDL_memcmp(pGuidProductFromDirectInput, s_XInputProductGUID[iDevice], sizeof(GUID)) == 0) { - return SDL_TRUE; - } - } - - /* Go through RAWINPUT (WinXP and later) to find HID devices. */ - /* Cache this if we end up using it. */ - if (SDL_RawDevList == NULL) { - if ((GetRawInputDeviceList(NULL, &SDL_RawDevListCount, sizeof (RAWINPUTDEVICELIST)) == -1) || (!SDL_RawDevListCount)) { - return SDL_FALSE; /* oh well. */ - } - - SDL_RawDevList = (PRAWINPUTDEVICELIST) SDL_malloc(sizeof (RAWINPUTDEVICELIST) * SDL_RawDevListCount); - if (SDL_RawDevList == NULL) { - SDL_OutOfMemory(); - return SDL_FALSE; - } - - if (GetRawInputDeviceList(SDL_RawDevList, &SDL_RawDevListCount, sizeof (RAWINPUTDEVICELIST)) == -1) { - SDL_free(SDL_RawDevList); - SDL_RawDevList = NULL; - return SDL_FALSE; /* oh well. */ - } - } - - for (i = 0; i < SDL_RawDevListCount; i++) { - RID_DEVICE_INFO rdi; - char devName[128]; - UINT rdiSize = sizeof (rdi); - UINT nameSize = SDL_arraysize(devName); - - rdi.cbSize = sizeof (rdi); - if ((SDL_RawDevList[i].dwType == RIM_TYPEHID) && - (GetRawInputDeviceInfoA(SDL_RawDevList[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != ((UINT)-1)) && - (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == ((LONG)pGuidProductFromDirectInput->Data1)) && - (GetRawInputDeviceInfoA(SDL_RawDevList[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != ((UINT)-1)) && - (SDL_strstr(devName, "IG_") != NULL)) { - return SDL_TRUE; - } - } - - return SDL_FALSE; -} - - -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); -} - - -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) -{ - HWND messageWindow = 0; - HDEVNOTIFY hNotify = 0; - DEV_BROADCAST_DEVICEINTERFACE dbh; - SDL_bool bOpenedXInputDevices[SDL_XINPUT_MAX_DEVICES]; - WNDCLASSEX wincl; - - SDL_zero(bOpenedXInputDevices); - - WIN_CoInitialize(); - - 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 WIN_SetError("Failed to create register class for joystick autodetect"); - } - - messageWindow = (HWND)CreateWindowEx(0, L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); - if (!messageWindow) { - return WIN_SetError("Failed to create message window for joystick autodetect"); - } - - SDL_zero(dbh); - - dbh.dbcc_size = sizeof(dbh); - dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; - dbh.dbcc_classguid = GUID_DEVINTERFACE_HID; - - hNotify = RegisterDeviceNotification(messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE); - if (!hNotify) { - return WIN_SetError("Failed to create notify device for joystick autodetect"); - } - - SDL_LockMutex(s_mutexJoyStickEnum); - while (s_bJoystickThreadQuit == SDL_FALSE) { - MSG messages; - SDL_bool bXInputChanged = SDL_FALSE; - - 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_bXInputEnabled && XINPUTGETCAPABILITIES) { - /* scan for any change in XInput devices */ - Uint8 userId; - for (userId = 0; userId < SDL_XINPUT_MAX_DEVICES; userId++) { - XINPUT_CAPABILITIES capabilities; - const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities); - const SDL_bool available = (result == ERROR_SUCCESS); - if (bOpenedXInputDevices[userId] != available) { - bXInputChanged = SDL_TRUE; - bOpenedXInputDevices[userId] = available; - } - } - } - - if (s_bWindowsDeviceChanged || bXInputChanged) { - SDL_UnlockMutex(s_mutexJoyStickEnum); /* let main thread go while we SDL_Delay(). */ - SDL_Delay(300); /* wait for direct input to find out about this device */ - SDL_LockMutex(s_mutexJoyStickEnum); - - s_bDeviceRemoved = SDL_TRUE; - s_bDeviceAdded = SDL_TRUE; - s_bWindowsDeviceChanged = SDL_FALSE; - } - } - SDL_UnlockMutex(s_mutexJoyStickEnum); - - if (hNotify) - UnregisterDeviceNotification(hNotify); - - if (messageWindow) - DestroyWindow(messageWindow); - - UnregisterClass(wincl.lpszClassName, wincl.hInstance); - 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. - * It should return 0, or -1 on an unrecoverable fatal error. - */ -int -SDL_SYS_JoystickInit(void) -{ - HRESULT result; - HINSTANCE instance; - const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED); - if (env && !SDL_atoi(env)) { - s_bXInputEnabled = SDL_FALSE; - } - - result = WIN_CoInitialize(); - if (FAILED(result)) { - return SetDIerror("CoInitialize", result); - } - - coinitialized = SDL_TRUE; - - result = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER, - &IID_IDirectInput8, (LPVOID)&dinput); - - if (FAILED(result)) { - SDL_SYS_JoystickQuit(); - return SetDIerror("CoCreateInstance", result); - } - - /* Because we used CoCreateInstance, we need to Initialize it, first. */ - instance = GetModuleHandle(NULL); - if (instance == NULL) { - SDL_SYS_JoystickQuit(); - return SDL_SetError("GetModuleHandle() failed with error code %d.", GetLastError()); - } - result = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION); - - if (FAILED(result)) { - SDL_SYS_JoystickQuit(); - return SetDIerror("IDirectInput::Initialize", result); - } - - if ((s_bXInputEnabled) && (WIN_LoadXInputDLL() == -1)) { - s_bXInputEnabled = SDL_FALSE; /* oh well. */ - } - - 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(); - - 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 -#if SDL_DYNAMIC_API - s_threadJoystick= SDL_CreateThread_REAL(SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL); -#else - s_threadJoystick= SDL_CreateThread(SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL); -#endif -#else - s_threadJoystick = SDL_CreateThread(SDL_JoystickThread, "SDL_joystick", NULL); -#endif - } - return SDL_SYS_NumJoysticks(); -} - -/* 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; -} - -/* helper function for direct input, gets called for each connected joystick */ -static BOOL CALLBACK -EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext) -{ - JoyStick_DeviceData *pNewJoystick; - JoyStick_DeviceData *pPrevJoystick = NULL; - - if (SDL_IsXInputDevice(&pdidInstance->guidProduct)) { - return DIENUM_CONTINUE; /* ignore XInput devices here, keep going. */ - } - - pNewJoystick = *(JoyStick_DeviceData **)pContext; - while (pNewJoystick) { - if (!SDL_memcmp(&pNewJoystick->dxdevice.guidInstance, &pdidInstance->guidInstance, sizeof(pNewJoystick->dxdevice.guidInstance))) { - /* if we are replacing the front of the list then update it */ - if (pNewJoystick == *(JoyStick_DeviceData **)pContext) { - *(JoyStick_DeviceData **)pContext = pNewJoystick->pNext; - } else if (pPrevJoystick) { - pPrevJoystick->pNext = pNewJoystick->pNext; - } - - pNewJoystick->pNext = SYS_Joystick; - SYS_Joystick = pNewJoystick; - - return DIENUM_CONTINUE; /* already have this joystick loaded, just keep going */ - } - - pPrevJoystick = pNewJoystick; - pNewJoystick = pNewJoystick->pNext; - } - - pNewJoystick = (JoyStick_DeviceData *)SDL_malloc(sizeof(JoyStick_DeviceData)); - if (!pNewJoystick) { - return DIENUM_CONTINUE; /* better luck next time? */ - } - - SDL_zerop(pNewJoystick); - pNewJoystick->joystickname = WIN_StringToUTF8(pdidInstance->tszProductName); - if (!pNewJoystick->joystickname) { - SDL_free(pNewJoystick); - return DIENUM_CONTINUE; /* better luck next time? */ - } - - SDL_memcpy(&(pNewJoystick->dxdevice), pdidInstance, - sizeof(DIDEVICEINSTANCE)); - - pNewJoystick->XInputUserId = INVALID_XINPUT_USERID; - pNewJoystick->send_add_event = SDL_TRUE; - pNewJoystick->nInstanceID = ++s_nInstanceID; - SDL_memcpy(&pNewJoystick->guid, &pdidInstance->guidProduct, sizeof(pNewJoystick->guid)); - pNewJoystick->pNext = SYS_Joystick; - SYS_Joystick = pNewJoystick; - - s_bDeviceAdded = SDL_TRUE; - - return DIENUM_CONTINUE; /* get next device, please */ -} - -static char * -GetXInputName(const Uint8 userid, BYTE SubType) -{ - char name[32]; - - if (SDL_XInputUseOldJoystickMapping()) { - SDL_snprintf(name, sizeof(name), "X360 Controller #%u", 1 + userid); - } else { - switch (SubType) { - case XINPUT_DEVSUBTYPE_GAMEPAD: - SDL_snprintf(name, sizeof(name), "XInput Controller #%u", 1 + userid); - break; - case XINPUT_DEVSUBTYPE_WHEEL: - SDL_snprintf(name, sizeof(name), "XInput Wheel #%u", 1 + userid); - break; - case XINPUT_DEVSUBTYPE_ARCADE_STICK: - SDL_snprintf(name, sizeof(name), "XInput ArcadeStick #%u", 1 + userid); - break; - case XINPUT_DEVSUBTYPE_FLIGHT_STICK: - SDL_snprintf(name, sizeof(name), "XInput FlightStick #%u", 1 + userid); - break; - case XINPUT_DEVSUBTYPE_DANCE_PAD: - SDL_snprintf(name, sizeof(name), "XInput DancePad #%u", 1 + userid); - break; - case XINPUT_DEVSUBTYPE_GUITAR: - case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE: - case XINPUT_DEVSUBTYPE_GUITAR_BASS: - SDL_snprintf(name, sizeof(name), "XInput Guitar #%u", 1 + userid); - break; - case XINPUT_DEVSUBTYPE_DRUM_KIT: - SDL_snprintf(name, sizeof(name), "XInput DrumKit #%u", 1 + userid); - break; - case XINPUT_DEVSUBTYPE_ARCADE_PAD: - SDL_snprintf(name, sizeof(name), "XInput ArcadePad #%u", 1 + userid); - break; - default: - SDL_snprintf(name, sizeof(name), "XInput Device #%u", 1 + userid); - break; - } - } - return SDL_strdup(name); -} - -static void -AddXInputDevice(const Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext) -{ - JoyStick_DeviceData *pPrevJoystick = NULL; - JoyStick_DeviceData *pNewJoystick = *pContext; - - if (SDL_XInputUseOldJoystickMapping() && SubType != XINPUT_DEVSUBTYPE_GAMEPAD) - return; - - if (SubType == XINPUT_DEVSUBTYPE_UNKNOWN) - return; - - while (pNewJoystick) { - if (pNewJoystick->bXInputDevice && (pNewJoystick->XInputUserId == userid) && (pNewJoystick->SubType == SubType)) { - /* if we are replacing the front of the list then update it */ - if (pNewJoystick == *pContext) { - *pContext = pNewJoystick->pNext; - } else if (pPrevJoystick) { - pPrevJoystick->pNext = pNewJoystick->pNext; - } - - pNewJoystick->pNext = SYS_Joystick; - SYS_Joystick = pNewJoystick; - return; /* already in the list. */ - } - - pPrevJoystick = pNewJoystick; - pNewJoystick = pNewJoystick->pNext; - } - - pNewJoystick = (JoyStick_DeviceData *) SDL_malloc(sizeof (JoyStick_DeviceData)); - if (!pNewJoystick) { - return; /* better luck next time? */ - } - SDL_zerop(pNewJoystick); - - pNewJoystick->joystickname = GetXInputName(userid,SubType); - if (!pNewJoystick->joystickname) { - SDL_free(pNewJoystick); - return; /* better luck next time? */ - } - - pNewJoystick->bXInputDevice = SDL_TRUE; - if (SDL_XInputUseOldJoystickMapping()) { - SDL_zero(pNewJoystick->guid); - } else { - pNewJoystick->guid.data[0] = 'x'; - pNewJoystick->guid.data[1] = 'i'; - pNewJoystick->guid.data[2] = 'n'; - pNewJoystick->guid.data[3] = 'p'; - pNewJoystick->guid.data[4] = 'u'; - pNewJoystick->guid.data[5] = 't'; - pNewJoystick->guid.data[6] = SubType; - } - pNewJoystick->SubType = SubType; - pNewJoystick->XInputUserId = userid; - pNewJoystick->send_add_event = SDL_TRUE; - pNewJoystick->nInstanceID = ++s_nInstanceID; - pNewJoystick->pNext = SYS_Joystick; - SYS_Joystick = pNewJoystick; - - s_bDeviceAdded = SDL_TRUE; -} - -static void -EnumXInputDevices(JoyStick_DeviceData **pContext) -{ - if (s_bXInputEnabled) { - int iuserid; - /* iterate in reverse, so these are in the final list in ascending numeric order. */ - for (iuserid = SDL_XINPUT_MAX_DEVICES-1; iuserid >= 0; iuserid--) { - const Uint8 userid = (Uint8) iuserid; - XINPUT_CAPABILITIES capabilities; - if (XINPUTGETCAPABILITIES(userid, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) { - AddXInputDevice(userid, capabilities.SubType, pContext); - } - } - } -} - - -/* detect any new joysticks being inserted into the system */ -void SDL_SYS_JoystickDetect() -{ - JoyStick_DeviceData *pCurList = NULL; -#if !SDL_EVENTS_DISABLED - SDL_Event event; -#endif - - /* only enum the devices if the joystick thread told us something changed */ - if (!s_bDeviceAdded && !s_bDeviceRemoved) { - return; /* thread hasn't signaled, nothing to do right now. */ - } - - SDL_LockMutex(s_mutexJoyStickEnum); - - s_bDeviceAdded = SDL_FALSE; - s_bDeviceRemoved = SDL_FALSE; - - pCurList = SYS_Joystick; - SYS_Joystick = NULL; - - /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */ - IDirectInput8_EnumDevices(dinput, DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, &pCurList, DIEDFL_ATTACHEDONLY); - - SDL_free(SDL_RawDevList); /* in case we used this in DirectInput enumerator. */ - SDL_RawDevList = NULL; - SDL_RawDevListCount = 0; - - /* Look for XInput devices. Do this last, so they're first in the final list. */ - EnumXInputDevices(&pCurList); - - SDL_UnlockMutex(s_mutexJoyStickEnum); - - while (pCurList) { - JoyStick_DeviceData *pListNext = NULL; - -#if SDL_HAPTIC_DINPUT - if (pCurList->bXInputDevice) { - XInputHaptic_MaybeRemoveDevice(pCurList->XInputUserId); - } else { - DirectInputHaptic_MaybeRemoveDevice(&pCurList->dxdevice); - } -#endif - -#if !SDL_EVENTS_DISABLED - SDL_zero(event); - event.type = SDL_JOYDEVICEREMOVED; - - if (SDL_GetEventState(event.type) == SDL_ENABLE) { - event.jdevice.which = pCurList->nInstanceID; - if ((!SDL_EventOK) || (*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_HAPTIC_DINPUT - if (pNewJoystick->bXInputDevice) { - XInputHaptic_MaybeAddDevice(pNewJoystick->XInputUserId); - } else { - DirectInputHaptic_MaybeAddDevice(&pNewJoystick->dxdevice); - } -#endif - -#if !SDL_EVENTS_DISABLED - SDL_zero(event); - event.type = SDL_JOYDEVICEADDED; - - if (SDL_GetEventState(event.type) == SDL_ENABLE) { - event.jdevice.which = device_index; - if ((!SDL_EventOK) || (*SDL_EventOK) (SDL_EventOKParam, &event)) { - SDL_PushEvent(&event); - } - } -#endif /* !SDL_EVENTS_DISABLED */ - pNewJoystick->send_add_event = SDL_FALSE; - } - device_index++; - pNewJoystick = pNewJoystick->pNext; - } - } -} - -/* Function to get the device-dependent name of a joystick */ -const char * -SDL_SYS_JoystickNameForDeviceIndex(int device_index) -{ - JoyStick_DeviceData *device = SYS_Joystick; - - for (; device_index > 0; device_index--) - device = device->pNext; - - return device->joystickname; -} - -/* 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; -} - -/* 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. - */ -int -SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) -{ - HRESULT result; - JoyStick_DeviceData *joystickdevice = SYS_Joystick; - - for (; device_index > 0; device_index--) - joystickdevice = joystickdevice->pNext; - - /* allocate memory for system specific hardware data */ - joystick->instance_id = joystickdevice->nInstanceID; - joystick->closed = SDL_FALSE; - joystick->hwdata = - (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata)); - if (joystick->hwdata == NULL) { - return SDL_OutOfMemory(); - } - SDL_zerop(joystick->hwdata); - joystick->hwdata->guid = joystickdevice->guid; - - if (joystickdevice->bXInputDevice) { - const Uint8 userId = joystickdevice->XInputUserId; - XINPUT_CAPABILITIES capabilities; - XINPUT_VIBRATION state; - - SDL_assert(s_bXInputEnabled); - SDL_assert(XINPUTGETCAPABILITIES); - SDL_assert(XINPUTSETSTATE); - SDL_assert(userId >= 0); - SDL_assert(userId < SDL_XINPUT_MAX_DEVICES); - - joystick->hwdata->bXInputDevice = SDL_TRUE; - - if (XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities) != ERROR_SUCCESS) { - SDL_free(joystick->hwdata); - joystick->hwdata = NULL; - return SDL_SetError("Failed to obtain XInput device capabilities. Device disconnected?"); - } - SDL_zero(state); - joystick->hwdata->bXInputHaptic = (XINPUTSETSTATE(userId, &state) == ERROR_SUCCESS); - joystick->hwdata->userid = userId; - - /* The XInput API has a hard coded button/axis mapping, so we just match it */ - if (SDL_XInputUseOldJoystickMapping()) { - joystick->naxes = 6; - joystick->nbuttons = 15; - } else { - joystick->naxes = 6; - joystick->nbuttons = 11; - joystick->nhats = 1; - } - } else { /* use DirectInput, not XInput. */ - LPDIRECTINPUTDEVICE8 device; - DIPROPDWORD dipdw; - - joystick->hwdata->buffered = 1; - joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS); - - SDL_zero(dipdw); - dipdw.diph.dwSize = sizeof(DIPROPDWORD); - dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); - - result = - IDirectInput8_CreateDevice(dinput, - &(joystickdevice->dxdevice.guidInstance), &device, NULL); - if (FAILED(result)) { - return SetDIerror("IDirectInput::CreateDevice", result); - } - - /* Now get the IDirectInputDevice8 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)) { - return SetDIerror("IDirectInputDevice8::QueryInterface", result); - } - - /* Acquire shared access. Exclusive access is required for forces, - * though. */ - result = - IDirectInputDevice8_SetCooperativeLevel(joystick->hwdata-> - InputDevice, SDL_HelperWindow, - DISCL_EXCLUSIVE | - DISCL_BACKGROUND); - if (FAILED(result)) { - return SetDIerror("IDirectInputDevice8::SetCooperativeLevel", result); - } - - /* Use the extended data structure: DIJOYSTATE2. */ - result = - IDirectInputDevice8_SetDataFormat(joystick->hwdata->InputDevice, - &c_dfDIJoystick2); - if (FAILED(result)) { - return SetDIerror("IDirectInputDevice8::SetDataFormat", result); - } - - /* Get device capabilities */ - result = - IDirectInputDevice8_GetCapabilities(joystick->hwdata->InputDevice, - &joystick->hwdata->Capabilities); - - if (FAILED(result)) { - return SetDIerror("IDirectInputDevice8::GetCapabilities", result); - } - - /* Force capable? */ - if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) { - - result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice); - - if (FAILED(result)) { - return SetDIerror("IDirectInputDevice8::Acquire", result); - } - - /* reset all actuators. */ - result = - IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata-> - InputDevice, - DISFFC_RESET); - - /* Not necessarily supported, ignore if not supported. - if (FAILED(result)) { - return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand", result); - } - */ - - result = IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice); - - if (FAILED(result)) { - return SetDIerror("IDirectInputDevice8::Unacquire", result); - } - - /* 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)) { - return SetDIerror("IDirectInputDevice8::SetProperty", result); - } - */ - } - - /* 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)) { - return SetDIerror("IDirectInputDevice8::SetProperty", result); - } - } - return (0); -} - -/* return true if this joystick is plugged in right now */ -SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick * joystick) -{ - return !joystick->closed && !joystick->hwdata->removed; -} - - -/* Sort using the data offset into the DInput struct. - * This gives a reasonable ordering for the inputs. */ -static int -SortDevFunc(const void *a, const void *b) -{ - const input_t *inputA = (const input_t*)a; - const input_t *inputB = (const input_t*)b; - - if (inputA->ofs < inputB->ofs) - return -1; - if (inputA->ofs > inputB->ofs) - return 1; - return 0; -} - -/* Sort the input objects and recalculate the indices for each input. */ -static void -SortDevObjects(SDL_Joystick *joystick) -{ - input_t *inputs = joystick->hwdata->Inputs; - int nButtons = 0; - int nHats = 0; - int nAxis = 0; - int n; - - SDL_qsort(inputs, joystick->hwdata->NumInputs, sizeof(input_t), SortDevFunc); - - for (n = 0; n < joystick->hwdata->NumInputs; n++) { - switch (inputs[n].type) { - case BUTTON: - inputs[n].num = nButtons; - nButtons++; - break; - - case HAT: - inputs[n].num = nHats; - nHats++; - break; - - case AXIS: - inputs[n].num = nAxis; - nAxis++; - break; - } - } -} - -static BOOL CALLBACK -EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef) -{ - SDL_Joystick *joystick = (SDL_Joystick *) pvRef; - HRESULT result; - input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs]; - - 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; - DIPROPDWORD dilong; - - in->type = AXIS; - in->num = joystick->naxes; - 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->dwType; - diprg.diph.dwHow = DIPH_BYID; - diprg.lMin = AXIS_MIN; - diprg.lMax = AXIS_MAX; - - result = - IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice, - DIPROP_RANGE, &diprg.diph); - if (FAILED(result)) { - return DIENUM_CONTINUE; /* don't use this axis */ - } - - /* Set dead zone to 0. */ - dilong.diph.dwSize = sizeof(dilong); - dilong.diph.dwHeaderSize = sizeof(dilong.diph); - dilong.diph.dwObj = dev->dwType; - dilong.diph.dwHow = DIPH_BYID; - dilong.dwData = 0; - result = - IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice, - DIPROP_DEADZONE, &dilong.diph); - if (FAILED(result)) { - return DIENUM_CONTINUE; /* don't use this axis */ - } - - joystick->naxes++; - } else { - /* not supported at this time */ - return DIENUM_CONTINUE; - } - - joystick->hwdata->NumInputs++; - - if (joystick->hwdata->NumInputs == MAX_INPUTS) { - return DIENUM_STOP; /* too many */ - } - - return DIENUM_CONTINUE; -} - -/* Function to update the state of a joystick - called as a device poll. - * This function shouldn't update the joystick structure directly, - * but instead should call SDL_PrivateJoystick*() to deliver events - * and update joystick device state. - */ -void -SDL_SYS_JoystickUpdate_Polled(SDL_Joystick * joystick) -{ - DIJOYSTATE2 state; - HRESULT result; - int i; - - result = - IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice, - sizeof(DIJOYSTATE2), &state); - if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) { - IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice); - result = - IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice, - sizeof(DIJOYSTATE2), &state); - } - - if (result != DI_OK) { - joystick->hwdata->send_remove_event = SDL_TRUE; - joystick->hwdata->removed = SDL_TRUE; - return; - } - - /* Set each known axis, button and POV. */ - for (i = 0; i < joystick->hwdata->NumInputs; ++i) { - const input_t *in = &joystick->hwdata->Inputs[i]; - - switch (in->type) { - case AXIS: - switch (in->ofs) { - case DIJOFS_X: - SDL_PrivateJoystickAxis(joystick, in->num, (Sint16) state.lX); - break; - case DIJOFS_Y: - SDL_PrivateJoystickAxis(joystick, in->num, (Sint16) state.lY); - break; - case DIJOFS_Z: - SDL_PrivateJoystickAxis(joystick, in->num, (Sint16) state.lZ); - break; - case DIJOFS_RX: - SDL_PrivateJoystickAxis(joystick, in->num, (Sint16) state.lRx); - break; - case DIJOFS_RY: - SDL_PrivateJoystickAxis(joystick, in->num, (Sint16) state.lRy); - break; - case DIJOFS_RZ: - SDL_PrivateJoystickAxis(joystick, in->num, (Sint16) state.lRz); - break; - case DIJOFS_SLIDER(0): - SDL_PrivateJoystickAxis(joystick, in->num, (Sint16) state.rglSlider[0]); - break; - case DIJOFS_SLIDER(1): - SDL_PrivateJoystickAxis(joystick, in->num, (Sint16) state.rglSlider[1]); - break; - } - break; - - case BUTTON: - SDL_PrivateJoystickButton(joystick, in->num, - (Uint8) (state.rgbButtons[in->ofs - DIJOFS_BUTTON0] ? SDL_PRESSED : SDL_RELEASED)); - break; - case HAT: - { - Uint8 pos = TranslatePOV(state.rgdwPOV[in->ofs - DIJOFS_POV(0)]); - SDL_PrivateJoystickHat(joystick, in->num, pos); - break; - } - } - } -} - -void -SDL_SYS_JoystickUpdate_Buffered(SDL_Joystick * joystick) -{ - int i; - HRESULT result; - DWORD numevents; - DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE]; - - numevents = INPUT_QSIZE; - result = - IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice, - sizeof(DIDEVICEOBJECTDATA), evtbuf, - &numevents, 0); - if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) { - IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice); - result = - IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice, - sizeof(DIDEVICEOBJECTDATA), - evtbuf, &numevents, 0); - } - - /* Handle the events or punt */ - if (FAILED(result)) { - joystick->hwdata->send_remove_event = SDL_TRUE; - joystick->hwdata->removed = SDL_TRUE; - return; - } - - for (i = 0; i < (int) numevents; ++i) { - int j; - - for (j = 0; j < joystick->hwdata->NumInputs; ++j) { - const input_t *in = &joystick->hwdata->Inputs[j]; - - if (evtbuf[i].dwOfs != in->ofs) - continue; - - switch (in->type) { - case AXIS: - SDL_PrivateJoystickAxis(joystick, in->num, (Sint16) evtbuf[i].dwData); - break; - case BUTTON: - SDL_PrivateJoystickButton(joystick, in->num, - (Uint8) (evtbuf[i].dwData ? SDL_PRESSED : SDL_RELEASED)); - break; - case HAT: - { - Uint8 pos = TranslatePOV(evtbuf[i].dwData); - SDL_PrivateJoystickHat(joystick, in->num, pos); - } - } - } - } -} - -static void -UpdateXInputJoystickState_OLD(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState) -{ - static WORD s_XInputButtons[] = { - XINPUT_GAMEPAD_DPAD_UP, XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_RIGHT, - XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB, - XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, - XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y, - XINPUT_GAMEPAD_GUIDE - }; - WORD wButtons = pXInputState->Gamepad.wButtons; - Uint8 button; - Uint8 hat = SDL_HAT_CENTERED; - - SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX); - SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY))); - SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX); - SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY))); - SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768)); - SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768)); - - for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) { - SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED); - } -} - -static void -UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState) -{ - static WORD s_XInputButtons[] = { - XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y, - XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_START, - XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB, - XINPUT_GAMEPAD_GUIDE - }; - WORD wButtons = pXInputState->Gamepad.wButtons; - Uint8 button; - Uint8 hat = SDL_HAT_CENTERED; - - SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX); - SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY))); - SDL_PrivateJoystickAxis(joystick, 2, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768)); - SDL_PrivateJoystickAxis(joystick, 3, (Sint16)pXInputState->Gamepad.sThumbRX); - SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY))); - SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768)); - - for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) { - SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED); - } - - if (wButtons & XINPUT_GAMEPAD_DPAD_UP) { - hat |= SDL_HAT_UP; - } - if (wButtons & XINPUT_GAMEPAD_DPAD_DOWN) { - hat |= SDL_HAT_DOWN; - } - if (wButtons & XINPUT_GAMEPAD_DPAD_LEFT) { - hat |= SDL_HAT_LEFT; - } - if (wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) { - hat |= SDL_HAT_RIGHT; - } - SDL_PrivateJoystickHat(joystick, 0, hat); -} - -/* Function to update the state of a XInput style joystick. -*/ -void -SDL_SYS_JoystickUpdate_XInput(SDL_Joystick * joystick) -{ - HRESULT result; - XINPUT_STATE_EX XInputState; - - if (!XINPUTGETSTATE) - return; - - result = XINPUTGETSTATE(joystick->hwdata->userid, &XInputState); - if (result == ERROR_DEVICE_NOT_CONNECTED) { - joystick->hwdata->send_remove_event = SDL_TRUE; - joystick->hwdata->removed = SDL_TRUE; - return; - } - - /* only fire events if the data changed from last time */ - if (XInputState.dwPacketNumber && XInputState.dwPacketNumber != joystick->hwdata->dwPacketNumber) { - if (SDL_XInputUseOldJoystickMapping()) { - UpdateXInputJoystickState_OLD(joystick, &XInputState); - } else { - UpdateXInputJoystickState(joystick, &XInputState); - } - joystick->hwdata->dwPacketNumber = XInputState.dwPacketNumber; - } -} - -static Uint8 -TranslatePOV(DWORD value) -{ - const int HAT_VALS[] = { - SDL_HAT_UP, - SDL_HAT_UP | SDL_HAT_RIGHT, - SDL_HAT_RIGHT, - SDL_HAT_DOWN | SDL_HAT_RIGHT, - SDL_HAT_DOWN, - SDL_HAT_DOWN | SDL_HAT_LEFT, - SDL_HAT_LEFT, - SDL_HAT_UP | SDL_HAT_LEFT - }; - - if (LOWORD(value) == 0xFFFF) - return SDL_HAT_CENTERED; - - /* Round the value up: */ - value += 4500 / 2; - value %= 36000; - value /= 4500; - - if (value >= 8) - return SDL_HAT_CENTERED; /* shouldn't happen */ - - return HAT_VALS[value]; -} - -void -SDL_SYS_JoystickUpdate(SDL_Joystick * joystick) -{ - HRESULT result; - - if (joystick->closed || !joystick->hwdata) - return; - - 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 = SDL_TRUE; - joystick->uncentered = SDL_TRUE; - } -} - -/* Function to close a joystick after use */ -void -SDL_SYS_JoystickClose(SDL_Joystick * joystick) -{ - if (!joystick->hwdata->bXInputDevice) { - IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice); - IDirectInputDevice8_Release(joystick->hwdata->InputDevice); - } - - /* free system specific hardware data */ - SDL_free(joystick->hwdata); - - joystick->closed = SDL_TRUE; -} - -/* Function to perform any system-specific joystick related cleanup */ -void -SDL_SYS_JoystickQuit(void) -{ - JoyStick_DeviceData *device = SYS_Joystick; - - 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) { - IDirectInput8_Release(dinput); - dinput = NULL; - } - - if (coinitialized) { - WIN_CoUninitialize(); - coinitialized = SDL_FALSE; - } - - if (s_bXInputEnabled) { - WIN_UnloadXInputDLL(); - } -} - -/* return the stable device guid for this device index */ -SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID(int device_index) -{ - JoyStick_DeviceData *device = SYS_Joystick; - int index; - - for (index = device_index; index > 0; index--) - device = device->pNext; - - return device->guid; -} - -SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick) -{ - return joystick->hwdata->guid; -} - -SDL_bool SDL_SYS_IsXInputGamepad_DeviceIndex(int device_index) -{ - JoyStick_DeviceData *device = SYS_Joystick; - int index; - - for (index = device_index; index > 0; index--) - device = device->pNext; - - return (device->SubType == XINPUT_DEVSUBTYPE_GAMEPAD); -} - -#endif /* SDL_JOYSTICK_DINPUT */ - -/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/windows/SDL_windowsjoystick.c b/src/joystick/windows/SDL_windowsjoystick.c new file mode 100644 index 0000000000000..9028e3d5c4ecf --- /dev/null +++ b/src/joystick/windows/SDL_windowsjoystick.c @@ -0,0 +1,569 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2014 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT + +/* DirectInput joystick driver; written by Glenn Maynard, based on Andrei de + * A. Formiga's WINMM driver. + * + * Hats and sliders are completely untested; the app I'm writing this for mostly + * 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 IDirectInputDevice8_GetDeviceData and + * let it return 0 events. */ + +#include "SDL_error.h" +#include "SDL_assert.h" +#include "SDL_events.h" +#include "SDL_thread.h" +#include "SDL_timer.h" +#include "SDL_mutex.h" +#include "SDL_events.h" +#include "SDL_hints.h" +#include "SDL_joystick.h" +#include "../SDL_sysjoystick.h" +#if !SDL_EVENTS_DISABLED +#include "../../events/SDL_events_c.h" +#endif +#include "../../core/windows/SDL_windows.h" +#include + +#define INITGUID /* Only set here, if set twice will cause mingw32 to break. */ +#include "SDL_windowsjoystick_c.h" +#include "SDL_dinputjoystick_c.h" +#include "SDL_xinputjoystick_c.h" + +#include "../../haptic/windows/SDL_dinputhaptic_c.h" /* For haptic hot plugging */ +#include "../../haptic/windows/SDL_xinputhaptic_c.h" /* For haptic hot plugging */ + + +/* local variables */ +static SDL_bool s_bDeviceAdded = SDL_FALSE; +static SDL_bool s_bDeviceRemoved = SDL_FALSE; +static SDL_JoystickID s_nInstanceID = -1; +static SDL_cond *s_condJoystickThread = NULL; +static SDL_mutex *s_mutexJoyStickEnum = NULL; +static SDL_Thread *s_threadJoystick = NULL; +static SDL_bool s_bJoystickThreadQuit = SDL_FALSE; + +JoyStick_DeviceData *SYS_Joystick; /* array to hold joystick ID values */ + +static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE; + +#ifdef __WINRT__ + +typedef struct +{ + int unused; +} SDL_DeviceNotificationData; + +static void +SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data) +{ +} + +static int +SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data) +{ + return 0; +} + +static void +SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data) +{ +} + +#else /* !__WINRT__ */ + +typedef struct +{ + HRESULT coinitialized; + WNDCLASSEX wincl; + HWND messageWindow; + HDEVNOTIFY hNotify; +} SDL_DeviceNotificationData; + + +/* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal */ +static 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); +} + +static void +SDL_CleanupDeviceNotification(SDL_DeviceNotificationData *data) +{ + if (data->hNotify) + UnregisterDeviceNotification(data->hNotify); + + if (data->messageWindow) + DestroyWindow(data->messageWindow); + + UnregisterClass(data->wincl.lpszClassName, data->wincl.hInstance); + + if (data->coinitialized == S_OK) { + WIN_CoUninitialize(); + } +} + +static int +SDL_CreateDeviceNotification(SDL_DeviceNotificationData *data) +{ + DEV_BROADCAST_DEVICEINTERFACE dbh; + GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } }; + + SDL_zerop(data); + + data->coinitialized = WIN_CoInitialize(); + + data->wincl.hInstance = GetModuleHandle(NULL); + data->wincl.lpszClassName = L"Message"; + data->wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc; /* This function is called by windows */ + data->wincl.cbSize = sizeof (WNDCLASSEX); + + if (!RegisterClassEx(&data->wincl)) { + WIN_SetError("Failed to create register class for joystick autodetect"); + SDL_CleanupDeviceNotification(data); + return -1; + } + + data->messageWindow = (HWND)CreateWindowEx(0, L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); + if (!data->messageWindow) { + WIN_SetError("Failed to create message window for joystick autodetect"); + SDL_CleanupDeviceNotification(data); + return -1; + } + + SDL_zero(dbh); + dbh.dbcc_size = sizeof(dbh); + dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + dbh.dbcc_classguid = GUID_DEVINTERFACE_HID; + + data->hNotify = RegisterDeviceNotification(data->messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE); + if (!data->hNotify) { + WIN_SetError("Failed to create notify device for joystick autodetect"); + SDL_CleanupDeviceNotification(data); + return -1; + } + return 0; +} + +static void +SDL_CheckDeviceNotification(SDL_DeviceNotificationData *data) +{ + MSG msg; + + if (!data->messageWindow) { + return; + } + + while (PeekMessage(&msg, data->messageWindow, 0, 0, PM_NOREMOVE)) { + if (GetMessage(&msg, data->messageWindow, 0, 0) != 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + +#endif /* __WINRT__ */ + +/* Function/thread to scan the system for joysticks. */ +static int +SDL_JoystickThread(void *_data) +{ + SDL_DeviceNotificationData notification_data; + +#if SDL_JOYSTICK_XINPUT + SDL_bool bOpenedXInputDevices[XUSER_MAX_COUNT]; + SDL_zero(bOpenedXInputDevices); +#endif + + if (SDL_CreateDeviceNotification(¬ification_data) < 0) { + return -1; + } + + SDL_LockMutex(s_mutexJoyStickEnum); + while (s_bJoystickThreadQuit == SDL_FALSE) { + SDL_bool bXInputChanged = SDL_FALSE; + + SDL_CondWaitTimeout(s_condJoystickThread, s_mutexJoyStickEnum, 300); + + SDL_CheckDeviceNotification(¬ification_data); + +#if SDL_JOYSTICK_XINPUT + if (SDL_XINPUT_Enabled() && XINPUTGETCAPABILITIES) { + /* scan for any change in XInput devices */ + Uint8 userId; + for (userId = 0; userId < XUSER_MAX_COUNT; userId++) { + XINPUT_CAPABILITIES capabilities; + const DWORD result = XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities); + const SDL_bool available = (result == ERROR_SUCCESS); + if (bOpenedXInputDevices[userId] != available) { + bXInputChanged = SDL_TRUE; + bOpenedXInputDevices[userId] = available; + } + } + } +#endif /* SDL_JOYSTICK_XINPUT */ + + if (s_bWindowsDeviceChanged || bXInputChanged) { + SDL_UnlockMutex(s_mutexJoyStickEnum); /* let main thread go while we SDL_Delay(). */ + SDL_Delay(300); /* wait for direct input to find out about this device */ + SDL_LockMutex(s_mutexJoyStickEnum); + + s_bDeviceRemoved = SDL_TRUE; + s_bDeviceAdded = SDL_TRUE; + s_bWindowsDeviceChanged = SDL_FALSE; + } + } + SDL_UnlockMutex(s_mutexJoyStickEnum); + + SDL_CleanupDeviceNotification(¬ification_data); + + return 1; +} + +void SDL_SYS_AddJoystickDevice(JoyStick_DeviceData *device) +{ + device->send_add_event = SDL_TRUE; + device->nInstanceID = ++s_nInstanceID; + device->pNext = SYS_Joystick; + SYS_Joystick = device; + + s_bDeviceAdded = SDL_TRUE; +} + +/* 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. + * It should return 0, or -1 on an unrecoverable fatal error. + */ +int +SDL_SYS_JoystickInit(void) +{ + if (SDL_DINPUT_JoystickInit() < 0) { + SDL_SYS_JoystickQuit(); + return -1; + } + + if (SDL_XINPUT_JoystickInit() < 0) { + SDL_SYS_JoystickQuit(); + return -1; + } + + 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(); + + 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 +#if SDL_DYNAMIC_API + s_threadJoystick= SDL_CreateThread_REAL(SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL); +#else + s_threadJoystick= SDL_CreateThread(SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL); +#endif +#else + s_threadJoystick = SDL_CreateThread(SDL_JoystickThread, "SDL_joystick", NULL); +#endif + } + return SDL_SYS_NumJoysticks(); +} + +/* 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; +} + +/* detect any new joysticks being inserted into the system */ +void +SDL_SYS_JoystickDetect() +{ + JoyStick_DeviceData *pCurList = NULL; +#if !SDL_EVENTS_DISABLED + SDL_Event event; +#endif + + /* only enum the devices if the joystick thread told us something changed */ + if (!s_bDeviceAdded && !s_bDeviceRemoved) { + return; /* thread hasn't signaled, nothing to do right now. */ + } + + SDL_LockMutex(s_mutexJoyStickEnum); + + s_bDeviceAdded = SDL_FALSE; + s_bDeviceRemoved = SDL_FALSE; + + pCurList = SYS_Joystick; + SYS_Joystick = NULL; + + /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */ + SDL_DINPUT_JoystickDetect(&pCurList); + + /* Look for XInput devices. Do this last, so they're first in the final list. */ + SDL_XINPUT_JoystickDetect(&pCurList); + + SDL_UnlockMutex(s_mutexJoyStickEnum); + + while (pCurList) { + JoyStick_DeviceData *pListNext = NULL; + + if (pCurList->bXInputDevice) { + SDL_XINPUT_MaybeRemoveDevice(pCurList->XInputUserId); + } else { + SDL_DINPUT_MaybeRemoveDevice(&pCurList->dxdevice); + } + +#if !SDL_EVENTS_DISABLED + SDL_zero(event); + event.type = SDL_JOYDEVICEREMOVED; + + if (SDL_GetEventState(event.type) == SDL_ENABLE) { + event.jdevice.which = pCurList->nInstanceID; + if ((!SDL_EventOK) || (*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 (pNewJoystick->bXInputDevice) { + SDL_XINPUT_MaybeAddDevice(pNewJoystick->XInputUserId); + } else { + SDL_DINPUT_MaybeAddDevice(&pNewJoystick->dxdevice); + } + +#if !SDL_EVENTS_DISABLED + SDL_zero(event); + event.type = SDL_JOYDEVICEADDED; + + if (SDL_GetEventState(event.type) == SDL_ENABLE) { + event.jdevice.which = device_index; + if ((!SDL_EventOK) || (*SDL_EventOK) (SDL_EventOKParam, &event)) { + SDL_PushEvent(&event); + } + } +#endif /* !SDL_EVENTS_DISABLED */ + pNewJoystick->send_add_event = SDL_FALSE; + } + device_index++; + pNewJoystick = pNewJoystick->pNext; + } + } +} + +/* Function to get the device-dependent name of a joystick */ +const char * +SDL_SYS_JoystickNameForDeviceIndex(int device_index) +{ + JoyStick_DeviceData *device = SYS_Joystick; + + for (; device_index > 0; device_index--) + device = device->pNext; + + return device->joystickname; +} + +/* 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; +} + +/* 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. + */ +int +SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) +{ + JoyStick_DeviceData *joystickdevice = SYS_Joystick; + + for (; device_index > 0; device_index--) + joystickdevice = joystickdevice->pNext; + + /* allocate memory for system specific hardware data */ + joystick->instance_id = joystickdevice->nInstanceID; + joystick->closed = SDL_FALSE; + joystick->hwdata = + (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata)); + if (joystick->hwdata == NULL) { + return SDL_OutOfMemory(); + } + SDL_zerop(joystick->hwdata); + joystick->hwdata->guid = joystickdevice->guid; + + if (joystickdevice->bXInputDevice) { + return SDL_XINPUT_JoystickOpen(joystick, joystickdevice); + } else { + return SDL_DINPUT_JoystickOpen(joystick, joystickdevice); + } +} + +/* return true if this joystick is plugged in right now */ +SDL_bool +SDL_SYS_JoystickAttached(SDL_Joystick * joystick) +{ + return !joystick->closed && !joystick->hwdata->removed; +} + +void +SDL_SYS_JoystickUpdate(SDL_Joystick * joystick) +{ + if (joystick->closed || !joystick->hwdata) { + return; + } + + if (joystick->hwdata->bXInputDevice) { + SDL_XINPUT_JoystickUpdate(joystick); + } else { + SDL_DINPUT_JoystickUpdate(joystick); + } + + if (joystick->hwdata->removed) { + joystick->closed = SDL_TRUE; + joystick->uncentered = SDL_TRUE; + } +} + +/* Function to close a joystick after use */ +void +SDL_SYS_JoystickClose(SDL_Joystick * joystick) +{ + if (joystick->hwdata->bXInputDevice) { + SDL_XINPUT_JoystickClose(joystick); + } else { + SDL_DINPUT_JoystickClose(joystick); + } + + /* free system specific hardware data */ + SDL_free(joystick->hwdata); + + joystick->closed = SDL_TRUE; +} + +/* Function to perform any system-specific joystick related cleanup */ +void +SDL_SYS_JoystickQuit(void) +{ + JoyStick_DeviceData *device = SYS_Joystick; + + 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; + } + + SDL_DINPUT_JoystickQuit(); + SDL_XINPUT_JoystickQuit(); +} + +/* return the stable device guid for this device index */ +SDL_JoystickGUID +SDL_SYS_JoystickGetDeviceGUID(int device_index) +{ + JoyStick_DeviceData *device = SYS_Joystick; + int index; + + for (index = device_index; index > 0; index--) + device = device->pNext; + + return device->guid; +} + +SDL_JoystickGUID +SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick) +{ + return joystick->hwdata->guid; +} + +#endif /* SDL_JOYSTICK_DINPUT || SDL_JOYSTICK_XINPUT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/windows/SDL_windowsjoystick_c.h b/src/joystick/windows/SDL_windowsjoystick_c.h new file mode 100644 index 0000000000000..703b0ccd92349 --- /dev/null +++ b/src/joystick/windows/SDL_windowsjoystick_c.h @@ -0,0 +1,89 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2014 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#include "SDL_events.h" +#include "SDL_joystick.h" +#include "../SDL_sysjoystick.h" +#include "../../core/windows/SDL_windows.h" +#include "../../core/windows/SDL_directx.h" + +#define MAX_INPUTS 256 /* each joystick can have up to 256 inputs */ + +typedef struct JoyStick_DeviceData +{ + SDL_JoystickGUID guid; + char *joystickname; + Uint8 send_add_event; + SDL_JoystickID nInstanceID; + SDL_bool bXInputDevice; + BYTE SubType; + Uint8 XInputUserId; + DIDEVICEINSTANCE dxdevice; + struct JoyStick_DeviceData *pNext; +} JoyStick_DeviceData; + +extern JoyStick_DeviceData *SYS_Joystick; /* array to hold joystick ID values */ + +typedef enum Type +{ + BUTTON, + AXIS, + HAT +} Type; + +typedef struct input_t +{ + /* DirectInput offset for this input type: */ + DWORD ofs; + + /* Button, axis or hat: */ + Type type; + + /* SDL input offset: */ + Uint8 num; +} input_t; + +/* The private structure used to keep track of a joystick */ +struct joystick_hwdata +{ + SDL_JoystickGUID guid; + SDL_bool removed; + SDL_bool send_remove_event; + +#if SDL_JOYSTICK_DINPUT + LPDIRECTINPUTDEVICE8 InputDevice; + DIDEVCAPS Capabilities; + SDL_bool buffered; + input_t Inputs[MAX_INPUTS]; + int NumInputs; + int NumSliders; +#endif + + SDL_bool bXInputDevice; /* SDL_TRUE if this device supports using the xinput API rather than DirectInput */ + SDL_bool bXInputHaptic; /* Supports force feedback via XInput. */ + Uint8 userid; /* XInput userid index for this joystick */ + DWORD dwPacketNumber; +}; + +extern void SDL_SYS_AddJoystickDevice(JoyStick_DeviceData *device); + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/windows/SDL_xinputjoystick.c b/src/joystick/windows/SDL_xinputjoystick.c new file mode 100644 index 0000000000000..4a625a5eb950b --- /dev/null +++ b/src/joystick/windows/SDL_xinputjoystick.c @@ -0,0 +1,390 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2014 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#include "SDL_assert.h" +#include "SDL_hints.h" +#include "../SDL_sysjoystick.h" +#include "SDL_windowsjoystick_c.h" +#include "SDL_xinputjoystick_c.h" + + +#if SDL_JOYSTICK_XINPUT + +/* + * Internal stuff. + */ +static SDL_bool s_bXInputEnabled = SDL_TRUE; + + +static SDL_bool +SDL_XInputUseOldJoystickMapping() +{ + static int s_XInputUseOldJoystickMapping = -1; + if (s_XInputUseOldJoystickMapping < 0) { + const char *hint = SDL_GetHint(SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING); + s_XInputUseOldJoystickMapping = (hint && *hint == '1') ? 1 : 0; + } + return (s_XInputUseOldJoystickMapping > 0); +} + +SDL_bool SDL_XINPUT_Enabled(void) +{ + return s_bXInputEnabled; +} + +int +SDL_XINPUT_JoystickInit(void) +{ + const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED); + if (env && !SDL_atoi(env)) { + s_bXInputEnabled = SDL_FALSE; + } + + if (s_bXInputEnabled && WIN_LoadXInputDLL() < 0) { + s_bXInputEnabled = SDL_FALSE; /* oh well. */ + } + return 0; +} + +static char * +GetXInputName(const Uint8 userid, BYTE SubType) +{ + char name[32]; + + if (SDL_XInputUseOldJoystickMapping()) { + SDL_snprintf(name, sizeof(name), "X360 Controller #%u", 1 + userid); + } else { + switch (SubType) { + case XINPUT_DEVSUBTYPE_GAMEPAD: + SDL_snprintf(name, sizeof(name), "XInput Controller #%u", 1 + userid); + break; + case XINPUT_DEVSUBTYPE_WHEEL: + SDL_snprintf(name, sizeof(name), "XInput Wheel #%u", 1 + userid); + break; + case XINPUT_DEVSUBTYPE_ARCADE_STICK: + SDL_snprintf(name, sizeof(name), "XInput ArcadeStick #%u", 1 + userid); + break; + case XINPUT_DEVSUBTYPE_FLIGHT_STICK: + SDL_snprintf(name, sizeof(name), "XInput FlightStick #%u", 1 + userid); + break; + case XINPUT_DEVSUBTYPE_DANCE_PAD: + SDL_snprintf(name, sizeof(name), "XInput DancePad #%u", 1 + userid); + break; + case XINPUT_DEVSUBTYPE_GUITAR: + case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE: + case XINPUT_DEVSUBTYPE_GUITAR_BASS: + SDL_snprintf(name, sizeof(name), "XInput Guitar #%u", 1 + userid); + break; + case XINPUT_DEVSUBTYPE_DRUM_KIT: + SDL_snprintf(name, sizeof(name), "XInput DrumKit #%u", 1 + userid); + break; + case XINPUT_DEVSUBTYPE_ARCADE_PAD: + SDL_snprintf(name, sizeof(name), "XInput ArcadePad #%u", 1 + userid); + break; + default: + SDL_snprintf(name, sizeof(name), "XInput Device #%u", 1 + userid); + break; + } + } + return SDL_strdup(name); +} + +static void +AddXInputDevice(const Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext) +{ + JoyStick_DeviceData *pPrevJoystick = NULL; + JoyStick_DeviceData *pNewJoystick = *pContext; + + if (SDL_XInputUseOldJoystickMapping() && SubType != XINPUT_DEVSUBTYPE_GAMEPAD) + return; + + if (SubType == XINPUT_DEVSUBTYPE_UNKNOWN) + return; + + while (pNewJoystick) { + if (pNewJoystick->bXInputDevice && (pNewJoystick->XInputUserId == userid) && (pNewJoystick->SubType == SubType)) { + /* if we are replacing the front of the list then update it */ + if (pNewJoystick == *pContext) { + *pContext = pNewJoystick->pNext; + } else if (pPrevJoystick) { + pPrevJoystick->pNext = pNewJoystick->pNext; + } + + pNewJoystick->pNext = SYS_Joystick; + SYS_Joystick = pNewJoystick; + return; /* already in the list. */ + } + + pPrevJoystick = pNewJoystick; + pNewJoystick = pNewJoystick->pNext; + } + + pNewJoystick = (JoyStick_DeviceData *)SDL_malloc(sizeof(JoyStick_DeviceData)); + if (!pNewJoystick) { + return; /* better luck next time? */ + } + SDL_zerop(pNewJoystick); + + pNewJoystick->joystickname = GetXInputName(userid, SubType); + if (!pNewJoystick->joystickname) { + SDL_free(pNewJoystick); + return; /* better luck next time? */ + } + + pNewJoystick->bXInputDevice = SDL_TRUE; + if (SDL_XInputUseOldJoystickMapping()) { + SDL_zero(pNewJoystick->guid); + } else { + pNewJoystick->guid.data[0] = 'x'; + pNewJoystick->guid.data[1] = 'i'; + pNewJoystick->guid.data[2] = 'n'; + pNewJoystick->guid.data[3] = 'p'; + pNewJoystick->guid.data[4] = 'u'; + pNewJoystick->guid.data[5] = 't'; + pNewJoystick->guid.data[6] = SubType; + } + pNewJoystick->SubType = SubType; + pNewJoystick->XInputUserId = userid; + SDL_SYS_AddJoystickDevice(pNewJoystick); +} + +void +SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext) +{ + int iuserid; + + if (!s_bXInputEnabled) { + return; + } + + /* iterate in reverse, so these are in the final list in ascending numeric order. */ + for (iuserid = XUSER_MAX_COUNT - 1; iuserid >= 0; iuserid--) { + const Uint8 userid = (Uint8)iuserid; + XINPUT_CAPABILITIES capabilities; + if (XINPUTGETCAPABILITIES(userid, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) { + AddXInputDevice(userid, capabilities.SubType, pContext); + } + } +} + +int +SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice) +{ + const Uint8 userId = joystickdevice->XInputUserId; + XINPUT_CAPABILITIES capabilities; + XINPUT_VIBRATION state; + + SDL_assert(s_bXInputEnabled); + SDL_assert(XINPUTGETCAPABILITIES); + SDL_assert(XINPUTSETSTATE); + SDL_assert(userId >= 0); + SDL_assert(userId < XUSER_MAX_COUNT); + + joystick->hwdata->bXInputDevice = SDL_TRUE; + + if (XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities) != ERROR_SUCCESS) { + SDL_free(joystick->hwdata); + joystick->hwdata = NULL; + return SDL_SetError("Failed to obtain XInput device capabilities. Device disconnected?"); + } + SDL_zero(state); + joystick->hwdata->bXInputHaptic = (XINPUTSETSTATE(userId, &state) == ERROR_SUCCESS); + joystick->hwdata->userid = userId; + + /* The XInput API has a hard coded button/axis mapping, so we just match it */ + if (SDL_XInputUseOldJoystickMapping()) { + joystick->naxes = 6; + joystick->nbuttons = 15; + } else { + joystick->naxes = 6; + joystick->nbuttons = 11; + joystick->nhats = 1; + } + return 0; +} + +static void +UpdateXInputJoystickState_OLD(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState) +{ + static WORD s_XInputButtons[] = { + XINPUT_GAMEPAD_DPAD_UP, XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_RIGHT, + XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB, + XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_GUIDE + }; + WORD wButtons = pXInputState->Gamepad.wButtons; + Uint8 button; + Uint8 hat = SDL_HAT_CENTERED; + + SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX); + SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY))); + SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX); + SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY))); + SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768)); + SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768)); + + for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) { + SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED); + } +} + +static void +UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState) +{ + static WORD s_XInputButtons[] = { + XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB, + XINPUT_GAMEPAD_GUIDE + }; + WORD wButtons = pXInputState->Gamepad.wButtons; + Uint8 button; + Uint8 hat = SDL_HAT_CENTERED; + + SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX); + SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY))); + SDL_PrivateJoystickAxis(joystick, 2, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768)); + SDL_PrivateJoystickAxis(joystick, 3, (Sint16)pXInputState->Gamepad.sThumbRX); + SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY))); + SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768)); + + for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) { + SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED); + } + + if (wButtons & XINPUT_GAMEPAD_DPAD_UP) { + hat |= SDL_HAT_UP; + } + if (wButtons & XINPUT_GAMEPAD_DPAD_DOWN) { + hat |= SDL_HAT_DOWN; + } + if (wButtons & XINPUT_GAMEPAD_DPAD_LEFT) { + hat |= SDL_HAT_LEFT; + } + if (wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) { + hat |= SDL_HAT_RIGHT; + } + SDL_PrivateJoystickHat(joystick, 0, hat); +} + +void +SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick) +{ + HRESULT result; + XINPUT_STATE_EX XInputState; + + if (!XINPUTGETSTATE) + return; + + result = XINPUTGETSTATE(joystick->hwdata->userid, &XInputState); + if (result == ERROR_DEVICE_NOT_CONNECTED) { + joystick->hwdata->send_remove_event = SDL_TRUE; + joystick->hwdata->removed = SDL_TRUE; + return; + } + + /* only fire events if the data changed from last time */ + if (XInputState.dwPacketNumber && XInputState.dwPacketNumber != joystick->hwdata->dwPacketNumber) { + if (SDL_XInputUseOldJoystickMapping()) { + UpdateXInputJoystickState_OLD(joystick, &XInputState); + } else { + UpdateXInputJoystickState(joystick, &XInputState); + } + joystick->hwdata->dwPacketNumber = XInputState.dwPacketNumber; + } +} + +void +SDL_XINPUT_JoystickClose(SDL_Joystick * joystick) +{ +} + +void +SDL_XINPUT_JoystickQuit(void) +{ + if (s_bXInputEnabled) { + WIN_UnloadXInputDLL(); + } +} + +SDL_bool +SDL_SYS_IsXInputGamepad_DeviceIndex(int device_index) +{ + JoyStick_DeviceData *device = SYS_Joystick; + int index; + + for (index = device_index; index > 0; index--) + device = device->pNext; + + return (device->SubType == XINPUT_DEVSUBTYPE_GAMEPAD); +} + +#else /* !SDL_JOYSTICK_XINPUT */ + + +SDL_bool SDL_XINPUT_Enabled(void) +{ + return SDL_FALSE; +} + +int +SDL_XINPUT_JoystickInit(void) +{ + return 0; +} + +void +SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext) +{ +} + +int +SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice) +{ + return SDL_Unsupported(); +} + +void +SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick) +{ +} + +void +SDL_XINPUT_JoystickClose(SDL_Joystick * joystick) +{ +} + +void +SDL_XINPUT_JoystickQuit(void) +{ +} + +SDL_bool +SDL_SYS_IsXInputGamepad_DeviceIndex(int device_index) +{ + return SDL_FALSE; +} + +#endif /* SDL_JOYSTICK_XINPUT */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/windows/SDL_xinputjoystick_c.h b/src/joystick/windows/SDL_xinputjoystick_c.h new file mode 100644 index 0000000000000..825c292092978 --- /dev/null +++ b/src/joystick/windows/SDL_xinputjoystick_c.h @@ -0,0 +1,33 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2014 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#include "../../core/windows/SDL_xinput.h" + +extern SDL_bool SDL_XINPUT_Enabled(void); +extern int SDL_XINPUT_JoystickInit(void); +extern void SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext); +extern int SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice); +extern void SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick); +extern void SDL_XINPUT_JoystickClose(SDL_Joystick * joystick); +extern void SDL_XINPUT_JoystickQuit(void); + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/winrt/SDL_xinputjoystick.c b/src/joystick/winrt/SDL_xinputjoystick.c deleted file mode 100644 index 197d505469006..0000000000000 --- a/src/joystick/winrt/SDL_xinputjoystick.c +++ /dev/null @@ -1,551 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2014 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "../../SDL_internal.h" - -#if SDL_JOYSTICK_XINPUT - -/* SDL_xinputjoystick.c implements an XInput-only joystick and game controller - backend that is suitable for use on WinRT. SDL's DirectInput backend, also - XInput-capable, was not used as DirectInput is not available on WinRT (or, - at least, it isn't a public API). Some portions of this XInput backend - may copy parts of the XInput-using code from the DirectInput backend. - Refactoring the common parts into one location may be good to-do at some - point. - - TODO, WinRT: add hotplug support for XInput based game controllers -*/ - -#include "SDL_joystick.h" -#include "../SDL_sysjoystick.h" -#include "../SDL_joystick_c.h" -#include "SDL_events.h" -#include "../../events/SDL_events_c.h" -#include "SDL_timer.h" - -#include -#include - -#ifndef XINPUT_GAMEPAD_GUIDE -#define XINPUT_GAMEPAD_GUIDE 0x0400 -#endif - -struct joystick_hwdata { - //Uint8 bXInputHaptic; // Supports force feedback via XInput. - DWORD userIndex; // The XInput device index, in the range [0, XUSER_MAX_COUNT-1] (probably [0,3]). - XINPUT_STATE XInputState; // the last-read in XInputState, kept around to compare old and new values - SDL_bool isDeviceConnected; // was the device connected (on the last detection-polling, or during backend-initialization)? - SDL_bool isDeviceConnectionEventPending; // was a device added, and is the associated add-event pending? - SDL_bool isDeviceRemovalEventPending; // was the device removed, and is the associated remove-event pending? -}; - -/* Keep track of data on all XInput devices, regardless of whether or not - they've been opened (via SDL_JoystickOpen). - */ -static struct joystick_hwdata g_XInputData[XUSER_MAX_COUNT]; - -/* Device detection can be extremely costly performance-wise, in some cases. - In particular, if no devices are connected, calls to detect a single device, - via either XInputGetState() or XInputGetCapabilities(), can take upwards of - 20 ms on a 1st generation Surface RT, more if devices are detected across - all of of XInput's four device slots. WinRT and XInput do not appear to - have callback-based APIs to notify an app when a device is connected, at - least as of Windows 8.1. The synchronous XInput calls must be used. - - Once a device is connected, calling XInputGetState() is a much less costly - operation, with individual calls costing well under 1 ms, and often under - 0.1 ms [on a 1st gen Surface RT]. - - With XInput's performance limitations in mind, a separate device-detection - thread will be utilized (by SDL) to try to move costly XInput calls off the - main thread. Polling of active devices still, however, occurs on the main - thread. - */ -static SDL_Thread * g_DeviceDetectionThread = NULL; -static SDL_mutex * g_DeviceInfoLock = NULL; -static SDL_bool g_DeviceDetectionQuit = SDL_FALSE; - -/* Main function for the device-detection thread. - */ -static int -DeviceDetectionThreadMain(void * _data) -{ - DWORD result; - XINPUT_CAPABILITIES tempXInputCaps; - int i; - - while (1) { - /* See if the device-detection thread is being asked to shutdown. - */ - SDL_LockMutex(g_DeviceInfoLock); - if (g_DeviceDetectionQuit) { - SDL_UnlockMutex(g_DeviceInfoLock); - break; - } - SDL_UnlockMutex(g_DeviceInfoLock); - - /* Add a short delay to prevent the device-detection thread from eating - up too much CPU time: - */ - SDL_Delay(300); - - /* TODO, WinRT: try making the device-detection thread wakeup sooner from its CPU-preserving SDL_Delay, if the thread was asked to quit. - */ - - /* See if any new devices are connected. */ - SDL_LockMutex(g_DeviceInfoLock); - for (i = 0; i < XUSER_MAX_COUNT; ++i) { - if (!g_XInputData[i].isDeviceConnected && - !g_XInputData[i].isDeviceConnectionEventPending && - !g_XInputData[i].isDeviceRemovalEventPending) - { - SDL_UnlockMutex(g_DeviceInfoLock); - result = XInputGetCapabilities(i, 0, &tempXInputCaps); - SDL_LockMutex(g_DeviceInfoLock); - if (result == ERROR_SUCCESS) { - /* Yes, a device is connected. Mark it as such. - Others will be told about this (via an - SDL_JOYDEVICEADDED event) in the next call to - SDL_SYS_JoystickDetect. - */ - g_XInputData[i].isDeviceConnected = SDL_TRUE; - g_XInputData[i].isDeviceConnectionEventPending = SDL_TRUE; - } - } - } - SDL_UnlockMutex(g_DeviceInfoLock); - } - - return 0; -} - -/* Function to scan the system for joysticks. - * It should return 0, or -1 on an unrecoverable fatal error. - */ -int -SDL_SYS_JoystickInit(void) -{ - HRESULT result = S_OK; - XINPUT_STATE tempXInputState; - int i; - - SDL_zero(g_XInputData); - - /* Make initial notes on whether or not devices are connected (or not). - */ - for (i = 0; i < XUSER_MAX_COUNT; ++i) { - result = XInputGetState(i, &tempXInputState); - if (result == ERROR_SUCCESS) { - g_XInputData[i].isDeviceConnected = SDL_TRUE; - } - } - - /* Start up the device-detection thread. - */ - g_DeviceDetectionQuit = SDL_FALSE; - g_DeviceInfoLock = SDL_CreateMutex(); - g_DeviceDetectionThread = SDL_CreateThread(DeviceDetectionThreadMain, "SDL_joystick", NULL); - - return (0); -} - -int SDL_SYS_NumJoysticks() -{ - int joystickCount = 0; - DWORD i; - - /* Iterate through each possible XInput device and see if something - was connected (at joystick init, or during the last polling). - */ - SDL_LockMutex(g_DeviceInfoLock); - for (i = 0; i < XUSER_MAX_COUNT; ++i) { - if (g_XInputData[i].isDeviceConnected) { - ++joystickCount; - } - } - SDL_UnlockMutex(g_DeviceInfoLock); - - return joystickCount; -} - -void SDL_SYS_JoystickDetect() -{ - DWORD i; - SDL_Event event; - - /* Iterate through each possible XInput device, seeing if any devices - have been connected, or if they were removed. - */ - SDL_LockMutex(g_DeviceInfoLock); - for (i = 0; i < XUSER_MAX_COUNT; ++i) { - /* See if any new devices are connected. */ - if (g_XInputData[i].isDeviceConnectionEventPending) { -#if !SDL_EVENTS_DISABLED - SDL_zero(event); - event.type = SDL_JOYDEVICEADDED; - - if (SDL_GetEventState(event.type) == SDL_ENABLE) { - event.jdevice.which = i; - if ((SDL_EventOK == NULL) - || (*SDL_EventOK) (SDL_EventOKParam, &event)) { - SDL_PushEvent(&event); - } - } -#endif - g_XInputData[i].isDeviceConnectionEventPending = SDL_FALSE; - } else if (g_XInputData[i].isDeviceRemovalEventPending) { - /* A device was previously marked as removed (by - SDL_SYS_JoystickUpdate). Tell others about the device removal. - */ - - g_XInputData[i].isDeviceRemovalEventPending = SDL_FALSE; - -#if !SDL_EVENTS_DISABLED - SDL_zero(event); - event.type = SDL_JOYDEVICEREMOVED; - - if (SDL_GetEventState(event.type) == SDL_ENABLE) { - event.jdevice.which = i; //joystick->hwdata->userIndex; - if ((SDL_EventOK == NULL) - || (*SDL_EventOK) (SDL_EventOKParam, &event)) { - SDL_PushEvent(&event); - } - } -#endif - } - } - SDL_UnlockMutex(g_DeviceInfoLock); -} - - -/* Internal function to retreive device capabilities. - This function will return an SDL-standard value of 0 on success - (a device is connected, and data on it was retrieved), or -1 - on failure (no device was connected, or some other error - occurred. SDL_SetError() will be invoked to set an appropriate - error message. - */ -static int -SDL_XInput_GetDeviceCapabilities(int device_index, XINPUT_CAPABILITIES * pDeviceCaps) -{ - HRESULT dwResult; - - /* Make sure that the device index is a valid one. If not, return to the - caller with an error. - */ - if (device_index < 0 || device_index >= XUSER_MAX_COUNT) { - return SDL_SetError("invalid/unavailable device index"); - } - - /* See if a device exists, and if so, what its capabilities are. If a - device is not available, return to the caller with an error. - */ - switch ((dwResult = XInputGetCapabilities(device_index, 0, pDeviceCaps))) { - case ERROR_SUCCESS: - /* A device is available, and its capabilities were retrieved! */ - return 0; - case ERROR_DEVICE_NOT_CONNECTED: - return SDL_SetError("no device is connected at joystick index, %d", device_index); - default: - return SDL_SetError("an unknown error occurred when retrieving info on a device at joystick index, %d", device_index); - } -} - -/* Function to get the device-dependent name of a joystick */ -const char * -SDL_SYS_JoystickNameForDeviceIndex(int device_index) -{ - XINPUT_CAPABILITIES deviceCaps; - - if (SDL_XInput_GetDeviceCapabilities(device_index, &deviceCaps) != 0) { - /* Uh oh. Device capabilities couldn't be retrieved. Return to the - caller. SDL_SetError() has already been invoked (with relevant - information). - */ - return NULL; - } - - switch (deviceCaps.SubType) { - default: - if (deviceCaps.Type == XINPUT_DEVTYPE_GAMEPAD) { - return "Undefined game controller"; - } else { - return "Undefined controller"; - } - case XINPUT_DEVSUBTYPE_UNKNOWN: - if (deviceCaps.Type == XINPUT_DEVTYPE_GAMEPAD) { - return "Unknown game controller"; - } else { - return "Unknown controller"; - } - case XINPUT_DEVSUBTYPE_GAMEPAD: - return "Gamepad controller"; - case XINPUT_DEVSUBTYPE_WHEEL: - return "Racing wheel controller"; - case XINPUT_DEVSUBTYPE_ARCADE_STICK: - return "Arcade stick controller"; - case XINPUT_DEVSUBTYPE_FLIGHT_STICK: - return "Flight stick controller"; - case XINPUT_DEVSUBTYPE_DANCE_PAD: - return "Dance pad controller"; - case XINPUT_DEVSUBTYPE_GUITAR: - return "Guitar controller"; - case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE: - return "Guitar controller, Alternate"; - case XINPUT_DEVSUBTYPE_GUITAR_BASS: - return "Guitar controller, Bass"; - case XINPUT_DEVSUBTYPE_DRUM_KIT: - return "Drum controller"; - case XINPUT_DEVSUBTYPE_ARCADE_PAD: - return "Arcade pad controller"; - } -} - -/* Function to perform the mapping from device index to the instance id for this index */ -SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index) -{ - return 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. - */ -int -SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) -{ - XINPUT_CAPABILITIES deviceCaps; - - if (SDL_XInput_GetDeviceCapabilities(device_index, &deviceCaps) != 0) { - /* Uh oh. Device capabilities couldn't be retrieved. Return to the - caller. SDL_SetError() has already been invoked (with relevant - information). - */ - return -1; - } - - /* For now, only game pads are supported. If the device is something other - than that, return an error to the caller. - */ - if (deviceCaps.Type != XINPUT_DEVTYPE_GAMEPAD) { - return SDL_SetError("a device is connected (at joystick index, %d), but it is of an unknown device type (deviceCaps.Flags=%ul)", - device_index, (unsigned int)deviceCaps.Flags); - } - - /* Create the joystick data structure */ - joystick->instance_id = device_index; - joystick->hwdata = &g_XInputData[device_index]; - - // 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; - - /* We're done! */ - return (0); -} - -/* Function to determine is this joystick is attached to the system right now */ -SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick) -{ - SDL_bool isDeviceConnected; - SDL_LockMutex(g_DeviceInfoLock); - isDeviceConnected = joystick->hwdata->isDeviceConnected; - SDL_UnlockMutex(g_DeviceInfoLock); - return isDeviceConnected; -} - -/* Function to return > 0 if a bit array of buttons differs after applying a mask -*/ -static int ButtonChanged( int ButtonsNow, int ButtonsPrev, int ButtonMask ) -{ - return ( ButtonsNow & ButtonMask ) != ( ButtonsPrev & ButtonMask ); -} - -/* This is an almost-identical copy of UpdateXInputJoystickState from the - DirectInput + XInput backend. - - TODO, WinRT: look into making the DirectInput+Xinput and WinRT/XInput joystick backends share more code, without duplication - TODO, WinRT: consider adding support for the "old" XInput controller mapping (via SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING) -*/ -static void -UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE *pXInputState) -{ - static WORD s_XInputButtons[] = { - XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y, - XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_START, - XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB, - XINPUT_GAMEPAD_GUIDE - }; - WORD wButtons = pXInputState->Gamepad.wButtons; - Uint8 button; - Uint8 hat = SDL_HAT_CENTERED; - - SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX); - SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY))); - SDL_PrivateJoystickAxis(joystick, 2, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger * 65535 / 255) - 32768)); - SDL_PrivateJoystickAxis(joystick, 3, (Sint16)pXInputState->Gamepad.sThumbRX); - SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY))); - SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger * 65535 / 255) - 32768)); - - for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) { - SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED); - } - - if (wButtons & XINPUT_GAMEPAD_DPAD_UP) { - hat |= SDL_HAT_UP; - } - if (wButtons & XINPUT_GAMEPAD_DPAD_DOWN) { - hat |= SDL_HAT_DOWN; - } - if (wButtons & XINPUT_GAMEPAD_DPAD_LEFT) { - hat |= SDL_HAT_LEFT; - } - if (wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) { - hat |= SDL_HAT_RIGHT; - } - SDL_PrivateJoystickHat(joystick, 0, hat); -} - -/* Function to update the state of a joystick - called as a device poll. - * This function shouldn't update the joystick structure directly, - * but instead should call SDL_PrivateJoystick*() to deliver events - * and update joystick device state. - */ -void -SDL_SYS_JoystickUpdate(SDL_Joystick * joystick) -{ - HRESULT result; - XINPUT_STATE prevXInputState; - - SDL_LockMutex(g_DeviceInfoLock); - - /* Before polling for new data, make note of the old data */ - prevXInputState = joystick->hwdata->XInputState; - - /* Poll for new data */ - result = XInputGetState(joystick->hwdata->userIndex, &joystick->hwdata->XInputState); - if (result == ERROR_DEVICE_NOT_CONNECTED) { - if (joystick->hwdata->isDeviceConnected) { - joystick->hwdata->isDeviceConnected = SDL_FALSE; - joystick->hwdata->isDeviceRemovalEventPending = SDL_TRUE; - /* TODO, WinRT: make sure isDeviceRemovalEventPending gets cleared as appropriate, and that quick re-plugs don't cause trouble */ - } - SDL_UnlockMutex(g_DeviceInfoLock); - return; - } - - /* Make sure the device is marked as connected */ - joystick->hwdata->isDeviceConnected = SDL_TRUE; - - // only fire events if the data changed from last time - if ( joystick->hwdata->XInputState.dwPacketNumber != 0 - && joystick->hwdata->XInputState.dwPacketNumber != prevXInputState.dwPacketNumber ) - { - XINPUT_STATE *pXInputState = &joystick->hwdata->XInputState; - UpdateXInputJoystickState(joystick, pXInputState); - } - - SDL_UnlockMutex(g_DeviceInfoLock); -} - -/* Function to close a joystick after use */ -void -SDL_SYS_JoystickClose(SDL_Joystick * joystick) -{ - /* Clear cached button data on the joystick */ - SDL_LockMutex(g_DeviceInfoLock); - SDL_zero(joystick->hwdata->XInputState); - SDL_UnlockMutex(g_DeviceInfoLock); - - /* There's need to free 'hwdata', as it's a pointer to a global array. - The field will be cleared anyways, just to indicate that it's not - currently needed. - */ - joystick->hwdata = NULL; -} - -/* Function to perform any system-specific joystick related cleanup */ -void -SDL_SYS_JoystickQuit(void) -{ - /* Tell the joystick detection thread to stop, then wait for it to finish */ - SDL_LockMutex(g_DeviceInfoLock); - g_DeviceDetectionQuit = SDL_TRUE; - SDL_UnlockMutex(g_DeviceInfoLock); - SDL_WaitThread(g_DeviceDetectionThread, NULL); - - /* Clean up device-detection stuff */ - SDL_DestroyMutex(g_DeviceInfoLock); - g_DeviceInfoLock = NULL; - g_DeviceDetectionThread = NULL; - g_DeviceDetectionQuit = SDL_FALSE; - - return; -} - -SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index ) -{ - SDL_JoystickGUID guid; - // the GUID is just the first 16 chars of the name for now - const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index ); - SDL_zero( guid ); - SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) ); - return guid; -} - - -SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick) -{ - SDL_JoystickGUID guid; - // the GUID is just the first 16 chars of the name for now - const char *name = joystick->name; - SDL_zero( guid ); - SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) ); - return guid; -} - -SDL_bool SDL_SYS_IsXInputDeviceIndex(int device_index) -{ - /* The XInput-capable DirectInput joystick backend implements the same - function (SDL_SYS_IsXInputDeviceIndex), however in that case, not all - joystick devices are XInput devices. In this case, with the - WinRT-enabled XInput-only backend, all "joystick" devices are XInput - devices. - */ - return SDL_TRUE; -} - -SDL_bool SDL_SYS_IsXInputGamepad_DeviceIndex(int device_index) -{ - XINPUT_CAPABILITIES deviceCaps; - - if (SDL_XInput_GetDeviceCapabilities(device_index, &deviceCaps) != 0) { - return SDL_FALSE; - } - - return (deviceCaps.SubType == XINPUT_DEVSUBTYPE_GAMEPAD); -} - -#endif /* SDL_JOYSTICK_XINPUT */ - -/* vi: set ts=4 sw=4 expandtab: */