2020-12-05 13:08:24 +01:00
2018-08-29 14:27:10 +03:00
#ifdef _WIN32
2020-02-15 23:36:20 +01:00
#include "stdafx.h"
2017-06-04 09:48:33 -05:00
#include "xinput_pad_handler.h"
2020-02-22 20:42:49 +01:00
#include "Emu/Io/pad_config.h"
2025-02-23 14:39:39 +01:00
#include "util/dyn_lib.hpp"
2014-05-24 21:05:00 +02:00
2019-09-22 11:22:42 +02:00
namespace XINPUT_INFO
{
const DWORD GUIDE_BUTTON = 0x0400 ;
const LPCWSTR LIBRARY_FILENAMES [] = {
L "xinput1_3.dll" , // Prioritizing 1_3 because of SCP
L "xinput1_4.dll" ,
L "xinput9_1_0.dll"
};
} // namespace XINPUT_INFO
2024-06-11 22:30:16 +02:00
xinput_pad_handler :: xinput_pad_handler () : PadHandlerBase ( pad_handler :: xinput )
2014-05-24 21:05:00 +02:00
{
2019-09-20 16:28:55 +02:00
// Unique names for the config files and our pad settings dialog
button_list =
{
2021-08-08 16:45:45 +02:00
{ XInputKeyCodes :: None , "" },
2019-09-20 16:28:55 +02:00
{ XInputKeyCodes :: A , "A" },
{ XInputKeyCodes :: B , "B" },
{ XInputKeyCodes :: X , "X" },
{ XInputKeyCodes :: Y , "Y" },
{ XInputKeyCodes :: Left , "Left" },
{ XInputKeyCodes :: Right , "Right" },
{ XInputKeyCodes :: Up , "Up" },
{ XInputKeyCodes :: Down , "Down" },
{ XInputKeyCodes :: LB , "LB" },
{ XInputKeyCodes :: RB , "RB" },
{ XInputKeyCodes :: Back , "Back" },
{ XInputKeyCodes :: Start , "Start" },
{ XInputKeyCodes :: LS , "LS" },
{ XInputKeyCodes :: RS , "RS" },
{ XInputKeyCodes :: Guide , "Guide" },
{ XInputKeyCodes :: LT , "LT" },
{ XInputKeyCodes :: RT , "RT" },
2024-08-13 02:08:25 +02:00
{ XInputKeyCodes :: LT_Pos , "LT+" },
{ XInputKeyCodes :: LT_Neg , "LT-" },
{ XInputKeyCodes :: RT_Pos , "RT+" },
{ XInputKeyCodes :: RT_Neg , "RT-" },
2019-09-20 16:28:55 +02:00
{ XInputKeyCodes :: LSXNeg , "LS X-" },
{ XInputKeyCodes :: LSXPos , "LS X+" },
{ XInputKeyCodes :: LSYPos , "LS Y+" },
{ XInputKeyCodes :: LSYNeg , "LS Y-" },
{ XInputKeyCodes :: RSXNeg , "RS X-" },
{ XInputKeyCodes :: RSXPos , "RS X+" },
{ XInputKeyCodes :: RSYPos , "RS Y+" },
{ XInputKeyCodes :: RSYNeg , "RS Y-" }
};
2017-12-23 22:25:51 +01:00
init_configs ();
2017-11-27 22:31:15 +01:00
// Define border values
thumb_max = 32767 ;
trigger_min = 0 ;
trigger_max = 255 ;
// set capabilities
b_has_config = true ;
b_has_rumble = true ;
b_has_deadzones = true ;
2021-03-07 01:53:27 +01:00
b_has_battery = true ;
2024-07-06 17:46:37 +02:00
b_has_battery_led = false ;
2025-01-08 01:22:50 +01:00
b_has_orientation = false ;
2017-11-27 22:31:15 +01:00
2018-01-17 02:23:50 +01:00
m_name_string = "XInput Pad #" ;
2018-01-16 14:07:57 +01:00
m_max_devices = XUSER_MAX_COUNT ;
2017-11-27 22:31:15 +01:00
m_trigger_threshold = trigger_max / 2 ;
m_thumb_threshold = thumb_max / 2 ;
2014-05-24 21:05:00 +02:00
}
2017-06-04 09:48:33 -05:00
xinput_pad_handler ::~ xinput_pad_handler ()
2014-05-24 21:05:00 +02:00
{
2019-09-20 16:28:55 +02:00
if ( library )
{
xinputGetExtended = nullptr ;
2020-09-16 23:36:06 +03:00
xinputGetCustomData = nullptr ;
2019-09-20 16:28:55 +02:00
xinputGetState = nullptr ;
xinputSetState = nullptr ;
xinputGetBatteryInformation = nullptr ;
}
2014-05-24 21:05:00 +02:00
}
2021-08-10 21:45:26 +02:00
void xinput_pad_handler :: init_config ( cfg_pad * cfg )
2017-12-23 22:25:51 +01:00
{
2020-11-26 00:06:03 +01:00
if ( ! cfg ) return ;
2017-12-23 22:25:51 +01:00
// Set default button mapping
2022-09-19 15:57:51 +03:00
cfg -> ls_left . def = :: at32 ( button_list , XInputKeyCodes :: LSXNeg );
cfg -> ls_down . def = :: at32 ( button_list , XInputKeyCodes :: LSYNeg );
cfg -> ls_right . def = :: at32 ( button_list , XInputKeyCodes :: LSXPos );
cfg -> ls_up . def = :: at32 ( button_list , XInputKeyCodes :: LSYPos );
cfg -> rs_left . def = :: at32 ( button_list , XInputKeyCodes :: RSXNeg );
cfg -> rs_down . def = :: at32 ( button_list , XInputKeyCodes :: RSYNeg );
cfg -> rs_right . def = :: at32 ( button_list , XInputKeyCodes :: RSXPos );
cfg -> rs_up . def = :: at32 ( button_list , XInputKeyCodes :: RSYPos );
cfg -> start . def = :: at32 ( button_list , XInputKeyCodes :: Start );
cfg -> select . def = :: at32 ( button_list , XInputKeyCodes :: Back );
2026-03-18 09:09:18 +01:00
cfg -> ps . def = cfg_pad :: make_button_string ( button_list , {{ XInputKeyCodes :: Guide }, { XInputKeyCodes :: Start , XInputKeyCodes :: Back }});
2022-09-19 15:57:51 +03:00
cfg -> square . def = :: at32 ( button_list , XInputKeyCodes :: X );
cfg -> cross . def = :: at32 ( button_list , XInputKeyCodes :: A );
cfg -> circle . def = :: at32 ( button_list , XInputKeyCodes :: B );
cfg -> triangle . def = :: at32 ( button_list , XInputKeyCodes :: Y );
cfg -> left . def = :: at32 ( button_list , XInputKeyCodes :: Left );
cfg -> down . def = :: at32 ( button_list , XInputKeyCodes :: Down );
cfg -> right . def = :: at32 ( button_list , XInputKeyCodes :: Right );
cfg -> up . def = :: at32 ( button_list , XInputKeyCodes :: Up );
cfg -> r1 . def = :: at32 ( button_list , XInputKeyCodes :: RB );
cfg -> r2 . def = :: at32 ( button_list , XInputKeyCodes :: RT );
cfg -> r3 . def = :: at32 ( button_list , XInputKeyCodes :: RS );
cfg -> l1 . def = :: at32 ( button_list , XInputKeyCodes :: LB );
cfg -> l2 . def = :: at32 ( button_list , XInputKeyCodes :: LT );
cfg -> l3 . def = :: at32 ( button_list , XInputKeyCodes :: LS );
2017-12-23 22:25:51 +01:00
2022-09-19 15:57:51 +03:00
cfg -> pressure_intensity_button . def = :: at32 ( button_list , XInputKeyCodes :: None );
2024-08-09 23:44:45 +02:00
cfg -> analog_limiter_button . def = :: at32 ( button_list , XInputKeyCodes :: None );
2025-01-08 01:22:50 +01:00
cfg -> orientation_reset_button . def = :: at32 ( button_list , XInputKeyCodes :: None );
2021-08-08 16:45:45 +02:00
2017-12-23 22:25:51 +01:00
// Set default misc variables
2024-05-27 21:50:09 +02:00
cfg -> lstick_anti_deadzone . def = static_cast < u32 > ( 0.13 * thumb_max ); // 13%
cfg -> rstick_anti_deadzone . def = static_cast < u32 > ( 0.13 * thumb_max ); // 13%
2017-12-23 22:25:51 +01:00
cfg -> lstickdeadzone . def = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ; // between 0 and 32767
cfg -> rstickdeadzone . def = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE ; // between 0 and 32767
cfg -> ltriggerthreshold . def = XINPUT_GAMEPAD_TRIGGER_THRESHOLD ; // between 0 and 255
cfg -> rtriggerthreshold . def = XINPUT_GAMEPAD_TRIGGER_THRESHOLD ; // between 0 and 255
// apply defaults
cfg -> from_default ();
}
2022-10-21 22:35:31 +02:00
void xinput_pad_handler :: SetPadData ( const std :: string & padId , u8 /*player_id*/ , u8 large_motor , u8 small_motor , s32 /* r*/ , s32 /* g*/ , s32 /* b*/ , bool /*player_led*/ , bool /*battery_led*/ , u32 /*battery_led_brightness*/ )
2017-11-27 22:31:15 +01:00
{
2021-03-07 01:53:27 +01:00
const int device_number = GetDeviceNumber ( padId );
2017-11-30 00:43:52 +01:00
if ( device_number < 0 )
2017-11-27 22:31:15 +01:00
return ;
// The left motor is the low-frequency rumble motor. The right motor is the high-frequency rumble motor.
// The two motors are not the same, and they create different vibration effects.
2024-08-17 11:21:11 +02:00
XINPUT_VIBRATION vibrate
{
. wLeftMotorSpeed = static_cast < u16 > ( large_motor * 257 ), // between 0 to 65535
. wRightMotorSpeed = static_cast < u16 > ( small_motor * 257 ) // between 0 to 65535
};
2017-11-27 22:31:15 +01:00
2023-07-11 20:40:30 +02:00
xinputSetState ( static_cast < u32 > ( device_number ), & vibrate );
2017-11-27 22:31:15 +01:00
}
2021-03-07 01:53:27 +01:00
u32 xinput_pad_handler :: get_battery_level ( const std :: string & padId )
{
const int device_number = GetDeviceNumber ( padId );
if ( device_number < 0 )
return 0 ;
// Receive Battery Info. If device is not on cable, get battery level, else assume full.
XINPUT_BATTERY_INFORMATION battery_info ;
2023-07-11 20:40:30 +02:00
xinputGetBatteryInformation ( device_number , BATTERY_DEVTYPE_GAMEPAD , & battery_info );
2021-03-07 01:53:27 +01:00
switch ( battery_info . BatteryType )
{
case BATTERY_TYPE_DISCONNECTED :
return 0 ;
case BATTERY_TYPE_WIRED :
return 100 ;
default :
break ;
}
switch ( battery_info . BatteryLevel )
{
case BATTERY_LEVEL_EMPTY :
return 0 ;
case BATTERY_LEVEL_LOW :
return 33 ;
case BATTERY_LEVEL_MEDIUM :
return 66 ;
case BATTERY_LEVEL_FULL :
return 100 ;
default :
return 0 ;
}
}
2017-11-30 00:43:52 +01:00
int xinput_pad_handler :: GetDeviceNumber ( const std :: string & padId )
{
if ( ! Init ())
return - 1 ;
2021-03-07 01:53:27 +01:00
const usz pos = padId . find ( m_name_string );
2020-03-05 14:05:23 +03:00
if ( pos == umax )
2017-11-30 00:43:52 +01:00
return - 1 ;
2021-04-07 23:05:18 +02:00
const int device_number = std :: stoul ( padId . substr ( pos + 12 )) - 1 ; // Controllers 1-n in GUI
2017-11-30 00:43:52 +01:00
if ( device_number >= XUSER_MAX_COUNT )
return - 1 ;
return device_number ;
}
2026-03-15 11:17:23 +01:00
std :: unordered_map < u32 , u16 > xinput_pad_handler :: get_button_values ( const std :: shared_ptr < PadDevice >& device )
2017-11-27 22:31:15 +01:00
{
2026-03-15 11:17:23 +01:00
std :: unordered_map < u32 , u16 > values ;
2021-02-12 01:55:32 +01:00
XInputDevice * dev = static_cast < XInputDevice *> ( device . get ());
2019-09-20 16:28:55 +02:00
if ( ! dev || dev -> state != ERROR_SUCCESS ) // the state has to be aquired with update_connection before calling this function
return values ;
// Try SCP first, if it fails for that pad then try normal XInput
if ( dev -> is_scp_device )
{
2024-08-16 21:15:19 +02:00
return get_button_values_scp ( dev -> state_scp , m_trigger_recognition_mode );
2019-09-20 16:28:55 +02:00
}
2024-08-16 21:15:19 +02:00
return get_button_values_base ( dev -> state_base , m_trigger_recognition_mode );
2019-09-20 16:28:55 +02:00
}
2026-03-15 11:17:23 +01:00
std :: unordered_map < u32 , u16 > xinput_pad_handler :: get_button_values_base ( const XINPUT_STATE & state , trigger_recognition_mode trigger_mode )
2019-09-20 16:28:55 +02:00
{
2026-03-15 11:17:23 +01:00
std :: unordered_map < u32 , u16 > values ;
2017-11-27 22:31:15 +01:00
// Triggers
2024-08-16 21:15:19 +02:00
if ( trigger_mode == trigger_recognition_mode :: any || trigger_mode == trigger_recognition_mode :: one_directional )
2024-08-13 02:08:25 +02:00
{
values [ XInputKeyCodes :: LT ] = state . Gamepad . bLeftTrigger ;
values [ XInputKeyCodes :: RT ] = state . Gamepad . bRightTrigger ;
}
2024-08-16 21:15:19 +02:00
if ( trigger_mode == trigger_recognition_mode :: any || trigger_mode == trigger_recognition_mode :: two_directional )
{
const float lTrigger = state . Gamepad . bLeftTrigger / 255.0f ;
const float rTrigger = state . Gamepad . bRightTrigger / 255.0f ;
2024-08-13 02:08:25 +02:00
2024-08-16 21:15:19 +02:00
values [ XInputKeyCodes :: LT_Pos ] = static_cast < u16 > ( lTrigger > 0.5f ? std :: clamp (( lTrigger - 0.5f ) * 2.0f * 255.0f , 0.0f , 255.0f ) : 0.0f );
values [ XInputKeyCodes :: LT_Neg ] = static_cast < u16 > ( lTrigger < 0.5f ? std :: clamp (( 0.5f - lTrigger ) * 2.0f * 255.0f , 0.0f , 255.0f ) : 0.0f );
2024-08-13 02:08:25 +02:00
2024-08-16 21:15:19 +02:00
values [ XInputKeyCodes :: RT_Pos ] = static_cast < u16 > ( rTrigger > 0.5f ? std :: clamp (( rTrigger - 0.5f ) * 2.0f * 255.0f , 0.0f , 255.0f ) : 0.0f );
values [ XInputKeyCodes :: RT_Neg ] = static_cast < u16 > ( rTrigger < 0.5f ? std :: clamp (( 0.5f - rTrigger ) * 2.0f * 255.0f , 0.0f , 255.0f ) : 0.0f );
}
2017-11-27 22:31:15 +01:00
// Sticks
2021-03-07 01:53:27 +01:00
const int lx = state . Gamepad . sThumbLX ;
const int ly = state . Gamepad . sThumbLY ;
const int rx = state . Gamepad . sThumbRX ;
const int ry = state . Gamepad . sThumbRY ;
2017-11-27 22:31:15 +01:00
// Left Stick X Axis
values [ XInputKeyCodes :: LSXNeg ] = lx < 0 ? abs ( lx ) - 1 : 0 ;
values [ XInputKeyCodes :: LSXPos ] = lx > 0 ? lx : 0 ;
// Left Stick Y Axis
values [ XInputKeyCodes :: LSYNeg ] = ly < 0 ? abs ( ly ) - 1 : 0 ;
values [ XInputKeyCodes :: LSYPos ] = ly > 0 ? ly : 0 ;
// Right Stick X Axis
values [ XInputKeyCodes :: RSXNeg ] = rx < 0 ? abs ( rx ) - 1 : 0 ;
values [ XInputKeyCodes :: RSXPos ] = rx > 0 ? rx : 0 ;
// Right Stick Y Axis
values [ XInputKeyCodes :: RSYNeg ] = ry < 0 ? abs ( ry ) - 1 : 0 ;
values [ XInputKeyCodes :: RSYPos ] = ry > 0 ? ry : 0 ;
// Buttons
2021-04-07 23:05:18 +02:00
const WORD buttons = state . Gamepad . wButtons ;
2017-11-27 22:31:15 +01:00
// A, B, X, Y
values [ XInputKeyCodes :: A ] = buttons & XINPUT_GAMEPAD_A ? 255 : 0 ;
values [ XInputKeyCodes :: B ] = buttons & XINPUT_GAMEPAD_B ? 255 : 0 ;
values [ XInputKeyCodes :: X ] = buttons & XINPUT_GAMEPAD_X ? 255 : 0 ;
values [ XInputKeyCodes :: Y ] = buttons & XINPUT_GAMEPAD_Y ? 255 : 0 ;
// D-Pad
values [ XInputKeyCodes :: Left ] = buttons & XINPUT_GAMEPAD_DPAD_LEFT ? 255 : 0 ;
values [ XInputKeyCodes :: Right ] = buttons & XINPUT_GAMEPAD_DPAD_RIGHT ? 255 : 0 ;
values [ XInputKeyCodes :: Up ] = buttons & XINPUT_GAMEPAD_DPAD_UP ? 255 : 0 ;
values [ XInputKeyCodes :: Down ] = buttons & XINPUT_GAMEPAD_DPAD_DOWN ? 255 : 0 ;
// LB, RB, LS, RS
values [ XInputKeyCodes :: LB ] = buttons & XINPUT_GAMEPAD_LEFT_SHOULDER ? 255 : 0 ;
values [ XInputKeyCodes :: RB ] = buttons & XINPUT_GAMEPAD_RIGHT_SHOULDER ? 255 : 0 ;
values [ XInputKeyCodes :: LS ] = buttons & XINPUT_GAMEPAD_LEFT_THUMB ? 255 : 0 ;
values [ XInputKeyCodes :: RS ] = buttons & XINPUT_GAMEPAD_RIGHT_THUMB ? 255 : 0 ;
// Start, Back, Guide
values [ XInputKeyCodes :: Start ] = buttons & XINPUT_GAMEPAD_START ? 255 : 0 ;
values [ XInputKeyCodes :: Back ] = buttons & XINPUT_GAMEPAD_BACK ? 255 : 0 ;
values [ XInputKeyCodes :: Guide ] = buttons & XINPUT_INFO :: GUIDE_BUTTON ? 255 : 0 ;
return values ;
}
2026-03-15 11:17:23 +01:00
std :: unordered_map < u32 , u16 > xinput_pad_handler :: get_button_values_scp ( const SCP_EXTN & state , trigger_recognition_mode trigger_mode )
2019-09-22 11:22:42 +02:00
{
2026-03-15 11:17:23 +01:00
std :: unordered_map < u32 , u16 > values ;
2019-09-22 11:22:42 +02:00
// Triggers
2024-08-16 21:15:19 +02:00
if ( trigger_mode == trigger_recognition_mode :: any || trigger_mode == trigger_recognition_mode :: one_directional )
2024-08-13 02:08:25 +02:00
{
values [ xinput_pad_handler :: XInputKeyCodes :: LT ] = static_cast < u16 > ( state . SCP_L2 * 255.0f );
values [ xinput_pad_handler :: XInputKeyCodes :: RT ] = static_cast < u16 > ( state . SCP_R2 * 255.0f );
}
2024-08-16 21:15:19 +02:00
if ( trigger_mode == trigger_recognition_mode :: any || trigger_mode == trigger_recognition_mode :: two_directional )
{
values [ XInputKeyCodes :: LT_Pos ] = static_cast < u16 > ( state . SCP_L2 > 0.5f ? std :: clamp (( state . SCP_L2 - 0.5f ) * 2.0f * 255.0f , 0.0f , 255.0f ) : 0.0f );
values [ XInputKeyCodes :: LT_Neg ] = static_cast < u16 > ( state . SCP_L2 < 0.5f ? std :: clamp (( 0.5f - state . SCP_L2 ) * 2.0f * 255.0f , 0.0f , 255.0f ) : 0.0f );
2024-08-13 02:08:25 +02:00
2024-08-16 21:15:19 +02:00
values [ XInputKeyCodes :: RT_Pos ] = static_cast < u16 > ( state . SCP_R2 > 0.5f ? std :: clamp (( state . SCP_R2 - 0.5f ) * 2.0f * 255.0f , 0.0f , 255.0f ) : 0.0f );
values [ XInputKeyCodes :: RT_Neg ] = static_cast < u16 > ( state . SCP_R2 < 0.5f ? std :: clamp (( 0.5f - state . SCP_R2 ) * 2.0f * 255.0f , 0.0f , 255.0f ) : 0.0f );
}
2019-09-22 11:22:42 +02:00
// Sticks
2021-03-07 01:53:27 +01:00
const float lx = state . SCP_LX ;
const float ly = state . SCP_LY ;
const float rx = state . SCP_RX ;
const float ry = state . SCP_RY ;
2019-09-22 11:22:42 +02:00
// Left Stick X Axis
values [ xinput_pad_handler :: XInputKeyCodes :: LSXNeg ] = lx < 0.0f ? static_cast < u16 > ( lx * - 32768.0f ) : 0 ;
values [ xinput_pad_handler :: XInputKeyCodes :: LSXPos ] = lx > 0.0f ? static_cast < u16 > ( lx * 32767.0f ) : 0 ;
// Left Stick Y Axis
values [ xinput_pad_handler :: XInputKeyCodes :: LSYNeg ] = ly < 0.0f ? static_cast < u16 > ( ly * - 32768.0f ) : 0 ;
values [ xinput_pad_handler :: XInputKeyCodes :: LSYPos ] = ly > 0.0f ? static_cast < u16 > ( ly * 32767.0f ) : 0 ;
// Right Stick X Axis
values [ xinput_pad_handler :: XInputKeyCodes :: RSXNeg ] = rx < 0.0f ? static_cast < u16 > ( rx * - 32768.0f ) : 0 ;
values [ xinput_pad_handler :: XInputKeyCodes :: RSXPos ] = rx > 0.0f ? static_cast < u16 > ( rx * 32767.0f ) : 0 ;
// Right Stick Y Axis
values [ xinput_pad_handler :: XInputKeyCodes :: RSYNeg ] = ry < 0.0f ? static_cast < u16 > ( ry * - 32768.0f ) : 0 ;
values [ xinput_pad_handler :: XInputKeyCodes :: RSYPos ] = ry > 0.0f ? static_cast < u16 > ( ry * 32767.0f ) : 0 ;
// A, B, X, Y
values [ xinput_pad_handler :: XInputKeyCodes :: A ] = static_cast < u16 > ( state . SCP_X * 255.0f );
values [ xinput_pad_handler :: XInputKeyCodes :: B ] = static_cast < u16 > ( state . SCP_C * 255.0f );
values [ xinput_pad_handler :: XInputKeyCodes :: X ] = static_cast < u16 > ( state . SCP_S * 255.0f );
values [ xinput_pad_handler :: XInputKeyCodes :: Y ] = static_cast < u16 > ( state . SCP_T * 255.0f );
// D-Pad
values [ xinput_pad_handler :: XInputKeyCodes :: Left ] = static_cast < u16 > ( state . SCP_LEFT * 255.0f );
values [ xinput_pad_handler :: XInputKeyCodes :: Right ] = static_cast < u16 > ( state . SCP_RIGHT * 255.0f );
values [ xinput_pad_handler :: XInputKeyCodes :: Up ] = static_cast < u16 > ( state . SCP_UP * 255.0f );
values [ xinput_pad_handler :: XInputKeyCodes :: Down ] = static_cast < u16 > ( state . SCP_DOWN * 255.0f );
// LB, RB, LS, RS
values [ xinput_pad_handler :: XInputKeyCodes :: LB ] = static_cast < u16 > ( state . SCP_L1 * 255.0f );
values [ xinput_pad_handler :: XInputKeyCodes :: RB ] = static_cast < u16 > ( state . SCP_R1 * 255.0f );
values [ xinput_pad_handler :: XInputKeyCodes :: LS ] = static_cast < u16 > ( state . SCP_L3 * 255.0f );
values [ xinput_pad_handler :: XInputKeyCodes :: RS ] = static_cast < u16 > ( state . SCP_R3 * 255.0f );
// Start, Back, Guide
values [ xinput_pad_handler :: XInputKeyCodes :: Start ] = static_cast < u16 > ( state . SCP_START * 255.0f );
values [ xinput_pad_handler :: XInputKeyCodes :: Back ] = static_cast < u16 > ( state . SCP_SELECT * 255.0f );
values [ xinput_pad_handler :: XInputKeyCodes :: Guide ] = static_cast < u16 > ( state . SCP_PS * 255.0f );
return values ;
}
2026-03-17 08:35:53 +01:00
pad_preview_values xinput_pad_handler :: get_preview_values ( const std :: unordered_map < u32 , u16 >& data , const std :: vector < std :: string >& /*buttons*/ )
2019-09-22 11:22:42 +02:00
{
2020-12-14 14:33:43 +01:00
return {
2022-09-19 15:57:51 +03:00
:: at32 ( data , LT ),
:: at32 ( data , RT ),
:: at32 ( data , LSXPos ) - :: at32 ( data , LSXNeg ),
:: at32 ( data , LSYPos ) - :: at32 ( data , LSYNeg ),
:: at32 ( data , RSXPos ) - :: at32 ( data , RSXNeg ),
:: at32 ( data , RSYPos ) - :: at32 ( data , RSYNeg )
2020-12-14 14:33:43 +01:00
};
2019-09-22 11:22:42 +02:00
}
2017-08-15 14:03:07 +02:00
bool xinput_pad_handler :: Init ()
2014-05-24 21:05:00 +02:00
{
2022-10-21 22:47:25 +02:00
if ( m_is_init )
2017-12-10 10:41:55 +01:00
return true ;
2017-08-15 14:03:07 +02:00
2017-11-27 22:31:15 +01:00
for ( auto it : XINPUT_INFO :: LIBRARY_FILENAMES )
2014-05-24 21:05:00 +02:00
{
2025-02-23 14:39:39 +01:00
library . load ( it );
2014-05-24 21:05:00 +02:00
if ( library )
{
2025-02-23 14:39:39 +01:00
xinputGetExtended = library . get < PFN_XINPUTGETEXTENDED > ( "XInputGetExtended" ); // Optional
xinputGetCustomData = library . get < PFN_XINPUTGETCUSTOMDATA > ( "XInputGetCustomData" ); // Optional
xinputGetState = library . get < PFN_XINPUTGETSTATE > ( reinterpret_cast < LPCSTR > ( 100 ));
2014-05-24 21:05:00 +02:00
if ( ! xinputGetState )
2025-02-23 14:39:39 +01:00
xinputGetState = library . get < PFN_XINPUTGETSTATE > ( "XInputGetState" );
2017-07-11 00:59:40 +02:00
2025-02-23 14:39:39 +01:00
xinputSetState = library . get < PFN_XINPUTSETSTATE > ( "XInputSetState" );
xinputGetBatteryInformation = library . get < PFN_XINPUTGETBATTERYINFORMATION > ( "XInputGetBatteryInformation" );
2014-05-24 21:05:00 +02:00
2019-09-22 11:22:42 +02:00
if ( xinputGetState && xinputSetState && xinputGetBatteryInformation )
2014-05-24 21:05:00 +02:00
{
2022-10-21 22:47:25 +02:00
m_is_init = true ;
2014-05-24 21:05:00 +02:00
break ;
}
2019-09-22 11:22:42 +02:00
xinputGetExtended = nullptr ;
2020-09-16 23:36:06 +03:00
xinputGetCustomData = nullptr ;
2014-05-24 21:05:00 +02:00
xinputGetState = nullptr ;
2019-09-22 11:22:42 +02:00
xinputSetState = nullptr ;
2017-11-27 22:31:15 +01:00
xinputGetBatteryInformation = nullptr ;
2014-05-24 21:05:00 +02:00
}
}
2025-01-08 01:22:50 +01:00
b_has_orientation = !! xinputGetCustomData ;
2022-10-21 22:47:25 +02:00
if ( ! m_is_init )
2017-12-10 10:41:55 +01:00
return false ;
2014-05-24 21:05:00 +02:00
2017-08-15 14:03:07 +02:00
return true ;
2017-04-28 20:02:58 -05:00
}
2022-08-13 09:56:04 +02:00
std :: vector < pad_list_entry > xinput_pad_handler :: list_devices ()
2014-05-24 21:05:00 +02:00
{
2022-08-13 09:56:04 +02:00
std :: vector < pad_list_entry > xinput_pads_list ;
2017-08-15 14:03:07 +02:00
2017-12-10 10:41:55 +01:00
if ( ! Init ())
return xinput_pads_list ;
2017-08-15 14:03:07 +02:00
2017-11-27 22:31:15 +01:00
for ( DWORD i = 0 ; i < XUSER_MAX_COUNT ; i ++ )
2017-08-15 14:03:07 +02:00
{
2019-09-20 16:28:55 +02:00
DWORD result = ERROR_NOT_CONNECTED ;
// Try SCP first, if it fails for that pad then try normal XInput
if ( xinputGetExtended )
{
SCP_EXTN state ;
result = xinputGetExtended ( i , & state );
}
if ( result != ERROR_SUCCESS )
{
XINPUT_STATE state ;
result = xinputGetState ( i , & state );
}
2017-08-15 14:03:07 +02:00
if ( result == ERROR_SUCCESS )
2022-08-13 09:56:04 +02:00
xinput_pads_list . emplace_back ( m_name_string + std :: to_string ( i + 1 ), false ); // Controllers 1-n in GUI
2017-08-15 14:03:07 +02:00
}
return xinput_pads_list ;
}
2019-09-20 16:28:55 +02:00
std :: shared_ptr < PadDevice > xinput_pad_handler :: get_device ( const std :: string & device )
2017-08-15 14:03:07 +02:00
{
2019-09-20 16:28:55 +02:00
// Convert device string to u32 representing xinput device number
2021-03-07 01:53:27 +01:00
const int device_number = GetDeviceNumber ( device );
2017-11-30 00:43:52 +01:00
if ( device_number < 0 )
2019-09-20 16:28:55 +02:00
return nullptr ;
2017-08-15 14:03:07 +02:00
2024-08-17 11:21:11 +02:00
std :: shared_ptr < XInputDevice > dev = std :: make_shared < XInputDevice > ();
dev -> deviceNumber = static_cast < u32 > ( device_number );
2017-11-27 22:31:15 +01:00
2024-08-17 11:21:11 +02:00
return dev ;
2019-09-20 16:28:55 +02:00
}
2026-03-15 11:17:23 +01:00
bool xinput_pad_handler :: get_is_left_trigger ( const std :: shared_ptr < PadDevice >& /*device*/ , u32 keyCode )
2019-09-20 16:28:55 +02:00
{
return keyCode == XInputKeyCodes :: LT ;
}
2026-03-15 11:17:23 +01:00
bool xinput_pad_handler :: get_is_right_trigger ( const std :: shared_ptr < PadDevice >& /*device*/ , u32 keyCode )
2019-09-20 16:28:55 +02:00
{
return keyCode == XInputKeyCodes :: RT ;
}
2026-03-15 11:17:23 +01:00
bool xinput_pad_handler :: get_is_left_stick ( const std :: shared_ptr < PadDevice >& /*device*/ , u32 keyCode )
2019-09-20 16:28:55 +02:00
{
switch ( keyCode )
{
case XInputKeyCodes :: LSXNeg :
case XInputKeyCodes :: LSXPos :
case XInputKeyCodes :: LSYPos :
case XInputKeyCodes :: LSYNeg :
return true ;
default :
2017-12-23 22:25:51 +01:00
return false ;
2019-09-20 16:28:55 +02:00
}
}
2017-11-27 22:31:15 +01:00
2026-03-15 11:17:23 +01:00
bool xinput_pad_handler :: get_is_right_stick ( const std :: shared_ptr < PadDevice >& /*device*/ , u32 keyCode )
2019-09-20 16:28:55 +02:00
{
switch ( keyCode )
{
case XInputKeyCodes :: RSXNeg :
case XInputKeyCodes :: RSXPos :
case XInputKeyCodes :: RSYPos :
case XInputKeyCodes :: RSYNeg :
return true ;
default :
return false ;
}
}
2017-08-15 14:03:07 +02:00
2019-09-20 16:28:55 +02:00
PadHandlerBase :: connection xinput_pad_handler :: update_connection ( const std :: shared_ptr < PadDevice >& device )
{
2021-02-12 01:55:32 +01:00
XInputDevice * dev = static_cast < XInputDevice *> ( device . get ());
2019-09-20 16:28:55 +02:00
if ( ! dev )
return connection :: disconnected ;
2017-08-15 14:03:07 +02:00
2019-09-20 16:28:55 +02:00
dev -> state = ERROR_NOT_CONNECTED ;
dev -> state_scp = {};
dev -> state_base = {};
2017-11-27 22:31:15 +01:00
2019-09-20 16:28:55 +02:00
// Try SCP first, if it fails for that pad then try normal XInput
if ( xinputGetExtended )
dev -> state = xinputGetExtended ( dev -> deviceNumber , & dev -> state_scp );
2017-08-15 14:03:07 +02:00
2019-09-20 16:28:55 +02:00
dev -> is_scp_device = dev -> state == ERROR_SUCCESS ;
2017-08-15 14:03:07 +02:00
2019-09-20 16:28:55 +02:00
if ( ! dev -> is_scp_device )
dev -> state = xinputGetState ( dev -> deviceNumber , & dev -> state_base );
2017-09-12 23:42:00 +02:00
2019-09-20 16:28:55 +02:00
if ( dev -> state == ERROR_SUCCESS )
return connection :: connected ;
return connection :: disconnected ;
}
2022-08-13 09:56:04 +02:00
void xinput_pad_handler :: get_extended_info ( const pad_ensemble & binding )
2019-09-20 16:28:55 +02:00
{
2022-08-13 09:56:04 +02:00
const auto & device = binding . device ;
const auto & pad = binding . pad ;
2021-02-12 01:55:32 +01:00
XInputDevice * dev = static_cast < XInputDevice *> ( device . get ());
2019-09-20 16:28:55 +02:00
if ( ! dev || ! pad )
return ;
2021-03-07 01:53:27 +01:00
const auto padnum = dev -> deviceNumber ;
2019-09-20 16:28:55 +02:00
// Receive Battery Info. If device is not on cable, get battery level, else assume full
XINPUT_BATTERY_INFORMATION battery_info ;
2023-07-11 20:40:30 +02:00
xinputGetBatteryInformation ( padnum , BATTERY_DEVTYPE_GAMEPAD , & battery_info );
2019-09-20 16:28:55 +02:00
pad -> m_cable_state = battery_info . BatteryType == BATTERY_TYPE_WIRED ? 1 : 0 ;
pad -> m_battery_level = pad -> m_cable_state ? BATTERY_LEVEL_FULL : battery_info . BatteryLevel ;
2020-09-16 23:36:06 +03:00
if ( xinputGetCustomData )
{
SCP_DS3_ACCEL sensors ;
if ( xinputGetCustomData ( dev -> deviceNumber , 0 , & sensors ) == ERROR_SUCCESS )
{
pad -> m_sensors [ 0 ]. m_value = sensors . SCP_ACCEL_X ;
pad -> m_sensors [ 1 ]. m_value = sensors . SCP_ACCEL_Y ;
pad -> m_sensors [ 2 ]. m_value = sensors . SCP_ACCEL_Z ;
pad -> m_sensors [ 3 ]. m_value = sensors . SCP_GYRO ;
2025-01-08 01:22:50 +01:00
set_raw_orientation ( * pad );
2020-09-16 23:36:06 +03:00
}
}
2019-09-20 16:28:55 +02:00
}
2022-08-13 09:56:04 +02:00
void xinput_pad_handler :: apply_pad_data ( const pad_ensemble & binding )
2019-09-20 16:28:55 +02:00
{
2022-08-13 09:56:04 +02:00
const auto & device = binding . device ;
const auto & pad = binding . pad ;
2021-02-12 01:55:32 +01:00
XInputDevice * dev = static_cast < XInputDevice *> ( device . get ());
2019-09-20 16:28:55 +02:00
if ( ! dev || ! pad )
return ;
2021-04-07 23:05:18 +02:00
const auto padnum = dev -> deviceNumber ;
2021-08-29 08:39:16 +02:00
const auto cfg = dev -> config ;
2019-09-20 16:28:55 +02:00
// The left motor is the low-frequency rumble motor. The right motor is the high-frequency rumble motor.
// The two motors are not the same, and they create different vibration effects. Values range between 0 to 65535.
2025-09-10 17:49:09 +02:00
const u8 speed_large = cfg -> get_large_motor_speed ( pad -> m_vibrate_motors );
const u8 speed_small = cfg -> get_small_motor_speed ( pad -> m_vibrate_motors );
2019-09-20 16:28:55 +02:00
2024-08-17 11:21:11 +02:00
dev -> new_output_data |= dev -> large_motor != speed_large || dev -> small_motor != speed_small ;
2019-09-20 16:28:55 +02:00
2022-10-21 22:35:31 +02:00
dev -> large_motor = speed_large ;
dev -> small_motor = speed_small ;
2019-09-20 16:28:55 +02:00
2024-08-17 11:21:11 +02:00
const auto now = steady_clock :: now ();
const auto elapsed = now - dev -> last_output ;
2019-09-20 16:28:55 +02:00
// XBox One Controller can't handle faster vibration updates than ~10ms. Elite is even worse. So I'll use 20ms to be on the safe side. No lag was noticable.
2024-08-17 11:21:11 +02:00
if (( dev -> new_output_data && elapsed > 20 ms ) || elapsed > min_output_interval )
2019-09-20 16:28:55 +02:00
{
2024-08-17 11:21:11 +02:00
XINPUT_VIBRATION vibrate
{
. wLeftMotorSpeed = static_cast < u16 > ( speed_large * 257 ), // between 0 to 65535
. wRightMotorSpeed = static_cast < u16 > ( speed_small * 257 ) // between 0 to 65535
};
2019-09-20 16:28:55 +02:00
2023-07-11 20:40:30 +02:00
if ( xinputSetState ( padnum , & vibrate ) == ERROR_SUCCESS )
2019-09-20 16:28:55 +02:00
{
2024-08-17 11:21:11 +02:00
dev -> new_output_data = false ;
dev -> last_output = now ;
2019-09-20 16:28:55 +02:00
}
}
2014-05-24 21:05:00 +02:00
}
2016-02-02 00:51:09 +03:00
#endif