2020-12-05 13:08:24 +01:00
# include "stdafx.h"
2017-12-23 22:25:51 +01:00
# include "PadHandler.h"
2023-05-18 17:17:30 +02:00
# include "Emu/system_config.h"
2025-01-08 01:22:50 +01:00
# include "Emu/Cell/timers.hpp"
2019-12-22 17:39:42 +01:00
# include "Input/pad_thread.h"
2020-04-25 02:12:19 +02:00
# include "Input/product_info.h"
2017-12-23 22:25:51 +01:00
cfg_input g_cfg_input ;
2024-06-11 22:30:16 +02:00
PadHandlerBase : : PadHandlerBase ( pad_handler type ) : m_type ( type )
2017-12-23 22:25:51 +01:00
{
}
2023-06-13 01:19:32 +02:00
std : : set < u32 > PadHandlerBase : : narrow_set ( const std : : set < u64 > & src )
2017-12-23 22:25:51 +01:00
{
2023-06-13 01:19:32 +02:00
if ( src . empty ( ) )
return { } ;
2017-12-23 22:25:51 +01:00
2023-06-13 01:19:32 +02:00
std : : set < u32 > dst ;
for ( const u64 & s : src )
2017-12-23 22:25:51 +01:00
{
2023-06-13 01:19:32 +02:00
dst . insert ( : : narrow < u32 > ( s ) ) ;
2017-12-23 22:25:51 +01:00
}
2023-06-13 01:19:32 +02:00
return dst ;
2017-12-23 22:25:51 +01:00
}
2021-12-16 23:19:09 +01:00
// Get new multiplied value based on the multiplier
s32 PadHandlerBase : : MultipliedInput ( s32 raw_value , s32 multiplier )
{
return ( multiplier * raw_value ) / 100 ;
}
2023-12-11 22:03:40 +01:00
// Get new scaled value between 0 and range based on its minimum and maximum
f32 PadHandlerBase : : ScaledInput ( f32 raw_value , f32 minimum , f32 maximum , f32 deadzone , f32 range )
2017-12-23 22:25:51 +01:00
{
2023-12-11 22:03:40 +01:00
if ( deadzone > 0 & & deadzone > minimum )
{
// adjust minimum so we smoothly start at 0 when we surpass the deadzone value
minimum = deadzone ;
}
// convert [min, max] to [0, 1]
const f32 val = static_cast < f32 > ( std : : clamp ( raw_value , minimum , maximum ) - minimum ) / ( maximum - minimum ) ;
// convert [0, 1] to [0, range]
2022-08-13 09:56:04 +02:00
return range * val ;
2017-12-23 22:25:51 +01:00
}
2023-12-11 22:03:40 +01:00
// Get new scaled value between -range and range based on its minimum and maximum
f32 PadHandlerBase : : ScaledAxisInput ( f32 raw_value , f32 minimum , f32 maximum , f32 deadzone , f32 range )
2017-12-23 22:25:51 +01:00
{
2023-12-11 22:03:40 +01:00
// convert [min, max] to [0, 1]
f32 val = static_cast < f32 > ( std : : clamp ( raw_value , minimum , maximum ) - minimum ) / ( maximum - minimum ) ;
if ( deadzone > 0 )
{
// convert [0, 1] to [-0.5, 0.5]
val - = 0.5f ;
// Convert deadzone to [0, 0.5]
deadzone = std : : max ( 0.0f , std : : min ( 1.0f , deadzone / maximum ) ) / 2.0f ;
if ( val > = 0.0f )
{
// Apply deadzone. The result will be [0, 0.5]
val = ScaledInput ( val , 0.0f , 0.5f , deadzone , 0.5f ) ;
}
else
{
// Apply deadzone. The result will be [-0.5, 0]
val = ScaledInput ( std : : abs ( val ) , 0 , 0.5f , deadzone , 0.5f ) * - 1.0f ;
}
// convert [-0.5, 0.5] back to [0, 1]
val + = 0.5f ;
}
// convert [0, 1] to [-range, range]
2022-08-13 09:56:04 +02:00
return ( 2.0f * range * val ) - range ;
2017-12-23 22:25:51 +01:00
}
// Get normalized trigger value based on the range defined by a threshold
2024-05-27 21:50:09 +02:00
u16 PadHandlerBase : : NormalizeTriggerInput ( u16 value , u32 threshold ) const
2017-12-23 22:25:51 +01:00
{
if ( value < = threshold | | threshold > = trigger_max )
{
return static_cast < u16 > ( 0 ) ;
}
2023-08-26 09:56:50 +02:00
2023-12-29 18:33:29 +01:00
return static_cast < u16 > ( ScaledInput ( static_cast < f32 > ( value ) , static_cast < f32 > ( trigger_min ) , static_cast < f32 > ( trigger_max ) , static_cast < f32 > ( threshold ) ) ) ;
2017-12-23 22:25:51 +01:00
}
// normalizes a directed input, meaning it will correspond to a single "button" and not an axis with two directions
// the input values must lie in 0+
2020-07-03 21:16:01 +02:00
u16 PadHandlerBase : : NormalizeDirectedInput ( s32 raw_value , s32 threshold , s32 maximum ) const
2017-12-23 22:25:51 +01:00
{
2023-12-11 22:03:40 +01:00
if ( threshold > = maximum | | maximum < = 0 | | raw_value < 0 )
2017-12-23 22:25:51 +01:00
{
return static_cast < u16 > ( 0 ) ;
}
2023-12-29 18:33:29 +01:00
return static_cast < u16 > ( ScaledInput ( static_cast < f32 > ( raw_value ) , 0.0f , static_cast < f32 > ( maximum ) , static_cast < f32 > ( threshold ) ) ) ;
2017-12-23 22:25:51 +01:00
}
2023-12-29 18:33:29 +01:00
u16 PadHandlerBase : : NormalizeStickInput ( u16 raw_value , s32 threshold , s32 multiplier , bool ignore_threshold ) const
2017-12-23 22:25:51 +01:00
{
2021-12-16 23:19:09 +01:00
const s32 scaled_value = MultipliedInput ( raw_value , multiplier ) ;
2019-05-16 21:14:33 +02:00
2017-12-23 22:25:51 +01:00
if ( ignore_threshold )
{
2023-12-11 22:03:40 +01:00
threshold = 0 ;
2017-12-23 22:25:51 +01:00
}
2023-08-26 09:56:50 +02:00
2023-12-29 18:33:29 +01:00
return static_cast < u16 > ( ScaledInput ( static_cast < f32 > ( scaled_value ) , 0.0f , static_cast < f32 > ( thumb_max ) , static_cast < f32 > ( threshold ) ) ) ;
2017-12-23 22:25:51 +01:00
}
2024-05-27 21:50:09 +02:00
// This function normalizes stick deadzone based on the DS3's deadzone, which is ~13% (default of anti deadzone)
2017-12-23 22:25:51 +01:00
// X and Y is expected to be in (-255) to 255 range, deadzone should be in terms of thumb stick range
// return is new x and y values in 0-255 range
2024-05-27 21:50:09 +02:00
std : : tuple < u16 , u16 > PadHandlerBase : : NormalizeStickDeadzone ( s32 inX , s32 inY , u32 deadzone , u32 anti_deadzone ) const
2017-12-23 22:25:51 +01:00
{
2022-08-13 09:56:04 +02:00
f32 X = inX / 255.0f ;
f32 Y = inY / 255.0f ;
2017-12-23 22:25:51 +01:00
2024-05-27 21:50:09 +02:00
const f32 mag = std : : min ( sqrtf ( X * X + Y * Y ) , 1.f ) ;
if ( mag > 0.f )
2017-12-23 22:25:51 +01:00
{
2024-05-27 21:50:09 +02:00
const f32 dz_max = static_cast < f32 > ( thumb_max ) ;
const f32 dz = deadzone / dz_max ;
const f32 anti_dz = anti_deadzone / dz_max ;
2017-12-23 22:25:51 +01:00
2024-05-27 21:50:09 +02:00
f32 pos ;
2017-12-23 22:25:51 +01:00
2024-05-27 21:50:09 +02:00
if ( dz < = 0.f | | mag > dz )
2020-02-23 11:32:32 +01:00
{
2024-05-27 21:50:09 +02:00
const f32 range = 1.f - dz ;
pos = std : : lerp ( anti_dz , 1.f , ( mag - dz ) / range ) ;
2017-12-23 22:25:51 +01:00
}
2020-02-23 11:32:32 +01:00
else
{
2024-05-27 21:50:09 +02:00
pos = std : : lerp ( 0.f , anti_dz , mag / dz ) ;
2017-12-23 22:25:51 +01:00
}
2024-05-27 21:50:09 +02:00
const f32 scale = pos / mag ;
X * = scale ;
Y * = scale ;
2017-12-23 22:25:51 +01:00
}
2024-05-27 21:50:09 +02:00
2017-12-23 22:25:51 +01:00
return std : : tuple < u16 , u16 > ( ConvertAxis ( X ) , ConvertAxis ( Y ) ) ;
}
// get clamped value between 0 and 255
u16 PadHandlerBase : : Clamp0To255 ( f32 input )
{
2018-09-05 22:52:30 +02:00
return static_cast < u16 > ( std : : clamp ( input , 0.0f , 255.0f ) ) ;
2017-12-23 22:25:51 +01:00
}
// get clamped value between 0 and 1023
u16 PadHandlerBase : : Clamp0To1023 ( f32 input )
{
2018-09-05 22:52:30 +02:00
return static_cast < u16 > ( std : : clamp ( input , 0.0f , 1023.0f ) ) ;
2017-12-23 22:25:51 +01:00
}
// input has to be [-1,1]. result will be [0,255]
2022-08-13 09:56:04 +02:00
u16 PadHandlerBase : : ConvertAxis ( f32 value )
2017-12-23 22:25:51 +01:00
{
2022-10-15 12:10:40 +02:00
return static_cast < u16 > ( ( value + 1.0 ) * ( 255.0 / 2.0 ) ) ;
2017-12-23 22:25:51 +01:00
}
// The DS3, (and i think xbox controllers) give a 'square-ish' type response, so that the corners will give (almost)max x/y instead of the ~30x30 from a perfect circle
// using a simple scale/sensitivity increase would *work* although it eats a chunk of our usable range in exchange
// this might be the best for now, in practice it seems to push the corners to max of 20x20, with a squircle_factor of 8000
// This function assumes inX and inY is already in 0-255
2024-09-08 20:03:14 +02:00
void PadHandlerBase : : ConvertToSquirclePoint ( u16 & inX , u16 & inY , u32 squircle_factor )
2017-12-23 22:25:51 +01:00
{
2024-09-08 20:03:14 +02:00
if ( ! squircle_factor )
return ;
constexpr f32 radius = 127.5f ;
2017-12-23 22:25:51 +01:00
// convert inX and Y to a (-1, 1) vector;
2024-09-08 20:03:14 +02:00
const f32 x = ( inX - radius ) / radius ;
const f32 y = ( inY - radius ) / radius ;
2017-12-23 22:25:51 +01:00
2024-09-08 20:03:14 +02:00
// compute angle and len of given point to be used for squircle radius. Clamp to circle, we don't want to exceed the squircle.
2017-12-23 22:25:51 +01:00
const f32 angle = std : : atan2 ( y , x ) ;
2024-09-08 20:03:14 +02:00
const f32 distance_to_center = std : : min ( 1.0f , std : : sqrt ( std : : pow ( x , 2.f ) + std : : pow ( y , 2.f ) ) ) ;
2017-12-23 22:25:51 +01:00
// now find len/point on the given squircle from our current angle and radius in polar coords
// https://thatsmaths.com/2016/07/14/squircles/
2024-09-08 20:03:14 +02:00
const f32 new_len = ( 1 + std : : pow ( std : : sin ( 2 * angle ) , 2.f ) / ( squircle_factor / 1000.f ) ) * distance_to_center ;
2017-12-23 22:25:51 +01:00
2018-04-29 08:41:51 +02:00
// we now have len and angle, convert to cartesian
2024-09-08 20:03:14 +02:00
inX = Clamp0To255 ( std : : round ( ( ( new_len * std : : cos ( angle ) ) + 1 ) * radius ) ) ;
inY = Clamp0To255 ( std : : round ( ( ( new_len * std : : sin ( angle ) ) + 1 ) * radius ) ) ;
2017-12-23 22:25:51 +01:00
}
void PadHandlerBase : : init_configs ( )
{
2020-02-19 18:03:59 +01:00
for ( u32 i = 0 ; i < MAX_GAMEPADS ; i + + )
2017-12-23 22:25:51 +01:00
{
2021-08-10 21:45:26 +02:00
init_config ( & m_pad_configs [ i ] ) ;
2017-12-23 22:25:51 +01:00
}
}
2019-09-20 16:28:55 +02:00
2023-01-17 01:28:54 +01:00
cfg_pad * PadHandlerBase : : get_config ( const std : : string & pad_id )
{
int index = 0 ;
for ( uint i = 0 ; i < MAX_GAMEPADS ; i + + )
{
if ( g_cfg_input . player [ i ] - > handler = = m_type )
{
if ( g_cfg_input . player [ i ] - > device . to_string ( ) = = pad_id )
{
m_pad_configs [ index ] . from_string ( g_cfg_input . player [ i ] - > config . to_string ( ) ) ;
return & m_pad_configs [ index ] ;
}
index + + ;
}
}
return nullptr ;
}
2024-08-09 20:39:56 +02:00
PadHandlerBase : : connection PadHandlerBase : : get_next_button_press ( const std : : string & pad_id , const pad_callback & callback , const pad_fail_callback & fail_callback , gui_call_type call_type , const std : : vector < std : : string > & /*buttons*/ )
2019-09-20 16:28:55 +02:00
{
2024-08-09 20:39:56 +02:00
if ( call_type = = gui_call_type : : blacklist )
2019-09-20 16:28:55 +02:00
blacklist . clear ( ) ;
2024-08-09 20:39:56 +02:00
if ( call_type = = gui_call_type : : reset_input | | call_type = = gui_call_type : : blacklist )
2024-08-08 03:04:21 +02:00
min_button_values . clear ( ) ;
2019-09-20 16:28:55 +02:00
auto device = get_device ( pad_id ) ;
2023-06-13 01:19:32 +02:00
const connection status = update_connection ( device ) ;
2019-09-20 16:28:55 +02:00
if ( status = = connection : : disconnected )
2020-07-04 13:18:45 +02:00
{
if ( fail_callback )
fail_callback ( pad_id ) ;
2022-10-21 22:55:31 +02:00
return status ;
2020-07-04 13:18:45 +02:00
}
2022-06-05 15:58:05 +02:00
2024-08-09 20:39:56 +02:00
if ( status = = connection : : no_data | | call_type = = gui_call_type : : get_connection )
2022-06-05 15:58:05 +02:00
{
2022-10-21 22:55:31 +02:00
return status ;
2022-06-05 15:58:05 +02:00
}
2019-09-20 16:28:55 +02:00
2024-07-08 20:17:21 +02:00
if ( m_type = = pad_handler : : move )
{
// Keep the pad cached to reduce expensive one time requests
if ( ! m_pad_for_pad_settings | | m_pad_for_pad_settings - > m_pad_handler ! = m_type )
{
m_pad_for_pad_settings = std : : make_shared < Pad > ( m_type , 0 , 0 , 0 , 0 ) ;
}
// Get extended device ID
pad_ensemble binding { m_pad_for_pad_settings , device , nullptr } ;
get_extended_info ( binding ) ;
}
2019-09-20 16:28:55 +02:00
// Get the current button values
auto data = get_button_values ( device ) ;
// Check for each button in our list if its corresponding (maybe remapped) button or axis was pressed.
// Return the new value if the button was pressed (aka. its value was bigger than 0 or the defined threshold)
2022-08-13 09:56:04 +02:00
// Get all the legally pressed buttons and use the one with highest value (prioritize first)
struct
{
u16 value = 0 ;
std : : string name ;
} pressed_button { } ;
2022-06-05 15:58:05 +02:00
for ( const auto & [ keycode , name ] : button_list )
2019-09-20 16:28:55 +02:00
{
2024-08-09 20:39:56 +02:00
if ( call_type ! = gui_call_type : : blacklist & & blacklist . contains ( keycode ) )
2019-09-20 16:28:55 +02:00
continue ;
2024-08-08 03:04:21 +02:00
const u16 value = data [ keycode ] ;
u16 & min_value = min_button_values [ keycode ] ;
2024-08-09 20:39:56 +02:00
if ( call_type = = gui_call_type : : reset_input | | value < min_value )
2024-08-08 03:04:21 +02:00
{
min_value = value ;
continue ;
}
2022-10-15 12:41:21 +02:00
const bool is_trigger = get_is_left_trigger ( device , keycode ) | | get_is_right_trigger ( device , keycode ) ;
2025-04-05 21:50:45 +02:00
const bool is_stick = ! is_trigger & & ( get_is_left_stick ( device , keycode ) | | get_is_right_stick ( device , keycode ) ) ;
2024-07-30 22:51:17 +02:00
const bool is_touch_motion = ! is_trigger & & ! is_stick & & get_is_touch_pad_motion ( device , keycode ) ;
const bool is_button = ! is_trigger & & ! is_stick & & ! is_touch_motion ;
2019-09-20 16:28:55 +02:00
2024-07-30 22:51:17 +02:00
if ( ( is_trigger & & ( value > m_trigger_threshold ) ) | |
( is_stick & & ( value > m_thumb_threshold ) ) | |
2024-08-08 03:04:21 +02:00
( is_button & & ( value > button_press_threshold ) ) | |
( is_touch_motion & & ( value > touch_threshold ) ) )
2019-09-20 16:28:55 +02:00
{
2024-08-09 20:39:56 +02:00
if ( call_type = = gui_call_type : : blacklist )
2019-09-20 16:28:55 +02:00
{
2023-06-14 20:57:30 +02:00
blacklist . insert ( keycode ) ;
2022-06-05 15:58:05 +02:00
input_log . error ( " %s Calibration: Added key [ %d = %s ] to blacklist. Value = %d " , m_type , keycode , name , value ) ;
2024-08-08 03:04:21 +02:00
continue ;
2019-09-20 16:28:55 +02:00
}
2024-08-08 03:04:21 +02:00
2024-08-09 20:39:56 +02:00
const u16 diff = value > min_value ? value - min_value : 0 ;
2024-08-08 03:04:21 +02:00
if ( diff > button_press_threshold & & value > pressed_button . value )
2022-08-13 09:56:04 +02:00
{
2025-04-05 21:50:45 +02:00
pressed_button = { . value = value , . name = name } ;
2022-08-13 09:56:04 +02:00
}
2019-09-20 16:28:55 +02:00
}
}
2024-08-09 20:39:56 +02:00
if ( call_type = = gui_call_type : : reset_input )
2024-08-08 03:04:21 +02:00
{
return connection : : no_data ;
}
2024-08-09 20:39:56 +02:00
if ( call_type = = gui_call_type : : blacklist )
2019-09-20 16:28:55 +02:00
{
if ( blacklist . empty ( ) )
2020-02-01 09:23:50 +01:00
input_log . success ( " %s Calibration: Blacklist is clear. No input spam detected " , m_type ) ;
2022-10-21 22:55:31 +02:00
return status ;
2019-09-20 16:28:55 +02:00
}
2020-07-04 13:18:45 +02:00
if ( callback )
{
2024-08-08 02:02:57 +02:00
pad_preview_values preview_values = get_preview_values ( data ) ;
2022-10-21 22:55:31 +02:00
const u32 battery_level = get_battery_level ( pad_id ) ;
2022-08-13 09:56:04 +02:00
if ( pressed_button . value > 0 )
2024-08-08 02:02:57 +02:00
callback ( pressed_button . value , pressed_button . name , pad_id , battery_level , std : : move ( preview_values ) ) ;
2020-07-04 13:18:45 +02:00
else
2024-08-08 02:02:57 +02:00
callback ( 0 , " " , pad_id , battery_level , std : : move ( preview_values ) ) ;
2020-07-04 13:18:45 +02:00
}
2022-10-21 22:55:31 +02:00
return status ;
2022-08-13 09:56:04 +02:00
}
2019-09-20 16:28:55 +02:00
2022-08-13 09:56:04 +02:00
void PadHandlerBase : : get_motion_sensors ( const std : : string & pad_id , const motion_callback & callback , const motion_fail_callback & fail_callback , motion_preview_values preview_values , const std : : array < AnalogSensor , 4 > & /*sensors*/ )
{
if ( ! b_has_motion )
{
return ;
}
// Reset sensors
auto device = get_device ( pad_id ) ;
2023-06-13 01:19:32 +02:00
const connection status = update_connection ( device ) ;
2022-08-13 09:56:04 +02:00
if ( status = = connection : : disconnected )
{
if ( fail_callback )
fail_callback ( pad_id , std : : move ( preview_values ) ) ;
return ;
}
if ( status = = connection : : no_data | | ! callback )
{
return ;
}
2024-07-08 20:17:21 +02:00
// Keep the pad cached to reduce expensive one time requests
if ( ! m_pad_for_pad_settings | | m_pad_for_pad_settings - > m_pad_handler ! = m_type )
{
m_pad_for_pad_settings = std : : make_shared < Pad > ( m_type , 0 , 0 , 0 , 0 ) ;
}
2022-08-13 09:56:04 +02:00
// Get the current motion values
2024-07-08 20:17:21 +02:00
pad_ensemble binding { m_pad_for_pad_settings , device , nullptr } ;
2022-08-13 09:56:04 +02:00
get_extended_info ( binding ) ;
for ( usz i = 0 ; i < preview_values . size ( ) ; i + + )
{
2024-07-08 20:17:21 +02:00
preview_values [ i ] = m_pad_for_pad_settings - > m_sensors [ i ] . m_value ;
2022-08-13 09:56:04 +02:00
}
callback ( pad_id , std : : move ( preview_values ) ) ;
2019-09-20 16:28:55 +02:00
}
2024-05-27 21:50:09 +02:00
void PadHandlerBase : : convert_stick_values ( u16 & x_out , u16 & y_out , s32 x_in , s32 y_in , u32 deadzone , u32 anti_deadzone , u32 padsquircling ) const
2020-07-12 19:30:45 +02:00
{
// Normalize our stick axis based on the deadzone
2024-05-27 21:50:09 +02:00
std : : tie ( x_out , y_out ) = NormalizeStickDeadzone ( x_in , y_in , deadzone , anti_deadzone ) ;
2020-07-12 19:30:45 +02:00
// Apply pad squircling if necessary
if ( padsquircling ! = 0 )
{
2024-09-08 20:03:14 +02:00
ConvertToSquirclePoint ( x_out , y_out , padsquircling ) ;
2020-07-12 19:30:45 +02:00
}
}
2019-09-20 16:28:55 +02:00
// Update the pad button values based on their type and thresholds. With this you can use axis or triggers as buttons or vice versa
2024-08-09 23:44:45 +02:00
void PadHandlerBase : : TranslateButtonPress ( const std : : shared_ptr < PadDevice > & device , u64 keyCode , bool & pressed , u16 & val , bool use_stick_multipliers , bool ignore_stick_threshold , bool ignore_trigger_threshold )
2019-09-20 16:28:55 +02:00
{
if ( ! device | | ! device - > config )
{
return ;
}
2022-10-15 12:41:21 +02:00
if ( get_is_left_trigger ( device , keyCode ) )
2019-09-20 16:28:55 +02:00
{
pressed = val > ( ignore_trigger_threshold ? 0 : device - > config - > ltriggerthreshold ) ;
val = pressed ? NormalizeTriggerInput ( val , device - > config - > ltriggerthreshold ) : 0 ;
}
2022-10-15 12:41:21 +02:00
else if ( get_is_right_trigger ( device , keyCode ) )
2019-09-20 16:28:55 +02:00
{
pressed = val > ( ignore_trigger_threshold ? 0 : device - > config - > rtriggerthreshold ) ;
val = pressed ? NormalizeTriggerInput ( val , device - > config - > rtriggerthreshold ) : 0 ;
}
2022-10-15 12:41:21 +02:00
else if ( get_is_left_stick ( device , keyCode ) )
2019-09-20 16:28:55 +02:00
{
pressed = val > ( ignore_stick_threshold ? 0 : device - > config - > lstickdeadzone ) ;
2024-08-09 23:44:45 +02:00
val = pressed ? NormalizeStickInput ( val , device - > config - > lstickdeadzone , use_stick_multipliers ? device - > config - > lstickmultiplier : 100 , ignore_stick_threshold ) : 0 ;
2019-09-20 16:28:55 +02:00
}
2022-10-15 12:41:21 +02:00
else if ( get_is_right_stick ( device , keyCode ) )
2019-09-20 16:28:55 +02:00
{
pressed = val > ( ignore_stick_threshold ? 0 : device - > config - > rstickdeadzone ) ;
2024-08-09 23:44:45 +02:00
val = pressed ? NormalizeStickInput ( val , device - > config - > rstickdeadzone , use_stick_multipliers ? device - > config - > rstickmultiplier : 100 , ignore_stick_threshold ) : 0 ;
2019-09-20 16:28:55 +02:00
}
else // normal button (should in theory also support sensitive buttons)
{
pressed = val > 0 ;
val = pressed ? val : 0 ;
}
}
2024-07-06 18:41:41 +02:00
bool PadHandlerBase : : bindPadToDevice ( std : : shared_ptr < Pad > pad )
2019-09-20 16:28:55 +02:00
{
2024-07-06 18:41:41 +02:00
if ( ! pad | | pad - > m_player_id > = g_cfg_input . player . size ( ) )
2022-01-27 21:06:37 +01:00
{
return false ;
}
2024-07-06 18:41:41 +02:00
const cfg_player * player_config = g_cfg_input . player [ pad - > m_player_id ] ;
2022-08-13 09:56:04 +02:00
if ( ! player_config )
{
return false ;
}
std : : shared_ptr < PadDevice > pad_device = get_device ( player_config - > device ) ;
2019-09-20 16:28:55 +02:00
if ( ! pad_device )
2020-10-25 20:59:03 +01:00
{
2022-08-13 09:56:04 +02:00
input_log . error ( " PadHandlerBase::bindPadToDevice: no PadDevice found for device '%s' " , player_config - > device . to_string ( ) ) ;
2019-09-20 16:28:55 +02:00
return false ;
2020-10-25 20:59:03 +01:00
}
2019-09-20 16:28:55 +02:00
2024-07-06 18:41:41 +02:00
m_pad_configs [ pad - > m_player_id ] . from_string ( player_config - > config . to_string ( ) ) ;
pad_device - > config = & m_pad_configs [ pad - > m_player_id ] ;
pad_device - > player_id = pad - > m_player_id ;
2021-08-10 21:45:26 +02:00
cfg_pad * config = pad_device - > config ;
if ( config = = nullptr )
2020-10-25 20:59:03 +01:00
{
2022-08-13 09:56:04 +02:00
input_log . error ( " PadHandlerBase::bindPadToDevice: no profile found for device %d '%s' " , m_bindings . size ( ) , player_config - > device . to_string ( ) ) ;
2019-09-20 16:28:55 +02:00
return false ;
2020-10-25 20:59:03 +01:00
}
2019-09-20 16:28:55 +02:00
2023-06-13 01:19:32 +02:00
std : : array < std : : set < u32 > , button : : button_count > mapping = get_mapped_key_codes ( pad_device , config ) ;
2019-09-20 16:28:55 +02:00
2020-04-25 02:12:19 +02:00
u32 pclass_profile = 0x0 ;
2023-08-26 15:47:52 +02:00
u32 capabilities = CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE ;
2020-04-25 02:12:19 +02:00
2023-06-13 01:19:32 +02:00
for ( const input : : product_info & product : input : : get_products_by_class ( config - > device_class_type ) )
2020-04-25 02:12:19 +02:00
{
2021-08-10 21:45:26 +02:00
if ( product . vendor_id = = config - > vendor_id & & product . product_id = = config - > product_id )
2020-04-25 02:12:19 +02:00
{
pclass_profile = product . pclass_profile ;
2023-08-26 15:47:52 +02:00
capabilities = product . capabilites ;
2020-04-25 02:12:19 +02:00
}
}
2025-04-05 21:50:45 +02:00
pad - > Init (
2019-09-20 16:28:55 +02:00
CELL_PAD_STATUS_DISCONNECTED ,
2023-08-26 15:47:52 +02:00
capabilities ,
2019-09-20 16:28:55 +02:00
CELL_PAD_DEV_TYPE_STANDARD ,
2021-08-10 21:45:26 +02:00
config - > device_class_type ,
2020-04-25 02:12:19 +02:00
pclass_profile ,
2021-08-10 21:45:26 +02:00
config - > vendor_id ,
config - > product_id ,
2025-04-05 21:50:45 +02:00
config - > pressure_intensity ) ;
2019-09-20 16:28:55 +02:00
2024-08-09 23:44:45 +02:00
if ( b_has_pressure_intensity_button )
{
pad - > m_buttons . emplace_back ( special_button_offset , mapping [ button : : pressure_intensity_button ] , special_button_value : : pressure_intensity ) ;
pad - > m_pressure_intensity_button_index = static_cast < s32 > ( pad - > m_buttons . size ( ) ) - 1 ;
}
if ( b_has_analog_limiter_button )
{
pad - > m_buttons . emplace_back ( special_button_offset , mapping [ button : : analog_limiter_button ] , special_button_value : : analog_limiter ) ;
pad - > m_analog_limiter_button_index = static_cast < s32 > ( pad - > m_buttons . size ( ) ) - 1 ;
}
2021-08-06 02:08:18 +02:00
2025-01-08 01:22:50 +01:00
if ( b_has_orientation )
{
pad - > m_buttons . emplace_back ( special_button_offset , mapping [ button : : orientation_reset_button ] , special_button_value : : orientation_reset ) ;
pad - > m_orientation_reset_button_index = static_cast < s32 > ( pad - > m_buttons . size ( ) ) - 1 ;
}
2019-09-20 16:28:55 +02:00
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL1 , mapping [ button : : up ] , CELL_PAD_CTRL_UP ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL1 , mapping [ button : : down ] , CELL_PAD_CTRL_DOWN ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL1 , mapping [ button : : left ] , CELL_PAD_CTRL_LEFT ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL1 , mapping [ button : : right ] , CELL_PAD_CTRL_RIGHT ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL2 , mapping [ button : : cross ] , CELL_PAD_CTRL_CROSS ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL2 , mapping [ button : : square ] , CELL_PAD_CTRL_SQUARE ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL2 , mapping [ button : : circle ] , CELL_PAD_CTRL_CIRCLE ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL2 , mapping [ button : : triangle ] , CELL_PAD_CTRL_TRIANGLE ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL2 , mapping [ button : : l1 ] , CELL_PAD_CTRL_L1 ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL2 , mapping [ button : : l2 ] , CELL_PAD_CTRL_L2 ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL1 , mapping [ button : : l3 ] , CELL_PAD_CTRL_L3 ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL2 , mapping [ button : : r1 ] , CELL_PAD_CTRL_R1 ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL2 , mapping [ button : : r2 ] , CELL_PAD_CTRL_R2 ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL1 , mapping [ button : : r3 ] , CELL_PAD_CTRL_R3 ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL1 , mapping [ button : : start ] , CELL_PAD_CTRL_START ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL1 , mapping [ button : : select ] , CELL_PAD_CTRL_SELECT ) ;
2023-02-10 03:53:10 +01:00
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_DIGITAL1 , mapping [ button : : ps ] , CELL_PAD_CTRL_PS ) ;
2019-09-20 16:28:55 +02:00
2023-08-26 15:47:52 +02:00
if ( pad - > m_class_type = = CELL_PAD_PCLASS_TYPE_SKATEBOARD )
{
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK , mapping [ button : : skateboard_ir_nose ] , CELL_PAD_CTRL_PRESS_TRIANGLE ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK , mapping [ button : : skateboard_ir_tail ] , CELL_PAD_CTRL_PRESS_CIRCLE ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK , mapping [ button : : skateboard_ir_left ] , CELL_PAD_CTRL_PRESS_CROSS ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK , mapping [ button : : skateboard_ir_right ] , CELL_PAD_CTRL_PRESS_SQUARE ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK , mapping [ button : : skateboard_tilt_left ] , CELL_PAD_CTRL_PRESS_L1 ) ;
pad - > m_buttons . emplace_back ( CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK , mapping [ button : : skateboard_tilt_right ] , CELL_PAD_CTRL_PRESS_R1 ) ;
}
2024-10-15 00:53:07 +02:00
pad - > m_sticks [ 0 ] = AnalogStick ( CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X , mapping [ button : : ls_left ] , mapping [ button : : ls_right ] ) ;
pad - > m_sticks [ 1 ] = AnalogStick ( CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y , mapping [ button : : ls_down ] , mapping [ button : : ls_up ] ) ;
pad - > m_sticks [ 2 ] = AnalogStick ( CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X , mapping [ button : : rs_left ] , mapping [ button : : rs_right ] ) ;
pad - > m_sticks [ 3 ] = AnalogStick ( CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y , mapping [ button : : rs_down ] , mapping [ button : : rs_up ] ) ;
2019-09-20 16:28:55 +02:00
2024-10-15 00:53:07 +02:00
pad - > m_sensors [ 0 ] = AnalogSensor ( CELL_PAD_BTN_OFFSET_SENSOR_X , 0 , 0 , 0 , DEFAULT_MOTION_X ) ;
pad - > m_sensors [ 1 ] = AnalogSensor ( CELL_PAD_BTN_OFFSET_SENSOR_Y , 0 , 0 , 0 , DEFAULT_MOTION_Y ) ;
pad - > m_sensors [ 2 ] = AnalogSensor ( CELL_PAD_BTN_OFFSET_SENSOR_Z , 0 , 0 , 0 , DEFAULT_MOTION_Z ) ;
pad - > m_sensors [ 3 ] = AnalogSensor ( CELL_PAD_BTN_OFFSET_SENSOR_G , 0 , 0 , 0 , DEFAULT_MOTION_G ) ;
2019-09-20 16:28:55 +02:00
2024-10-15 00:53:07 +02:00
pad - > m_vibrateMotors [ 0 ] = VibrateMotor ( true , 0 ) ;
pad - > m_vibrateMotors [ 1 ] = VibrateMotor ( false , 0 ) ;
2019-09-20 16:28:55 +02:00
2022-08-13 09:56:04 +02:00
m_bindings . emplace_back ( pad , pad_device , nullptr ) ;
2019-09-20 16:28:55 +02:00
return true ;
}
2023-06-13 01:19:32 +02:00
std : : array < std : : set < u32 > , PadHandlerBase : : button : : button_count > PadHandlerBase : : get_mapped_key_codes ( const std : : shared_ptr < PadDevice > & device , const cfg_pad * cfg )
2021-08-10 21:45:26 +02:00
{
2023-06-13 01:19:32 +02:00
std : : array < std : : set < u32 > , button : : button_count > mapping { } ;
2022-10-15 12:41:21 +02:00
if ( ! device | | ! cfg )
2021-08-10 21:45:26 +02:00
return mapping ;
2025-04-05 21:50:45 +02:00
device - > trigger_code_left = FindKeyCodes < u32 , u64 > ( button_list , cfg - > l2 ) ;
2023-06-13 01:19:32 +02:00
device - > trigger_code_right = FindKeyCodes < u32 , u64 > ( button_list , cfg - > r2 ) ;
2025-04-05 21:50:45 +02:00
device - > axis_code_left [ 0 ] = FindKeyCodes < u32 , u64 > ( button_list , cfg - > ls_left ) ;
device - > axis_code_left [ 1 ] = FindKeyCodes < u32 , u64 > ( button_list , cfg - > ls_right ) ;
device - > axis_code_left [ 2 ] = FindKeyCodes < u32 , u64 > ( button_list , cfg - > ls_down ) ;
device - > axis_code_left [ 3 ] = FindKeyCodes < u32 , u64 > ( button_list , cfg - > ls_up ) ;
2023-06-13 01:19:32 +02:00
device - > axis_code_right [ 0 ] = FindKeyCodes < u32 , u64 > ( button_list , cfg - > rs_left ) ;
device - > axis_code_right [ 1 ] = FindKeyCodes < u32 , u64 > ( button_list , cfg - > rs_right ) ;
device - > axis_code_right [ 2 ] = FindKeyCodes < u32 , u64 > ( button_list , cfg - > rs_down ) ;
device - > axis_code_right [ 3 ] = FindKeyCodes < u32 , u64 > ( button_list , cfg - > rs_up ) ;
2025-04-05 21:50:45 +02:00
mapping [ button : : up ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > up ) ;
mapping [ button : : down ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > down ) ;
mapping [ button : : left ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > left ) ;
mapping [ button : : right ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > right ) ;
mapping [ button : : cross ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > cross ) ;
mapping [ button : : square ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > square ) ;
mapping [ button : : circle ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > circle ) ;
2023-06-13 01:19:32 +02:00
mapping [ button : : triangle ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > triangle ) ;
2025-04-05 21:50:45 +02:00
mapping [ button : : start ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > start ) ;
mapping [ button : : select ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > select ) ;
mapping [ button : : l1 ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > l1 ) ;
mapping [ button : : l2 ] = narrow_set ( device - > trigger_code_left ) ;
mapping [ button : : l3 ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > l3 ) ;
mapping [ button : : r1 ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > r1 ) ;
mapping [ button : : r2 ] = narrow_set ( device - > trigger_code_right ) ;
mapping [ button : : r3 ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > r3 ) ;
mapping [ button : : ls_left ] = narrow_set ( device - > axis_code_left [ 0 ] ) ;
2023-06-13 01:19:32 +02:00
mapping [ button : : ls_right ] = narrow_set ( device - > axis_code_left [ 1 ] ) ;
2025-04-05 21:50:45 +02:00
mapping [ button : : ls_down ] = narrow_set ( device - > axis_code_left [ 2 ] ) ;
mapping [ button : : ls_up ] = narrow_set ( device - > axis_code_left [ 3 ] ) ;
mapping [ button : : rs_left ] = narrow_set ( device - > axis_code_right [ 0 ] ) ;
2023-06-13 01:19:32 +02:00
mapping [ button : : rs_right ] = narrow_set ( device - > axis_code_right [ 1 ] ) ;
2025-04-05 21:50:45 +02:00
mapping [ button : : rs_down ] = narrow_set ( device - > axis_code_right [ 2 ] ) ;
mapping [ button : : rs_up ] = narrow_set ( device - > axis_code_right [ 3 ] ) ;
mapping [ button : : ps ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > ps ) ;
mapping [ button : : skateboard_ir_nose ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > ir_nose ) ;
mapping [ button : : skateboard_ir_tail ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > ir_tail ) ;
mapping [ button : : skateboard_ir_left ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > ir_left ) ;
mapping [ button : : skateboard_ir_right ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > ir_right ) ;
mapping [ button : : skateboard_tilt_left ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > tilt_left ) ;
2023-08-26 15:47:52 +02:00
mapping [ button : : skateboard_tilt_right ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > tilt_right ) ;
2024-08-09 23:44:45 +02:00
if ( b_has_pressure_intensity_button )
{
mapping [ button : : pressure_intensity_button ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > pressure_intensity_button ) ;
}
if ( b_has_analog_limiter_button )
{
mapping [ button : : analog_limiter_button ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > analog_limiter_button ) ;
}
2021-08-06 02:08:18 +02:00
2025-01-08 01:22:50 +01:00
if ( b_has_orientation )
{
mapping [ button : : orientation_reset_button ] = FindKeyCodes < u32 , u32 > ( button_list , cfg - > orientation_reset_button ) ;
}
2019-09-20 16:28:55 +02:00
return mapping ;
}
2022-08-13 09:56:04 +02:00
void PadHandlerBase : : get_mapping ( 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 ;
2019-09-20 16:28:55 +02:00
if ( ! device | | ! pad )
return ;
2023-06-13 01:19:32 +02:00
const cfg_pad * cfg = device - > config ;
2023-06-14 20:57:30 +02:00
if ( ! cfg )
return ;
2019-09-20 16:28:55 +02:00
auto button_values = get_button_values ( device ) ;
2021-08-29 09:08:31 +02:00
// Find out if special buttons are pressed (introduced by RPCS3).
// These buttons will have a delay of one cycle, but whatever.
2024-08-09 23:44:45 +02:00
const bool analog_limiter_enabled = pad - > get_analog_limiter_button_active ( cfg - > analog_limiter_toggle_mode . get ( ) , pad - > m_player_id ) ;
2024-07-06 18:52:47 +02:00
const bool adjust_pressure = pad - > get_pressure_intensity_button_active ( cfg - > pressure_intensity_toggle_mode . get ( ) , pad - > m_player_id ) ;
2023-08-26 10:07:28 +02:00
const u32 pressure_intensity_deadzone = cfg - > pressure_intensity_deadzone . get ( ) ;
2021-08-29 09:08:31 +02:00
2019-09-20 16:28:55 +02:00
// Translate any corresponding keycodes to our normal DS3 buttons and triggers
2023-06-14 20:57:30 +02:00
for ( Button & button : pad - > m_buttons )
2019-09-20 16:28:55 +02:00
{
2023-06-13 01:19:32 +02:00
bool pressed { } ;
u16 value { } ;
2021-08-29 09:08:31 +02:00
2023-06-14 20:57:30 +02:00
for ( u32 code : button . m_key_codes )
2021-08-29 09:08:31 +02:00
{
2023-06-13 01:19:32 +02:00
bool press { } ;
u16 val = button_values [ code ] ;
2024-08-09 23:44:45 +02:00
TranslateButtonPress ( device , code , press , val , analog_limiter_enabled ) ;
2023-06-13 01:19:32 +02:00
if ( press )
{
// Modify pressure if necessary if the button was pressed
if ( adjust_pressure )
{
val = pad - > m_pressure_intensity ;
}
2023-08-26 10:07:28 +02:00
else if ( pressure_intensity_deadzone > 0 )
{
// Ignore triggers, since they have their own deadzones
if ( ! get_is_left_trigger ( device , code ) & & ! get_is_right_trigger ( device , code ) )
{
val = NormalizeDirectedInput ( val , pressure_intensity_deadzone , 255 ) ;
}
}
2023-06-13 01:19:32 +02:00
value = std : : max ( value , val ) ;
2023-08-26 10:07:28 +02:00
pressed = value > 0 ;
2023-06-13 01:19:32 +02:00
}
2021-08-29 09:08:31 +02:00
}
2023-06-14 20:57:30 +02:00
button . m_value = value ;
button . m_pressed = pressed ;
2019-09-20 16:28:55 +02:00
}
// used to get the absolute value of an axis
2023-06-13 01:19:32 +02:00
s32 stick_val [ 4 ] { } ;
2019-09-20 16:28:55 +02:00
// Translate any corresponding keycodes to our two sticks. (ignoring thresholds for now)
2023-08-29 20:46:09 +02:00
for ( usz i = 0 ; i < pad - > m_sticks . size ( ) ; i + + )
2019-09-20 16:28:55 +02:00
{
2023-06-13 01:19:32 +02:00
bool pressed { } ;
u16 val_min { } ;
u16 val_max { } ;
// m_key_codes_min are the mapped keys for left or down
for ( u32 key_min : pad - > m_sticks [ i ] . m_key_codes_min )
{
u16 val = button_values [ key_min ] ;
2024-08-09 23:44:45 +02:00
TranslateButtonPress ( device , key_min , pressed , val , analog_limiter_enabled , true ) ;
2019-09-20 16:28:55 +02:00
2023-06-13 01:19:32 +02:00
if ( pressed )
{
val_min = std : : max ( val_min , val ) ;
}
}
2019-09-20 16:28:55 +02:00
2023-06-13 01:19:32 +02:00
// m_key_codes_max are the mapped keys for right or up
for ( u32 key_max : pad - > m_sticks [ i ] . m_key_codes_max )
{
u16 val = button_values [ key_max ] ;
2024-08-09 23:44:45 +02:00
TranslateButtonPress ( device , key_max , pressed , val , analog_limiter_enabled , true ) ;
2023-06-13 01:19:32 +02:00
if ( pressed )
{
val_max = std : : max ( val_max , val ) ;
}
}
2019-09-20 16:28:55 +02:00
// cancel out opposing values and get the resulting difference
stick_val [ i ] = val_max - val_min ;
}
u16 lx , ly , rx , ry ;
2020-07-12 19:30:45 +02:00
// Normalize and apply pad squircling
2024-05-27 21:50:09 +02:00
convert_stick_values ( lx , ly , stick_val [ 0 ] , stick_val [ 1 ] , cfg - > lstickdeadzone , cfg - > lstick_anti_deadzone , cfg - > lpadsquircling ) ;
convert_stick_values ( rx , ry , stick_val [ 2 ] , stick_val [ 3 ] , cfg - > rstickdeadzone , cfg - > rstick_anti_deadzone , cfg - > rpadsquircling ) ;
2019-09-20 16:28:55 +02:00
if ( m_type = = pad_handler : : ds4 )
{
ly = 255 - ly ;
ry = 255 - ry ;
// these are added with previous value and divided to 'smooth' out the readings
// the ds4 seems to rapidly flicker sometimes between two values and this seems to stop that
pad - > m_sticks [ 0 ] . m_value = ( lx + pad - > m_sticks [ 0 ] . m_value ) / 2 ; // LX
pad - > m_sticks [ 1 ] . m_value = ( ly + pad - > m_sticks [ 1 ] . m_value ) / 2 ; // LY
pad - > m_sticks [ 2 ] . m_value = ( rx + pad - > m_sticks [ 2 ] . m_value ) / 2 ; // RX
pad - > m_sticks [ 3 ] . m_value = ( ry + pad - > m_sticks [ 3 ] . m_value ) / 2 ; // RY
}
else
{
pad - > m_sticks [ 0 ] . m_value = lx ;
pad - > m_sticks [ 1 ] . m_value = 255 - ly ;
pad - > m_sticks [ 2 ] . m_value = rx ;
pad - > m_sticks [ 3 ] . m_value = 255 - ry ;
}
}
2022-10-21 22:48:38 +02:00
void PadHandlerBase : : process ( )
2019-09-20 16:28:55 +02:00
{
2022-08-13 09:56:04 +02:00
for ( usz i = 0 ; i < m_bindings . size ( ) ; + + i )
2019-09-20 16:28:55 +02:00
{
2022-08-13 09:56:04 +02:00
auto & device = m_bindings [ i ] . device ;
2025-04-05 21:50:45 +02:00
auto & pad = m_bindings [ i ] . pad ;
2019-09-20 16:28:55 +02:00
if ( ! device | | ! pad )
continue ;
2025-01-08 01:22:50 +01:00
pad - > move_data . orientation_enabled = b_has_orientation & & device - > config & & device - > config - > orientation_enabled . get ( ) ;
2025-03-30 14:45:58 +02:00
// Disable pad vibration if no new data was sent for 3 seconds
if ( pad - > m_last_rumble_time_us > 0 )
{
std : : lock_guard lock ( pad : : g_pad_mutex ) ;
if ( ( get_system_time ( ) - pad - > m_last_rumble_time_us ) > 3'000'000 )
{
for ( VibrateMotor & motor : pad - > m_vibrateMotors )
{
motor . m_value = 0 ;
}
pad - > m_last_rumble_time_us = 0 ;
}
}
2023-06-13 01:19:32 +02:00
const connection status = update_connection ( device ) ;
2019-09-20 16:28:55 +02:00
switch ( status )
{
case connection : : no_data :
case connection : : connected :
{
if ( ! last_connection_status [ i ] )
{
2020-02-01 09:23:50 +01:00
input_log . success ( " %s device %d connected " , m_type , i ) ;
2023-08-09 10:11:59 +02:00
pad - > m_port_status | = CELL_PAD_STATUS_CONNECTED + CELL_PAD_STATUS_ASSIGN_CHANGES ;
2024-02-11 16:39:31 +01:00
2019-09-20 16:28:55 +02:00
last_connection_status [ i ] = true ;
connected_devices + + ;
2025-01-08 01:22:50 +01:00
if ( b_has_orientation )
{
device - > reset_orientation ( ) ;
}
2019-09-20 16:28:55 +02:00
}
if ( status = = connection : : no_data )
2022-08-13 09:56:04 +02:00
{
// TODO: don't skip entirely if buddy device has data
2023-06-14 20:57:30 +02:00
apply_pad_data ( m_bindings [ i ] ) ;
2019-09-20 16:28:55 +02:00
continue ;
2022-08-13 09:56:04 +02:00
}
2019-09-20 16:28:55 +02:00
break ;
}
case connection : : disconnected :
{
2023-05-18 17:17:30 +02:00
if ( g_cfg . io . keep_pads_connected )
{
if ( ! last_connection_status [ i ] )
{
input_log . success ( " %s device %d connected by force " , m_type , i ) ;
2023-08-09 10:11:59 +02:00
pad - > m_port_status | = CELL_PAD_STATUS_CONNECTED + CELL_PAD_STATUS_ASSIGN_CHANGES ;
2024-02-11 16:39:31 +01:00
2023-05-18 17:17:30 +02:00
last_connection_status [ i ] = true ;
connected_devices + + ;
}
continue ;
}
2019-09-20 16:28:55 +02:00
if ( last_connection_status [ i ] )
{
2020-02-01 09:23:50 +01:00
input_log . error ( " %s device %d disconnected " , m_type , i ) ;
2023-08-09 10:11:59 +02:00
2019-09-20 16:28:55 +02:00
pad - > m_port_status & = ~ CELL_PAD_STATUS_CONNECTED ;
pad - > m_port_status | = CELL_PAD_STATUS_ASSIGN_CHANGES ;
2023-08-09 10:11:59 +02:00
2019-09-20 16:28:55 +02:00
last_connection_status [ i ] = false ;
connected_devices - - ;
2025-01-08 01:22:50 +01:00
if ( b_has_orientation )
{
device - > reset_orientation ( ) ;
}
2019-09-20 16:28:55 +02:00
}
continue ;
}
}
2022-08-13 09:56:04 +02:00
get_mapping ( m_bindings [ i ] ) ;
get_extended_info ( m_bindings [ i ] ) ;
2025-01-08 01:22:50 +01:00
get_orientation ( m_bindings [ i ] ) ;
2022-08-13 09:56:04 +02:00
apply_pad_data ( m_bindings [ i ] ) ;
2019-09-20 16:28:55 +02:00
}
}
2025-01-08 01:22:50 +01:00
void PadHandlerBase : : set_raw_orientation ( ps_move_data & move_data , f32 accel_x , f32 accel_y , f32 accel_z , f32 gyro_x , f32 gyro_y , f32 gyro_z )
{
if ( ! move_data . orientation_enabled )
{
move_data . reset_sensors ( ) ;
return ;
}
// This function expects DS3 sensor accel values in linear velocity (m/s²) and gyro values in angular velocity (degree/s)
// The default position is flat on the ground, pointing forward.
// The accelerometers constantly measure G forces.
// The gyros measure changes in orientation and will reset when the device isn't moved anymore.
move_data . accelerometer_x = - accel_x ; // move_data: Increases if the device is rolled to the left
move_data . accelerometer_y = accel_z ; // move_data: Increases if the device is pitched upwards
move_data . accelerometer_z = accel_y ; // move_data: Increases if the device is moved upwards
move_data . gyro_x = degree_to_rad ( - gyro_x ) ; // move_data: Increases if the device is pitched upwards
move_data . gyro_y = degree_to_rad ( gyro_z ) ; // move_data: Increases if the device is rolled to the right
move_data . gyro_z = degree_to_rad ( - gyro_y ) ; // move_data: Increases if the device is yawed to the left
}
void PadHandlerBase : : set_raw_orientation ( Pad & pad )
{
if ( ! pad . move_data . orientation_enabled )
{
pad . move_data . reset_sensors ( ) ;
return ;
}
// acceleration (linear velocity in m/s²)
const f32 accel_x = ( pad . m_sensors [ 0 ] . m_value - 512 ) / static_cast < f32 > ( MOTION_ONE_G ) ;
const f32 accel_y = ( pad . m_sensors [ 1 ] . m_value - 512 ) / static_cast < f32 > ( MOTION_ONE_G ) ;
const f32 accel_z = ( pad . m_sensors [ 2 ] . m_value - 512 ) / static_cast < f32 > ( MOTION_ONE_G ) ;
// gyro (angular velocity in degree/s)
constexpr f32 gyro_x = 0.0f ;
const f32 gyro_y = ( pad . m_sensors [ 3 ] . m_value - 512 ) / ( 123.f / 90.f ) ;
constexpr f32 gyro_z = 0.0f ;
set_raw_orientation ( pad . move_data , accel_x , accel_y , accel_z , gyro_x , gyro_y , gyro_z ) ;
}
void PadHandlerBase : : get_orientation ( const pad_ensemble & binding ) const
{
2025-04-05 21:50:45 +02:00
if ( ! b_has_orientation )
return ;
2025-01-08 01:22:50 +01:00
const auto & pad = binding . pad ;
const auto & device = binding . device ;
2025-04-05 21:50:45 +02:00
if ( ! pad | | ! device )
return ;
2025-01-08 01:22:50 +01:00
if ( pad - > move_data . calibration_requested )
{
device - > reset_orientation ( ) ;
pad - > move_data . quaternion = ps_move_data : : default_quaternion ;
pad - > move_data . calibration_succeeded = true ;
return ;
}
2025-01-08 14:50:50 +01:00
if ( ! pad - > move_data . orientation_enabled | | pad - > get_orientation_reset_button_active ( ) )
2025-01-08 01:22:50 +01:00
{
// This can be called extensively in quick succession, so let's just reset the pointer instead of creating a new object.
device - > ahrs . reset ( ) ;
pad - > move_data . quaternion = ps_move_data : : default_quaternion ;
return ;
}
device - > update_orientation ( pad - > move_data ) ;
}
void PadDevice : : reset_orientation ( )
{
// Initialize Fusion
ahrs = std : : make_shared < FusionAhrs > ( ) ;
FusionAhrsInitialise ( ahrs . get ( ) ) ;
ahrs - > settings . convention = FusionConvention : : FusionConventionEnu ;
ahrs - > settings . gain = 0.0f ; // If gain is set, the algorithm tries to adjust the orientation over time.
FusionAhrsSetSettings ( ahrs . get ( ) , & ahrs - > settings ) ;
FusionAhrsReset ( ahrs . get ( ) ) ;
}
void PadDevice : : update_orientation ( ps_move_data & move_data )
{
if ( ! ahrs )
{
reset_orientation ( ) ;
}
// Get elapsed time since last update
const u64 now_us = get_system_time ( ) ;
const float elapsed_sec = ( last_ahrs_update_time_us = = 0 ) ? 0.0f : ( ( now_us - last_ahrs_update_time_us ) / 1'000'000 .0f ) ;
last_ahrs_update_time_us = now_us ;
// The ps move handler's axis may differ from the Fusion axis, so we have to map them correctly.
// Don't ask how the axis work. It's basically been trial and error.
ensure ( ahrs - > settings . convention = = FusionConvention : : FusionConventionEnu ) ; // East-North-Up
const FusionVector accelerometer {
2025-04-05 21:50:45 +02:00
. axis {
2025-01-08 01:22:50 +01:00
. x = - move_data . accelerometer_x ,
. y = + move_data . accelerometer_y ,
2025-04-05 21:50:45 +02:00
. z = + move_data . accelerometer_z } } ;
2025-01-08 01:22:50 +01:00
const FusionVector gyroscope {
2025-04-05 21:50:45 +02:00
. axis {
2025-01-08 01:22:50 +01:00
. x = + PadHandlerBase : : rad_to_degree ( move_data . gyro_x ) ,
. y = + PadHandlerBase : : rad_to_degree ( move_data . gyro_z ) ,
2025-04-05 21:50:45 +02:00
. z = - PadHandlerBase : : rad_to_degree ( move_data . gyro_y ) } } ;
2025-01-08 01:22:50 +01:00
2025-04-05 21:50:45 +02:00
FusionVector magnetometer { } ;
2025-01-08 01:22:50 +01:00
if ( move_data . magnetometer_enabled )
{
magnetometer = FusionVector {
2025-04-05 21:50:45 +02:00
. axis {
2025-01-08 01:22:50 +01:00
. x = move_data . magnetometer_x ,
. y = move_data . magnetometer_y ,
2025-04-05 21:50:45 +02:00
. z = move_data . magnetometer_z } } ;
2025-01-08 01:22:50 +01:00
}
// Update Fusion
FusionAhrsUpdate ( ahrs . get ( ) , gyroscope , accelerometer , magnetometer , elapsed_sec ) ;
// Get quaternion
const FusionQuaternion quaternion = FusionAhrsGetQuaternion ( ahrs . get ( ) ) ;
move_data . quaternion [ 0 ] = quaternion . array [ 1 ] ;
move_data . quaternion [ 1 ] = quaternion . array [ 2 ] ;
move_data . quaternion [ 2 ] = quaternion . array [ 3 ] ;
move_data . quaternion [ 3 ] = quaternion . array [ 0 ] ;
}