2013-05-22 16:45:01 +00:00
# include < limits . h >
2013-07-22 22:06:07 +00:00
2013-07-06 17:08:59 +00:00
# include "base/NativeApp.h"
2013-04-16 14:34:20 +00:00
# include "Core/Config.h"
2013-03-30 23:25:10 +00:00
# include "input/input_state.h"
2013-07-06 17:08:59 +00:00
# include "input/keycodes.h"
2013-05-17 10:31:06 +00:00
# include "XinputDevice.h"
# include "ControlMapping.h"
2013-07-22 22:06:07 +00:00
// Utilities to dynamically load XInput. Adapted from SDL.
typedef DWORD ( WINAPI * XInputGetState_t ) ( DWORD dwUserIndex , XINPUT_STATE * pState ) ;
typedef DWORD ( WINAPI * XInputSetState_t ) ( DWORD dwUserIndex , XINPUT_VIBRATION * pVibration ) ;
typedef DWORD ( WINAPI * XInputGetCapabilities_t ) ( DWORD dwUserIndex , DWORD dwFlags , XINPUT_CAPABILITIES * pCapabilities ) ;
XInputGetState_t PPSSPP_XInputGetState = NULL ;
XInputSetState_t PPSSPP_XInputSetState = NULL ;
XInputGetCapabilities_t PPSSPP_XInputGetCapabilities = NULL ;
static DWORD PPSSPP_XInputVersion = 0 ;
static HMODULE s_pXInputDLL = 0 ;
static int s_XInputDLLRefCount = 0 ;
static void UnloadXInputDLL ( ) ;
static int LoadXInputDLL ( ) {
DWORD version = 0 ;
if ( s_pXInputDLL ) {
s_XInputDLLRefCount + + ;
return 0 ; /* already loaded */
}
version = ( 1 < < 16 ) | 4 ;
2013-08-26 17:00:16 +00:00
s_pXInputDLL = LoadLibrary ( L " XInput1_4.dll " ) ; // 1.4 Ships with Windows 8.
2013-07-22 22:06:07 +00:00
if ( ! s_pXInputDLL ) {
version = ( 1 < < 16 ) | 3 ;
2013-08-26 17:00:16 +00:00
s_pXInputDLL = LoadLibrary ( L " XInput1_3.dll " ) ; // 1.3 Ships with Vista and Win7, can be installed as a restributable component.
2013-07-22 22:06:07 +00:00
}
if ( ! s_pXInputDLL ) {
return - 1 ;
}
PPSSPP_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... */
PPSSPP_XInputGetState = ( XInputGetState_t ) GetProcAddress ( ( HMODULE ) s_pXInputDLL , ( LPCSTR ) 100 ) ;
PPSSPP_XInputSetState = ( XInputSetState_t ) GetProcAddress ( ( HMODULE ) s_pXInputDLL , " XInputSetState " ) ;
PPSSPP_XInputGetCapabilities = ( XInputGetCapabilities_t ) GetProcAddress ( ( HMODULE ) s_pXInputDLL , " XInputGetCapabilities " ) ;
if ( ! PPSSPP_XInputGetState | | ! PPSSPP_XInputSetState | | ! PPSSPP_XInputGetCapabilities ) {
UnloadXInputDLL ( ) ;
return - 1 ;
}
return 0 ;
}
static void UnloadXInputDLL ( ) {
if ( s_pXInputDLL ) {
if ( - - s_XInputDLLRefCount = = 0 ) {
FreeLibrary ( s_pXInputDLL ) ;
s_pXInputDLL = NULL ;
}
}
}
2012-11-12 15:38:21 +00:00
2012-11-14 01:15:54 +00:00
# ifndef XUSER_MAX_COUNT
# define XUSER_MAX_COUNT 4
# endif
2013-07-22 22:06:07 +00:00
// Undocumented. Steam annoyingly grabs this button though....
# define XINPUT_GUIDE_BUTTON 0x400
2013-07-06 17:08:59 +00:00
// Permanent map. Actual mapping happens elsewhere.
static const struct { int from , to ; } xinput_ctrl_map [ ] = {
2013-08-04 17:31:40 +00:00
{ XINPUT_GAMEPAD_A , NKCODE_BUTTON_A } ,
{ XINPUT_GAMEPAD_B , NKCODE_BUTTON_B } ,
{ XINPUT_GAMEPAD_X , NKCODE_BUTTON_X } ,
{ XINPUT_GAMEPAD_Y , NKCODE_BUTTON_Y } ,
{ XINPUT_GAMEPAD_BACK , NKCODE_BUTTON_SELECT } ,
{ XINPUT_GAMEPAD_START , NKCODE_BUTTON_START } ,
{ XINPUT_GAMEPAD_LEFT_SHOULDER , NKCODE_BUTTON_L1 } ,
{ XINPUT_GAMEPAD_RIGHT_SHOULDER , NKCODE_BUTTON_R1 } ,
{ XINPUT_GAMEPAD_LEFT_THUMB , NKCODE_BUTTON_THUMBL } ,
{ XINPUT_GAMEPAD_RIGHT_THUMB , NKCODE_BUTTON_THUMBR } ,
{ XINPUT_GAMEPAD_DPAD_UP , NKCODE_DPAD_UP } ,
{ XINPUT_GAMEPAD_DPAD_DOWN , NKCODE_DPAD_DOWN } ,
{ XINPUT_GAMEPAD_DPAD_LEFT , NKCODE_DPAD_LEFT } ,
{ XINPUT_GAMEPAD_DPAD_RIGHT , NKCODE_DPAD_RIGHT } ,
{ XINPUT_GUIDE_BUTTON , NKCODE_HOME } ,
2013-05-17 10:31:06 +00:00
} ;
2013-07-06 17:08:59 +00:00
static const unsigned int xinput_ctrl_map_size = sizeof ( xinput_ctrl_map ) / sizeof ( xinput_ctrl_map [ 0 ] ) ;
2013-05-17 10:31:06 +00:00
2012-11-12 15:38:21 +00:00
XinputDevice : : XinputDevice ( ) {
2013-07-22 22:06:07 +00:00
if ( LoadXInputDLL ( ) ! = 0 ) {
2013-09-07 20:31:14 +00:00
ERROR_LOG ( SCECTRL , " Failed to load XInput! DLL missing " ) ;
2013-07-22 22:06:07 +00:00
}
2012-11-12 15:38:21 +00:00
ZeroMemory ( & this - > prevState , sizeof ( this - > prevState ) ) ;
this - > check_delay = 0 ;
this - > gamepad_idx = - 1 ;
}
2013-07-22 22:06:07 +00:00
XinputDevice : : ~ XinputDevice ( ) {
UnloadXInputDLL ( ) ;
}
2012-11-12 15:38:21 +00:00
struct Stick {
float x ;
float y ;
} ;
2013-03-30 23:25:10 +00:00
static Stick NormalizedDeadzoneFilter ( short x , short y ) ;
2012-11-12 15:38:21 +00:00
2013-03-30 22:32:34 +00:00
int XinputDevice : : UpdateState ( InputState & input_state ) {
2013-07-25 21:58:19 +00:00
if ( ! s_pXInputDLL )
return 0 ;
2012-11-12 15:38:21 +00:00
if ( this - > check_delay - - > 0 ) return - 1 ;
XINPUT_STATE state ;
ZeroMemory ( & state , sizeof ( XINPUT_STATE ) ) ;
DWORD dwResult ;
if ( this - > gamepad_idx > = 0 )
2013-07-22 22:06:07 +00:00
dwResult = PPSSPP_XInputGetState ( this - > gamepad_idx , & state ) ;
2012-11-12 15:38:21 +00:00
else {
// use the first gamepad that responds
for ( int i = 0 ; i < XUSER_MAX_COUNT ; i + + ) {
2013-07-22 22:06:07 +00:00
dwResult = PPSSPP_XInputGetState ( i , & state ) ;
2012-11-12 15:38:21 +00:00
if ( dwResult = = ERROR_SUCCESS ) {
this - > gamepad_idx = i ;
break ;
}
}
}
if ( dwResult = = ERROR_SUCCESS ) {
2013-07-06 17:08:59 +00:00
ApplyButtons ( state , input_state ) ;
2013-04-01 10:35:02 +00:00
2013-07-08 00:18:02 +00:00
if ( prevState . Gamepad . sThumbLX ! = state . Gamepad . sThumbLX | | prevState . Gamepad . sThumbLY ! = state . Gamepad . sThumbLY ) {
Stick left = NormalizedDeadzoneFilter ( state . Gamepad . sThumbLX , state . Gamepad . sThumbLY ) ;
AxisInput axis ;
axis . deviceId = DEVICE_ID_X360_0 ;
axis . axisId = JOYSTICK_AXIS_X ;
axis . value = left . x ;
NativeAxis ( axis ) ;
axis . axisId = JOYSTICK_AXIS_Y ;
axis . value = left . y ;
NativeAxis ( axis ) ;
}
if ( prevState . Gamepad . sThumbRX ! = state . Gamepad . sThumbRX | | prevState . Gamepad . sThumbRY ! = state . Gamepad . sThumbRY ) {
Stick right = NormalizedDeadzoneFilter ( state . Gamepad . sThumbRX , state . Gamepad . sThumbRY ) ;
AxisInput axis ;
axis . deviceId = DEVICE_ID_X360_0 ;
axis . axisId = JOYSTICK_AXIS_Z ;
axis . value = right . x ;
NativeAxis ( axis ) ;
axis . axisId = JOYSTICK_AXIS_RZ ;
axis . value = right . y ;
NativeAxis ( axis ) ;
}
2013-04-01 10:35:02 +00:00
2013-08-17 09:18:45 +00:00
if ( prevState . Gamepad . bLeftTrigger ! = state . Gamepad . bLeftTrigger ) {
AxisInput axis ;
axis . deviceId = DEVICE_ID_X360_0 ;
axis . axisId = JOYSTICK_AXIS_LTRIGGER ;
axis . value = ( float ) state . Gamepad . bLeftTrigger / 255.0f ;
NativeAxis ( axis ) ;
}
if ( prevState . Gamepad . bRightTrigger ! = state . Gamepad . bRightTrigger ) {
AxisInput axis ;
axis . deviceId = DEVICE_ID_X360_0 ;
axis . axisId = JOYSTICK_AXIS_RTRIGGER ;
axis . value = ( float ) state . Gamepad . bRightTrigger / 255.0f ;
NativeAxis ( axis ) ;
}
2012-11-12 15:38:21 +00:00
this - > prevState = state ;
this - > check_delay = 0 ;
2013-04-01 18:15:16 +00:00
// If there's an XInput pad, skip following pads. This prevents DInput and XInput
// from colliding.
2013-04-08 01:41:26 +00:00
return UPDATESTATE_SKIP_PAD ;
2012-11-12 15:38:21 +00:00
} else {
// wait check_delay frames before polling the controller again
this - > gamepad_idx = - 1 ;
this - > check_delay = 100 ;
2012-11-12 17:32:35 +00:00
return - 1 ;
2012-11-12 15:38:21 +00:00
}
}
2013-05-22 16:45:01 +00:00
inline float Clampf ( float val , float min , float max ) {
if ( val < min ) return min ;
if ( val > max ) return max ;
return val ;
}
static Stick NormalizedDeadzoneFilter ( short x , short y ) {
static const float DEADZONE = ( float ) XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE / 32767.0f ;
Stick s ;
s . x = ( float ) x / 32767.0f ;
s . y = ( float ) y / 32767.0f ;
float magnitude = sqrtf ( s . x * s . x + s . y * s . y ) ;
if ( magnitude > DEADZONE ) {
if ( magnitude > 1.0f ) {
s . x * = 1.41421f ;
s . y * = 1.41421f ;
}
s . x = Clampf ( s . x , - 1.0f , 1.0f ) ;
s . y = Clampf ( s . y , - 1.0f , 1.0f ) ;
} else {
s . x = 0.0f ;
s . y = 0.0f ;
}
return s ;
2012-11-12 15:38:21 +00:00
}
2013-07-06 17:08:59 +00:00
void XinputDevice : : ApplyButtons ( XINPUT_STATE & state , InputState & input_state ) {
u32 buttons = state . Gamepad . wButtons ;
2013-07-07 12:08:08 +00:00
u32 downMask = buttons & ( ~ prevButtons ) ;
u32 upMask = ( ~ buttons ) & prevButtons ;
2013-07-06 17:08:59 +00:00
prevButtons = buttons ;
for ( int i = 0 ; i < xinput_ctrl_map_size ; i + + ) {
if ( downMask & xinput_ctrl_map [ i ] . from ) {
KeyInput key ;
key . deviceId = DEVICE_ID_X360_0 ;
key . flags = KEY_DOWN ;
key . keyCode = xinput_ctrl_map [ i ] . to ;
NativeKey ( key ) ;
}
if ( upMask & xinput_ctrl_map [ i ] . from ) {
KeyInput key ;
key . deviceId = DEVICE_ID_X360_0 ;
key . flags = KEY_UP ;
key . keyCode = xinput_ctrl_map [ i ] . to ;
NativeKey ( key ) ;
}
}
2013-05-17 10:31:06 +00:00
}