2025-01-24 08:27:08 +01:00
# ifdef HAVE_SDL3
2022-10-15 21:01:38 +02:00
# include "stdafx.h"
# include "sdl_pad_handler.h"
2023-08-26 12:24:47 +02:00
# include "Emu/system_utils.hpp"
# include "Emu/system_config.h"
2025-01-02 17:49:07 +01:00
# include "Emu/System.h"
2022-10-15 21:01:38 +02:00
2024-02-22 17:38:18 +01:00
# include <mutex>
2022-10-15 21:01:38 +02:00
LOG_CHANNEL ( sdl_log , " SDL " ) ;
2024-06-23 08:37:37 +02:00
struct sdl_instance
{
public :
sdl_instance ( ) = default ;
~ sdl_instance ( )
{
// Only quit SDL once on exit. SDL uses a global state internally...
if ( m_initialized )
{
sdl_log . notice ( " Quitting SDL ... " ) ;
SDL_Quit ( ) ;
}
}
2024-09-22 19:50:42 +02:00
static sdl_instance & get_instance ( )
{
2025-04-05 21:50:45 +02:00
static sdl_instance instance { } ;
2024-09-22 19:50:42 +02:00
return instance ;
}
2024-06-23 08:37:37 +02:00
bool initialize ( )
{
// Only init SDL once. SDL uses a global state internally...
if ( m_initialized )
{
return true ;
}
sdl_log . notice ( " Initializing SDL ... " ) ;
// Set non-dynamic hints before SDL_Init
if ( ! SDL_SetHint ( SDL_HINT_JOYSTICK_THREAD , " 1 " ) )
{
sdl_log . error ( " Could not set SDL_HINT_JOYSTICK_THREAD: %s " , SDL_GetError ( ) ) ;
}
2025-01-24 08:27:08 +01:00
if ( ! SDL_Init ( SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD ) )
2024-06-23 08:37:37 +02:00
{
sdl_log . error ( " Could not initialize! SDL Error: %s " , SDL_GetError ( ) ) ;
return false ;
}
2025-01-24 08:27:08 +01:00
SDL_SetLogPriorities ( SDL_LOG_PRIORITY_VERBOSE ) ;
SDL_SetLogOutputFunction ( [ ] ( void * , int category , SDL_LogPriority priority , const char * message )
2024-06-23 08:37:37 +02:00
{
2025-04-05 21:50:45 +02:00
std : : string category_name ;
switch ( category )
{
case SDL_LOG_CATEGORY_APPLICATION :
category_name = " app " ;
break ;
case SDL_LOG_CATEGORY_ERROR :
category_name = " error " ;
break ;
case SDL_LOG_CATEGORY_ASSERT :
category_name = " assert " ;
break ;
case SDL_LOG_CATEGORY_SYSTEM :
category_name = " system " ;
break ;
case SDL_LOG_CATEGORY_AUDIO :
category_name = " audio " ;
break ;
case SDL_LOG_CATEGORY_VIDEO :
category_name = " video " ;
break ;
case SDL_LOG_CATEGORY_RENDER :
category_name = " render " ;
break ;
case SDL_LOG_CATEGORY_INPUT :
category_name = " input " ;
break ;
case SDL_LOG_CATEGORY_TEST :
category_name = " test " ;
break ;
default :
category_name = fmt : : format ( " unknown(%d) " , category ) ;
break ;
}
2024-06-23 08:37:37 +02:00
2025-04-05 21:50:45 +02:00
switch ( priority )
{
case SDL_LOG_PRIORITY_VERBOSE :
case SDL_LOG_PRIORITY_DEBUG :
sdl_log . trace ( " %s: %s " , category_name , message ) ;
break ;
case SDL_LOG_PRIORITY_INFO :
sdl_log . notice ( " %s: %s " , category_name , message ) ;
break ;
case SDL_LOG_PRIORITY_WARN :
sdl_log . warning ( " %s: %s " , category_name , message ) ;
break ;
case SDL_LOG_PRIORITY_ERROR :
sdl_log . error ( " %s: %s " , category_name , message ) ;
break ;
case SDL_LOG_PRIORITY_CRITICAL :
sdl_log . error ( " %s: %s " , category_name , message ) ;
break ;
default :
break ;
}
} ,
nullptr ) ;
2024-06-23 08:37:37 +02:00
m_initialized = true ;
return true ;
}
private :
bool m_initialized = false ;
} ;
2024-02-22 17:38:18 +01:00
2024-06-11 22:30:16 +02:00
sdl_pad_handler : : sdl_pad_handler ( ) : PadHandlerBase ( pad_handler : : sdl )
2022-10-15 21:01:38 +02:00
{
button_list =
2025-04-05 21:50:45 +02:00
{
{ SDLKeyCodes : : None , " " } ,
{ SDLKeyCodes : : South , " South " } ,
{ SDLKeyCodes : : East , " East " } ,
{ SDLKeyCodes : : West , " West " } ,
{ SDLKeyCodes : : North , " North " } ,
{ SDLKeyCodes : : Left , " Left " } ,
{ SDLKeyCodes : : Right , " Right " } ,
{ SDLKeyCodes : : Up , " Up " } ,
{ SDLKeyCodes : : Down , " Down " } ,
{ SDLKeyCodes : : LB , " LB " } ,
{ SDLKeyCodes : : RB , " RB " } ,
{ SDLKeyCodes : : Back , " Back " } ,
{ SDLKeyCodes : : Start , " Start " } ,
{ SDLKeyCodes : : LS , " LS " } ,
{ SDLKeyCodes : : RS , " RS " } ,
{ SDLKeyCodes : : Guide , " Guide " } ,
{ SDLKeyCodes : : Misc1 , " Misc 1 " } ,
{ SDLKeyCodes : : RPaddle1 , " R Paddle 1 " } ,
{ SDLKeyCodes : : LPaddle1 , " L Paddle 1 " } ,
{ SDLKeyCodes : : RPaddle2 , " R Paddle 2 " } ,
{ SDLKeyCodes : : LPaddle2 , " L Paddle 2 " } ,
{ SDLKeyCodes : : Touchpad , " Touchpad " } ,
{ SDLKeyCodes : : Touch_L , " Touch Left " } ,
{ SDLKeyCodes : : Touch_R , " Touch Right " } ,
{ SDLKeyCodes : : Touch_U , " Touch Up " } ,
{ SDLKeyCodes : : Touch_D , " Touch Down " } ,
{ SDLKeyCodes : : LT , " LT " } ,
{ SDLKeyCodes : : RT , " RT " } ,
{ SDLKeyCodes : : LSXNeg , " LS X- " } ,
{ SDLKeyCodes : : LSXPos , " LS X+ " } ,
{ SDLKeyCodes : : LSYPos , " LS Y+ " } ,
{ SDLKeyCodes : : LSYNeg , " LS Y- " } ,
{ SDLKeyCodes : : RSXNeg , " RS X- " } ,
{ SDLKeyCodes : : RSXPos , " RS X+ " } ,
{ SDLKeyCodes : : RSYPos , " RS Y+ " } ,
{ SDLKeyCodes : : RSYNeg , " RS Y- " } ,
} ;
2022-10-15 21:01:38 +02:00
init_configs ( ) ;
// Define border values
thumb_max = SDL_JOYSTICK_AXIS_MAX ;
trigger_min = 0 ;
trigger_max = SDL_JOYSTICK_AXIS_MAX ;
// set capabilities
b_has_config = true ;
b_has_deadzones = true ;
b_has_rumble = true ;
b_has_motion = true ;
b_has_led = true ;
2025-01-24 08:27:08 +01:00
b_has_player_led = true ;
2022-10-15 21:01:38 +02:00
b_has_rgb = true ;
b_has_battery = true ;
2024-07-06 17:46:37 +02:00
b_has_battery_led = true ;
2025-01-08 01:22:50 +01:00
b_has_orientation = true ;
2022-10-15 21:01:38 +02:00
m_trigger_threshold = trigger_max / 2 ;
m_thumb_threshold = thumb_max / 2 ;
}
sdl_pad_handler : : ~ sdl_pad_handler ( )
{
if ( ! m_is_init )
return ;
for ( auto & controller : m_controllers )
{
2025-01-24 08:27:08 +01:00
if ( controller . second & & controller . second - > sdl . gamepad )
2022-10-15 21:01:38 +02:00
{
set_rumble ( controller . second . get ( ) , 0 , 0 ) ;
2025-01-24 08:27:08 +01:00
SDL_CloseGamepad ( controller . second - > sdl . gamepad ) ;
controller . second - > sdl . gamepad = nullptr ;
2022-10-15 21:01:38 +02:00
}
}
}
void sdl_pad_handler : : init_config ( cfg_pad * cfg )
{
2025-04-05 21:50:45 +02:00
if ( ! cfg )
return ;
2022-10-15 21:01:38 +02:00
// Set default button mapping
2025-04-05 21:50:45 +02:00
cfg - > ls_left . def = : : at32 ( button_list , SDLKeyCodes : : LSXNeg ) ;
cfg - > ls_down . def = : : at32 ( button_list , SDLKeyCodes : : LSYNeg ) ;
2022-10-15 21:01:38 +02:00
cfg - > ls_right . def = : : at32 ( button_list , SDLKeyCodes : : LSXPos ) ;
2025-04-05 21:50:45 +02:00
cfg - > ls_up . def = : : at32 ( button_list , SDLKeyCodes : : LSYPos ) ;
cfg - > rs_left . def = : : at32 ( button_list , SDLKeyCodes : : RSXNeg ) ;
cfg - > rs_down . def = : : at32 ( button_list , SDLKeyCodes : : RSYNeg ) ;
2022-10-15 21:01:38 +02:00
cfg - > rs_right . def = : : at32 ( button_list , SDLKeyCodes : : RSXPos ) ;
2025-04-05 21:50:45 +02:00
cfg - > rs_up . def = : : at32 ( button_list , SDLKeyCodes : : RSYPos ) ;
cfg - > start . def = : : at32 ( button_list , SDLKeyCodes : : Start ) ;
cfg - > select . def = : : at32 ( button_list , SDLKeyCodes : : Back ) ;
cfg - > ps . def = : : at32 ( button_list , SDLKeyCodes : : Guide ) ;
cfg - > square . def = : : at32 ( button_list , SDLKeyCodes : : West ) ;
cfg - > cross . def = : : at32 ( button_list , SDLKeyCodes : : South ) ;
cfg - > circle . def = : : at32 ( button_list , SDLKeyCodes : : East ) ;
2025-01-24 08:27:08 +01:00
cfg - > triangle . def = : : at32 ( button_list , SDLKeyCodes : : North ) ;
2025-04-05 21:50:45 +02:00
cfg - > left . def = : : at32 ( button_list , SDLKeyCodes : : Left ) ;
cfg - > down . def = : : at32 ( button_list , SDLKeyCodes : : Down ) ;
cfg - > right . def = : : at32 ( button_list , SDLKeyCodes : : Right ) ;
cfg - > up . def = : : at32 ( button_list , SDLKeyCodes : : Up ) ;
cfg - > r1 . def = : : at32 ( button_list , SDLKeyCodes : : RB ) ;
cfg - > r2 . def = : : at32 ( button_list , SDLKeyCodes : : RT ) ;
cfg - > r3 . def = : : at32 ( button_list , SDLKeyCodes : : RS ) ;
cfg - > l1 . def = : : at32 ( button_list , SDLKeyCodes : : LB ) ;
cfg - > l2 . def = : : at32 ( button_list , SDLKeyCodes : : LT ) ;
cfg - > l3 . def = : : at32 ( button_list , SDLKeyCodes : : LS ) ;
2022-10-15 21:01:38 +02:00
cfg - > pressure_intensity_button . def = : : at32 ( button_list , SDLKeyCodes : : None ) ;
2024-08-09 23:44:45 +02:00
cfg - > analog_limiter_button . def = : : at32 ( button_list , SDLKeyCodes : : None ) ;
2025-01-08 01:22:50 +01:00
cfg - > orientation_reset_button . def = : : at32 ( button_list , SDLKeyCodes : : None ) ;
2022-10-15 21:01:38 +02: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%
2025-04-05 21:50:45 +02:00
cfg - > lstickdeadzone . def = 8000 ; // between 0 and SDL_JOYSTICK_AXIS_MAX
cfg - > rstickdeadzone . def = 8000 ; // between 0 and SDL_JOYSTICK_AXIS_MAX
cfg - > ltriggerthreshold . def = 0 ; // between 0 and SDL_JOYSTICK_AXIS_MAX
cfg - > rtriggerthreshold . def = 0 ; // between 0 and SDL_JOYSTICK_AXIS_MAX
cfg - > lpadsquircling . def = 8000 ;
cfg - > rpadsquircling . def = 8000 ;
2022-10-15 21:01:38 +02:00
// Set default color value
cfg - > colorR . def = 0 ;
cfg - > colorG . def = 0 ;
cfg - > colorB . def = 20 ;
// Set default LED options
2025-04-05 21:50:45 +02:00
cfg - > led_battery_indicator . def = false ;
2022-10-15 21:01:38 +02:00
cfg - > led_battery_indicator_brightness . def = 10 ;
2025-04-05 21:50:45 +02:00
cfg - > led_low_battery_blink . def = true ;
2022-10-15 21:01:38 +02:00
// apply defaults
cfg - > from_default ( ) ;
}
bool sdl_pad_handler : : Init ( )
{
if ( m_is_init )
return true ;
2025-01-02 17:49:07 +01:00
bool instance_success ;
Emu . BlockingCallFromMainThread ( [ & instance_success ] ( )
2025-04-05 21:50:45 +02:00
{
instance_success = sdl_instance : : get_instance ( ) . initialize ( ) ;
} ) ;
2025-01-02 17:49:07 +01:00
if ( ! instance_success )
2024-06-23 08:37:37 +02:00
return false ;
2022-10-15 21:01:38 +02:00
2023-08-26 12:24:47 +02:00
if ( g_cfg . io . load_sdl_mappings )
{
const std : : string db_path = rpcs3 : : utils : : get_input_config_root ( ) + " gamecontrollerdb.txt " ;
sdl_log . notice ( " Adding mappings from file '%s' " , db_path ) ;
if ( fs : : is_file ( db_path ) )
{
2025-01-24 08:27:08 +01:00
if ( SDL_AddGamepadMappingsFromFile ( db_path . c_str ( ) ) < 0 )
2023-08-26 12:24:47 +02:00
{
sdl_log . error ( " Could not add mappings from file '%s'! SDL Error: %s " , db_path , SDL_GetError ( ) ) ;
}
}
else
{
2023-10-22 10:35:25 +02:00
sdl_log . warning ( " Could not add mappings from file '%s'! File does not exist! " , db_path ) ;
2023-08-26 12:24:47 +02:00
}
}
2024-01-02 16:51:25 +01:00
if ( const char * revision = SDL_GetRevision ( ) ; revision & & strlen ( revision ) > 0 )
{
2025-01-24 08:27:08 +01:00
sdl_log . notice ( " Using version: %d.%d.%d (revision='%s') " , SDL_MAJOR_VERSION , SDL_MINOR_VERSION , SDL_MICRO_VERSION , revision ) ;
2024-01-02 16:51:25 +01:00
}
else
{
2025-01-24 08:27:08 +01:00
sdl_log . notice ( " Using version: %d.%d.%d " , SDL_MAJOR_VERSION , SDL_MINOR_VERSION , SDL_MICRO_VERSION ) ;
2024-01-02 16:51:25 +01:00
}
2022-10-15 21:01:38 +02:00
m_is_init = true ;
enumerate_devices ( ) ;
return true ;
}
void sdl_pad_handler : : process ( )
{
if ( ! m_is_init )
return ;
SDL_PumpEvents ( ) ;
PadHandlerBase : : process ( ) ;
}
2025-01-24 08:27:08 +01:00
SDLDevice : : sdl_info sdl_pad_handler : : get_sdl_info ( SDL_JoystickID id )
2022-10-15 21:01:38 +02:00
{
SDLDevice : : sdl_info info { } ;
2025-01-24 08:27:08 +01:00
info . gamepad = SDL_OpenGamepad ( id ) ;
2022-10-15 21:01:38 +02:00
2025-01-24 08:27:08 +01:00
if ( ! info . gamepad )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
sdl_log . error ( " Could not open device %d! SDL Error: %s " , id , SDL_GetError ( ) ) ;
2022-10-15 21:01:38 +02:00
return { } ;
}
2025-01-24 08:27:08 +01:00
if ( const char * name = SDL_GetGamepadName ( info . gamepad ) )
2022-10-15 21:01:38 +02:00
{
info . name = name ;
}
2025-01-24 08:27:08 +01:00
if ( const char * path = SDL_GetGamepadPath ( info . gamepad ) )
2022-10-15 21:01:38 +02:00
{
info . path = path ;
}
2025-01-24 08:27:08 +01:00
if ( const char * serial = SDL_GetGamepadSerial ( info . gamepad ) )
2022-10-15 21:01:38 +02:00
{
info . serial = serial ;
}
2025-01-24 08:27:08 +01:00
const SDL_PropertiesID property_id = SDL_GetGamepadProperties ( info . gamepad ) ;
if ( ! property_id )
{
sdl_log . error ( " Could not get properties of device %d! SDL Error: %s " , id , SDL_GetError ( ) ) ;
}
info . type = SDL_GetGamepadType ( info . gamepad ) ;
info . vid = SDL_GetGamepadVendor ( info . gamepad ) ;
info . pid = SDL_GetGamepadProduct ( info . gamepad ) ;
info . product_version = SDL_GetGamepadProductVersion ( info . gamepad ) ;
info . firmware_version = SDL_GetGamepadFirmwareVersion ( info . gamepad ) ;
info . has_led = SDL_HasProperty ( property_id , SDL_PROP_GAMEPAD_CAP_RGB_LED_BOOLEAN ) ;
info . has_mono_led = SDL_HasProperty ( property_id , SDL_PROP_GAMEPAD_CAP_MONO_LED_BOOLEAN ) ;
info . has_player_led = SDL_HasProperty ( property_id , SDL_PROP_GAMEPAD_CAP_PLAYER_LED_BOOLEAN ) ;
info . has_rumble = SDL_HasProperty ( property_id , SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN ) ;
info . has_rumble_triggers = SDL_HasProperty ( property_id , SDL_PROP_GAMEPAD_CAP_TRIGGER_RUMBLE_BOOLEAN ) ;
info . has_accel = SDL_GamepadHasSensor ( info . gamepad , SDL_SENSOR_ACCEL ) ;
info . has_gyro = SDL_GamepadHasSensor ( info . gamepad , SDL_SENSOR_GYRO ) ;
if ( const int num_touchpads = SDL_GetNumGamepadTouchpads ( info . gamepad ) ; num_touchpads > 0 )
2024-07-30 22:51:17 +02:00
{
info . touchpads . resize ( num_touchpads ) ;
for ( int i = 0 ; i < num_touchpads ; i + + )
{
SDLDevice : : touchpad & touchpad = : : at32 ( info . touchpads , i ) ;
touchpad . index = i ;
2025-01-24 08:27:08 +01:00
if ( const int num_fingers = SDL_GetNumGamepadTouchpadFingers ( info . gamepad , touchpad . index ) ; num_fingers > 0 )
2024-07-30 22:51:17 +02:00
{
touchpad . fingers . resize ( num_fingers ) ;
for ( int f = 0 ; f < num_fingers ; f + + )
{
: : at32 ( touchpad . fingers , f ) . index = f ;
}
}
}
}
2025-01-24 08:27:08 +01:00
sdl_log . notice ( " Found game pad %d: type=%d, name='%s', path='%s', serial='%s', vid=0x%x, pid=0x%x, product_version=0x%x, firmware_version=0x%x, has_led=%d, has_player_led=%d, has_mono_led=%d, has_rumble=%d, has_rumble_triggers=%d, has_accel=%d, has_gyro=%d " ,
id , static_cast < int > ( info . type ) , info . name , info . path , info . serial , info . vid , info . pid , info . product_version , info . firmware_version , info . has_led , info . has_player_led , info . has_mono_led , info . has_rumble , info . has_rumble_triggers , info . has_accel , info . has_gyro ) ;
2022-10-15 21:01:38 +02:00
if ( info . has_accel )
{
2025-01-24 08:27:08 +01:00
if ( ! SDL_SetGamepadSensorEnabled ( info . gamepad , SDL_SENSOR_ACCEL , true ) | |
! SDL_GamepadSensorEnabled ( info . gamepad , SDL_SENSOR_ACCEL ) )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
sdl_log . error ( " Could not activate acceleration sensor of device %d! SDL Error: %s " , id , SDL_GetError ( ) ) ;
2022-10-15 21:01:38 +02:00
info . has_accel = false ;
}
else
{
2025-01-24 08:27:08 +01:00
info . data_rate_accel = SDL_GetGamepadSensorDataRate ( info . gamepad , SDL_SENSOR_ACCEL ) ;
sdl_log . notice ( " Acceleration sensor data rate of device %d = %.2f/s " , id , info . data_rate_accel ) ;
2022-10-15 21:01:38 +02:00
}
}
if ( info . has_gyro )
{
2025-01-24 08:27:08 +01:00
if ( ! SDL_SetGamepadSensorEnabled ( info . gamepad , SDL_SENSOR_GYRO , true ) | |
! SDL_GamepadSensorEnabled ( info . gamepad , SDL_SENSOR_GYRO ) )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
sdl_log . error ( " Could not activate gyro sensor of device %d! SDL Error: %s " , id , SDL_GetError ( ) ) ;
2022-10-15 21:01:38 +02:00
info . has_gyro = false ;
}
else
{
2025-01-24 08:27:08 +01:00
info . data_rate_gyro = SDL_GetGamepadSensorDataRate ( info . gamepad , SDL_SENSOR_GYRO ) ;
sdl_log . notice ( " Gyro sensor data rate of device %d = %.2f/s " , id , info . data_rate_accel ) ;
2022-10-15 21:01:38 +02:00
}
}
2025-01-24 08:27:08 +01:00
for ( int i = 0 ; i < SDL_GAMEPAD_BUTTON_COUNT ; i + + )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
const SDL_GamepadButton button_id = static_cast < SDL_GamepadButton > ( i ) ;
if ( SDL_GamepadHasButton ( info . gamepad , button_id ) )
2022-10-15 21:01:38 +02:00
{
info . button_ids . insert ( button_id ) ;
}
}
2025-01-24 08:27:08 +01:00
for ( int i = 0 ; i < SDL_GAMEPAD_AXIS_COUNT ; i + + )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
const SDL_GamepadAxis axis_id = static_cast < SDL_GamepadAxis > ( i ) ;
if ( SDL_GamepadHasAxis ( info . gamepad , axis_id ) )
2022-10-15 21:01:38 +02:00
{
info . axis_ids . insert ( axis_id ) ;
}
}
return info ;
}
std : : vector < pad_list_entry > sdl_pad_handler : : list_devices ( )
{
std : : vector < pad_list_entry > pads_list ;
if ( ! Init ( ) )
return pads_list ;
for ( const auto & controller : m_controllers )
{
pads_list . emplace_back ( controller . first , false ) ;
}
return pads_list ;
}
void sdl_pad_handler : : enumerate_devices ( )
{
if ( ! m_is_init )
return ;
2025-01-24 08:27:08 +01:00
int count = 0 ;
SDL_JoystickID * gamepads = SDL_GetGamepads ( & count ) ;
for ( int i = 0 ; i < count & & gamepads ; i + + )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
if ( SDLDevice : : sdl_info info = get_sdl_info ( gamepads [ i ] ) ; info . gamepad )
2022-10-15 21:01:38 +02:00
{
std : : shared_ptr < SDLDevice > dev = std : : make_shared < SDLDevice > ( ) ;
dev - > sdl = std : : move ( info ) ;
2023-06-11 13:08:07 +02:00
// Count existing real devices with the same name
u32 device_count = 1 ; // This device also counts
for ( const auto & controller : m_controllers )
{
if ( controller . second & & ! controller . second - > sdl . is_virtual_device & & controller . second - > sdl . name = = dev - > sdl . name )
{
device_count + + ;
}
}
// Add real device
const std : : string device_name = fmt : : format ( " %s %d " , dev - > sdl . name , device_count ) ;
m_controllers [ device_name ] = std : : move ( dev ) ;
}
}
2025-01-24 08:27:08 +01:00
SDL_free ( gamepads ) ;
2023-06-11 13:08:07 +02:00
}
2025-01-24 08:27:08 +01:00
std : : shared_ptr < SDLDevice > sdl_pad_handler : : get_device_by_gamepad ( SDL_Gamepad * gamepad ) const
2023-06-11 13:08:07 +02:00
{
2025-01-24 08:27:08 +01:00
if ( ! gamepad )
2023-06-11 13:08:07 +02:00
return nullptr ;
2025-01-24 08:27:08 +01:00
const char * name = SDL_GetGamepadName ( gamepad ) ;
const char * path = SDL_GetGamepadPath ( gamepad ) ;
const char * serial = SDL_GetGamepadSerial ( gamepad ) ;
2023-06-11 13:08:07 +02:00
// Try to find a real device
for ( const auto & controller : m_controllers )
{
if ( ! controller . second | | controller . second - > sdl . is_virtual_device )
continue ;
const auto is_same = [ ] ( const char * c , std : : string_view s ) - > bool
{
return c ? c = = s : s . empty ( ) ;
} ;
if ( is_same ( name , controller . second - > sdl . name ) & &
is_same ( path , controller . second - > sdl . path ) & &
is_same ( serial , controller . second - > sdl . serial ) )
{
return controller . second ;
}
}
// Try to find a virtual device if we can't find a real device
for ( const auto & controller : m_controllers )
{
if ( ! controller . second | | ! controller . second - > sdl . is_virtual_device )
continue ;
if ( name & & controller . second - > sdl . name . starts_with ( name ) )
{
return controller . second ;
2022-10-15 21:01:38 +02:00
}
}
2023-06-11 13:08:07 +02:00
return nullptr ;
2022-10-15 21:01:38 +02:00
}
std : : shared_ptr < PadDevice > sdl_pad_handler : : get_device ( const std : : string & device )
{
if ( ! Init ( ) | | device . empty ( ) )
return nullptr ;
if ( auto it = m_controllers . find ( device ) ; it ! = m_controllers . end ( ) )
{
return it - > second ;
}
// Add a virtual controller until it is actually attached
std : : shared_ptr < SDLDevice > dev = std : : make_unique < SDLDevice > ( ) ;
dev - > sdl . name = device ;
2023-06-11 13:08:07 +02:00
dev - > sdl . is_virtual_device = true ;
2022-10-15 21:01:38 +02:00
m_controllers . emplace ( device , dev ) ;
sdl_log . warning ( " Adding empty device: %s " , device ) ;
return dev ;
}
PadHandlerBase : : connection sdl_pad_handler : : update_connection ( const std : : shared_ptr < PadDevice > & device )
{
if ( SDLDevice * dev = static_cast < SDLDevice * > ( device . get ( ) ) )
{
2025-01-24 08:27:08 +01:00
if ( dev - > sdl . gamepad )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
if ( SDL_GamepadConnected ( dev - > sdl . gamepad ) )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
if ( SDL_HasEvents ( SDL_EventType : : SDL_EVENT_GAMEPAD_AXIS_MOTION , SDL_EventType : : SDL_EVENT_GAMEPAD_BUTTON_UP ) | |
SDL_HasEvents ( SDL_EventType : : SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN , SDL_EventType : : SDL_EVENT_GAMEPAD_SENSOR_UPDATE ) | |
SDL_HasEvent ( SDL_EventType : : SDL_EVENT_JOYSTICK_BATTERY_UPDATED ) )
2022-10-15 21:01:38 +02:00
{
return connection : : connected ;
}
return connection : : no_data ;
}
2025-01-24 08:27:08 +01:00
SDL_CloseGamepad ( dev - > sdl . gamepad ) ;
dev - > sdl . gamepad = nullptr ;
2022-10-15 21:01:38 +02:00
}
// Try to reconnect
2025-01-24 08:27:08 +01:00
int count = 0 ;
SDL_JoystickID * gamepads = SDL_GetGamepads ( & count ) ;
for ( int i = 0 ; i < count ; i + + )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
// Get game pad
SDL_Gamepad * gamepad = SDL_OpenGamepad ( gamepads [ i ] ) ;
if ( ! gamepad )
2023-06-11 13:08:07 +02:00
{
continue ;
}
// Find out if we already know this controller
2025-01-24 08:27:08 +01:00
std : : shared_ptr < SDLDevice > sdl_device = get_device_by_gamepad ( gamepad ) ;
2023-06-11 13:08:07 +02:00
if ( ! sdl_device )
{
2025-01-24 08:27:08 +01:00
// Close the game pad if we don't know it.
SDL_CloseGamepad ( gamepad ) ;
2023-06-11 13:08:07 +02:00
continue ;
}
// Re-attach the controller if the device matches the current one
if ( sdl_device . get ( ) = = dev )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
if ( SDLDevice : : sdl_info info = get_sdl_info ( gamepads [ i ] ) ; info . gamepad )
2022-10-15 21:01:38 +02:00
{
2023-06-11 13:08:07 +02:00
dev - > sdl = std : : move ( info ) ;
2022-10-15 21:01:38 +02:00
}
2023-06-11 13:08:07 +02:00
break ;
2022-10-15 21:01:38 +02:00
}
}
2025-01-24 08:27:08 +01:00
SDL_free ( gamepads ) ;
2022-10-15 21:01:38 +02:00
}
return connection : : disconnected ;
}
2025-01-24 08:27:08 +01:00
void sdl_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 )
2022-10-15 21:01:38 +02:00
{
std : : shared_ptr < PadDevice > device = get_device ( padId ) ;
SDLDevice * dev = static_cast < SDLDevice * > ( device . get ( ) ) ;
if ( ! dev )
return ;
dev - > player_id = player_id ;
2023-01-17 01:28:54 +01:00
dev - > config = get_config ( padId ) ;
2022-10-15 21:01:38 +02:00
2023-01-17 01:28:54 +01:00
ensure ( dev - > config ) ;
2022-10-15 21:01:38 +02:00
set_rumble ( dev , large_motor , small_motor ) ;
2025-01-24 08:27:08 +01:00
dev - > update_player_leds = true ;
dev - > config - > player_led_enabled . set ( player_led ) ;
2022-10-15 21:01:38 +02:00
if ( battery_led )
{
const u32 combined_color = get_battery_color ( dev - > sdl . power_level , battery_led_brightness ) ;
dev - > config - > colorR . set ( combined_color > > 8 ) ;
dev - > config - > colorG . set ( combined_color & 0xff ) ;
dev - > config - > colorB . set ( 0 ) ;
}
else if ( r > = 0 & & g > = 0 & & b > = 0 & & r < = 255 & & g < = 255 & & b < = 255 )
{
dev - > config - > colorR . set ( r ) ;
dev - > config - > colorG . set ( g ) ;
dev - > config - > colorB . set ( b ) ;
}
2025-01-24 08:27:08 +01:00
if ( ( dev - > sdl . has_led | | dev - > sdl . has_mono_led ) & & ! SDL_SetGamepadLED ( dev - > sdl . gamepad , r , g , b ) )
2022-10-15 21:01:38 +02:00
{
sdl_log . error ( " Could not set LED of device %d! SDL Error: %s " , player_id , SDL_GetError ( ) ) ;
}
2025-01-24 08:27:08 +01:00
if ( dev - > sdl . has_player_led & & ! SDL_SetGamepadPlayerIndex ( dev - > sdl . gamepad , player_led ? player_id : - 1 ) )
{
sdl_log . error ( " Could not set player LED of device %d! SDL Error: %s " , player_id , SDL_GetError ( ) ) ;
}
2022-10-15 21:01:38 +02:00
}
2025-01-24 08:27:08 +01:00
u32 sdl_pad_handler : : get_battery_color ( int power_level , u32 brightness ) const
2022-10-15 21:01:38 +02:00
{
u32 combined_color { } ;
2025-04-05 21:50:45 +02:00
if ( power_level < 20 )
combined_color = 0xFF00 ;
else if ( power_level < 40 )
combined_color = 0xFF33 ;
else if ( power_level < 60 )
combined_color = 0xFFCC ;
else if ( power_level < 80 )
combined_color = 0x66FF ;
else
combined_color = 0x00FF ;
2023-02-13 11:33:06 +01:00
2022-10-15 21:01:38 +02:00
const u32 red = ( combined_color > > 8 ) * brightness / 100 ;
const u32 green = ( combined_color & 0xff ) * brightness / 100 ;
return ( ( red < < 8 ) | green ) ;
}
u32 sdl_pad_handler : : get_battery_level ( const std : : string & padId )
{
std : : shared_ptr < PadDevice > device = get_device ( padId ) ;
SDLDevice * dev = static_cast < SDLDevice * > ( device . get ( ) ) ;
if ( ! dev )
return 0 ;
2025-01-24 08:27:08 +01:00
if ( dev - > sdl . gamepad )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
const SDL_PowerState power_state = SDL_GetGamepadPowerInfo ( dev - > sdl . gamepad , & dev - > sdl . power_level ) ;
switch ( power_state )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
case SDL_PowerState : : SDL_POWERSTATE_ERROR :
case SDL_PowerState : : SDL_POWERSTATE_UNKNOWN :
return 0 ;
default :
return std : : clamp ( dev - > sdl . power_level , 0 , 100 ) ;
2022-10-15 21:01:38 +02:00
}
}
2025-04-05 21:50:45 +02:00
return 0 ;
2022-10-15 21:01:38 +02:00
}
void sdl_pad_handler : : get_extended_info ( const pad_ensemble & binding )
{
const auto & pad = binding . pad ;
SDLDevice * dev = static_cast < SDLDevice * > ( binding . device . get ( ) ) ;
2025-01-24 08:27:08 +01:00
if ( ! dev | | ! dev - > sdl . gamepad | | ! pad )
2022-10-15 21:01:38 +02:00
return ;
2025-01-24 08:27:08 +01:00
if ( dev - > sdl . gamepad )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
const SDL_PowerState power_state = SDL_GetGamepadPowerInfo ( dev - > sdl . gamepad , & dev - > sdl . power_level ) ;
switch ( power_state )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
case SDL_PowerState : : SDL_POWERSTATE_ON_BATTERY :
pad - > m_battery_level = std : : clamp ( dev - > sdl . power_level , 0 , 100 ) ;
pad - > m_cable_state = 0 ;
break ;
case SDL_PowerState : : SDL_POWERSTATE_NO_BATTERY :
case SDL_PowerState : : SDL_POWERSTATE_CHARGING :
case SDL_PowerState : : SDL_POWERSTATE_CHARGED :
pad - > m_battery_level = std : : clamp ( dev - > sdl . power_level , 0 , 100 ) ;
pad - > m_cable_state = 1 ;
break ;
default :
pad - > m_battery_level = 0 ;
pad - > m_cable_state = 0 ;
break ;
2022-10-15 21:01:38 +02:00
}
}
else
{
pad - > m_cable_state = 1 ;
pad - > m_battery_level = 100 ;
}
if ( dev - > sdl . has_accel )
{
2025-01-24 08:27:08 +01:00
if ( ! SDL_GetGamepadSensorData ( dev - > sdl . gamepad , SDL_SENSOR_ACCEL , dev - > values_accel . data ( ) , 3 ) )
2022-10-15 21:01:38 +02:00
{
sdl_log . error ( " Could not get acceleration sensor data of device %d! SDL Error: %s " , dev - > player_id , SDL_GetError ( ) ) ;
}
else
{
2025-01-08 01:22:50 +01:00
const f32 accel_x = dev - > values_accel [ 0 ] ; // Angular speed around the x axis (pitch)
const f32 accel_y = dev - > values_accel [ 1 ] ; // Angular speed around the y axis (yaw)
const f32 accel_z = dev - > values_accel [ 2 ] ; // Angular speed around the z axis (roll
2022-10-15 21:01:38 +02:00
// Convert to ds3. The ds3 resolution is 113/G.
2025-01-08 01:22:50 +01:00
pad - > m_sensors [ 0 ] . m_value = Clamp0To1023 ( ( accel_x / SDL_STANDARD_GRAVITY ) * - 1 * MOTION_ONE_G + 512 ) ;
pad - > m_sensors [ 1 ] . m_value = Clamp0To1023 ( ( accel_y / SDL_STANDARD_GRAVITY ) * - 1 * MOTION_ONE_G + 512 ) ;
pad - > m_sensors [ 2 ] . m_value = Clamp0To1023 ( ( accel_z / SDL_STANDARD_GRAVITY ) * - 1 * MOTION_ONE_G + 512 ) ;
2022-10-15 21:01:38 +02:00
}
}
2023-02-13 11:33:06 +01:00
2022-10-15 21:01:38 +02:00
if ( dev - > sdl . has_gyro )
{
2025-01-24 08:27:08 +01:00
if ( ! SDL_GetGamepadSensorData ( dev - > sdl . gamepad , SDL_SENSOR_GYRO , dev - > values_gyro . data ( ) , 3 ) )
2022-10-15 21:01:38 +02:00
{
sdl_log . error ( " Could not get gyro sensor data of device %d! SDL Error: %s " , dev - > player_id , SDL_GetError ( ) ) ;
}
else
{
2025-04-05 21:50:45 +02:00
// const f32 gyro_x = dev->values_gyro[0]; // Angular speed around the x axis (pitch)
2025-01-08 01:22:50 +01:00
const f32 gyro_y = dev - > values_gyro [ 1 ] ; // Angular speed around the y axis (yaw)
2025-04-05 21:50:45 +02:00
// const f32 gyro_z = dev->values_gyro[2]; // Angular speed around the z axis (roll)
2022-10-15 21:01:38 +02:00
// Convert to ds3. The ds3 resolution is 123/90°/sec. The SDL gyro is measured in rad/sec.
2025-01-08 01:22:50 +01:00
const f32 degree = rad_to_degree ( gyro_y ) ;
2022-10-15 21:01:38 +02:00
pad - > m_sensors [ 3 ] . m_value = Clamp0To1023 ( degree * ( 123.f / 90.f ) + 512 ) ;
}
}
2025-01-08 01:22:50 +01:00
if ( dev - > sdl . has_accel | | dev - > sdl . has_gyro )
{
set_raw_orientation ( * pad ) ;
}
2022-10-15 21:01:38 +02:00
}
void sdl_pad_handler : : 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 ( ! m_is_init )
return ;
SDL_PumpEvents ( ) ;
PadHandlerBase : : get_motion_sensors ( pad_id , callback , fail_callback , preview_values , sensors ) ;
}
2024-08-09 20:39:56 +02:00
PadHandlerBase : : connection sdl_pad_handler : : get_next_button_press ( const std : : string & padId , const pad_callback & callback , const pad_fail_callback & fail_callback , gui_call_type call_type , const std : : vector < std : : string > & buttons )
2022-10-15 21:01:38 +02:00
{
if ( ! m_is_init )
return connection : : disconnected ;
SDL_PumpEvents ( ) ;
2024-08-09 20:39:56 +02:00
return PadHandlerBase : : get_next_button_press ( padId , callback , fail_callback , call_type , buttons ) ;
2022-10-15 21:01:38 +02:00
}
void sdl_pad_handler : : apply_pad_data ( const pad_ensemble & binding )
{
const auto & pad = binding . pad ;
SDLDevice * dev = static_cast < SDLDevice * > ( binding . device . get ( ) ) ;
if ( ! dev | | ! pad )
return ;
cfg_pad * cfg = dev - > config ;
// 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.
if ( dev - > sdl . has_rumble | | dev - > sdl . has_rumble_triggers )
{
2025-01-21 02:56:45 +01:00
const u8 speed_large = cfg - > get_large_motor_speed ( pad - > m_vibrateMotors ) ;
const u8 speed_small = cfg - > get_small_motor_speed ( pad - > m_vibrateMotors ) ;
2022-10-15 21:01:38 +02:00
2024-08-17 11:21:11 +02:00
dev - > new_output_data | = dev - > large_motor ! = speed_large | | dev - > small_motor ! = speed_small ;
2022-10-15 21:01:38 +02:00
dev - > large_motor = speed_large ;
dev - > small_motor = speed_small ;
2024-08-17 11:21:11 +02:00
const auto now = steady_clock : : now ( ) ;
const auto elapsed = now - dev - > last_output ;
2022-10-15 21:01:38 +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 )
2022-10-15 21:01:38 +02:00
{
set_rumble ( dev , speed_large , speed_small ) ;
2024-08-17 11:21:11 +02:00
dev - > new_output_data = false ;
dev - > last_output = now ;
2022-10-15 21:01:38 +02:00
}
}
2025-01-24 08:27:08 +01:00
if ( dev - > sdl . has_led | | dev - > sdl . has_mono_led )
2022-10-15 21:01:38 +02:00
{
// Use LEDs to indicate battery level
if ( cfg - > led_battery_indicator )
{
// This makes sure that the LED color doesn't update every 1ms. DS4 only reports battery level in 10% increments
if ( dev - > sdl . last_power_level ! = dev - > sdl . power_level )
{
const u32 combined_color = get_battery_color ( dev - > sdl . power_level , cfg - > led_battery_indicator_brightness ) ;
cfg - > colorR . set ( combined_color > > 8 ) ;
cfg - > colorG . set ( combined_color & 0xff ) ;
cfg - > colorB . set ( 0 ) ;
dev - > sdl . last_power_level = dev - > sdl . power_level ;
dev - > led_needs_update = true ;
}
}
// Blink LED when battery is low
if ( cfg - > led_low_battery_blink )
{
const bool low_battery = pad - > m_battery_level < = 20 ;
if ( dev - > led_is_blinking & & ! low_battery )
{
dev - > led_is_blinking = false ;
dev - > led_needs_update = true ;
}
else if ( ! dev - > led_is_blinking & & low_battery )
{
dev - > led_is_blinking = true ;
dev - > led_needs_update = true ;
}
if ( dev - > led_is_blinking )
{
if ( const steady_clock : : time_point now = steady_clock : : now ( ) ; ( now - dev - > led_timestamp ) > 500 ms )
{
dev - > led_is_on = ! dev - > led_is_on ;
dev - > led_timestamp = now ;
dev - > led_needs_update = true ;
}
}
}
else if ( ! dev - > led_is_on )
{
dev - > led_is_on = true ;
dev - > led_needs_update = true ;
}
if ( dev - > led_needs_update )
{
const u8 r = dev - > led_is_on ? cfg - > colorR : 0 ;
const u8 g = dev - > led_is_on ? cfg - > colorG : 0 ;
const u8 b = dev - > led_is_on ? cfg - > colorB : 0 ;
2025-01-24 08:27:08 +01:00
if ( ! SDL_SetGamepadLED ( dev - > sdl . gamepad , r , g , b ) )
2022-10-15 21:01:38 +02:00
{
sdl_log . error ( " Could not set LED of device %d! SDL Error: %s " , dev - > player_id , SDL_GetError ( ) ) ;
}
dev - > led_needs_update = false ;
}
}
2025-01-24 08:27:08 +01:00
if ( dev - > sdl . has_player_led & & ( dev - > update_player_leds | | dev - > enable_player_leds ! = cfg - > player_led_enabled . get ( ) ) )
{
dev - > enable_player_leds = cfg - > player_led_enabled . get ( ) ;
dev - > update_player_leds = false ;
if ( ! SDL_SetGamepadPlayerIndex ( dev - > sdl . gamepad , dev - > enable_player_leds ? dev - > player_id : - 1 ) )
{
sdl_log . error ( " Could not set player LED of device %d! SDL Error: %s " , dev - > player_id , SDL_GetError ( ) ) ;
}
}
2022-10-15 21:01:38 +02:00
}
void sdl_pad_handler : : set_rumble ( SDLDevice * dev , u8 speed_large , u8 speed_small )
{
2025-04-05 21:50:45 +02:00
if ( ! dev | | ! dev - > sdl . gamepad )
return ;
2022-10-15 21:01:38 +02:00
2024-08-17 11:21:11 +02:00
constexpr u32 rumble_duration_ms = static_cast < u32 > ( ( min_output_interval + 100 ms ) . count ( ) ) ; // Some number higher than the min_output_interval.
2022-10-15 21:01:38 +02:00
if ( dev - > sdl . has_rumble )
{
2025-01-24 08:27:08 +01:00
if ( ! SDL_RumbleGamepad ( dev - > sdl . gamepad , speed_large * 257 , speed_small * 257 , rumble_duration_ms ) )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
sdl_log . error ( " Unable to play game pad rumble of player %d! SDL Error: %s " , dev - > player_id , SDL_GetError ( ) ) ;
2022-10-15 21:01:38 +02:00
}
}
// NOTE: Disabled for now. The triggers do not seem to be implemented correctly in SDL in the current version.
// The rumble is way too intensive and has a high frequency. It is very displeasing at the moment.
// In the future we could use additional trigger rumble to enhance the existing rumble.
if ( false & & dev - > sdl . has_rumble_triggers )
{
// Only the large motor shall control both triggers. It wouldn't make sense to differentiate here.
2025-01-24 08:27:08 +01:00
if ( ! SDL_RumbleGamepadTriggers ( dev - > sdl . gamepad , speed_large * 257 , speed_large * 257 , rumble_duration_ms ) )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
sdl_log . error ( " Unable to play game pad trigger rumble of player %d! SDL Error: %s " , dev - > player_id , SDL_GetError ( ) ) ;
2022-10-15 21:01:38 +02:00
}
}
}
bool sdl_pad_handler : : get_is_left_trigger ( const std : : shared_ptr < PadDevice > & /*device*/ , u64 keyCode )
{
return keyCode = = SDLKeyCodes : : LT ;
}
bool sdl_pad_handler : : get_is_right_trigger ( const std : : shared_ptr < PadDevice > & /*device*/ , u64 keyCode )
{
return keyCode = = SDLKeyCodes : : RT ;
}
bool sdl_pad_handler : : get_is_left_stick ( const std : : shared_ptr < PadDevice > & /*device*/ , u64 keyCode )
{
switch ( keyCode )
{
case SDLKeyCodes : : LSXNeg :
case SDLKeyCodes : : LSXPos :
case SDLKeyCodes : : LSYPos :
case SDLKeyCodes : : LSYNeg :
return true ;
default :
return false ;
}
}
bool sdl_pad_handler : : get_is_right_stick ( const std : : shared_ptr < PadDevice > & /*device*/ , u64 keyCode )
{
switch ( keyCode )
{
case SDLKeyCodes : : RSXNeg :
case SDLKeyCodes : : RSXPos :
case SDLKeyCodes : : RSYPos :
case SDLKeyCodes : : RSYNeg :
return true ;
default :
return false ;
}
}
2024-07-30 22:51:17 +02:00
bool sdl_pad_handler : : get_is_touch_pad_motion ( const std : : shared_ptr < PadDevice > & /*device*/ , u64 keyCode )
{
switch ( keyCode )
{
case SDLKeyCodes : : Touch_L :
case SDLKeyCodes : : Touch_R :
case SDLKeyCodes : : Touch_U :
case SDLKeyCodes : : Touch_D :
return true ;
default :
return false ;
}
}
2022-10-15 21:01:38 +02:00
std : : unordered_map < u64 , u16 > sdl_pad_handler : : get_button_values ( const std : : shared_ptr < PadDevice > & device )
{
std : : unordered_map < u64 , u16 > values ;
SDLDevice * dev = static_cast < SDLDevice * > ( device . get ( ) ) ;
2025-01-24 08:27:08 +01:00
if ( ! dev | | ! dev - > sdl . gamepad )
2022-10-15 21:01:38 +02:00
return values ;
2025-01-24 08:27:08 +01:00
for ( SDL_GamepadButton button_id : dev - > sdl . button_ids )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
const u8 value = SDL_GetGamepadButton ( dev - > sdl . gamepad , button_id ) ;
2022-10-15 21:01:38 +02:00
const SDLKeyCodes key_code = get_button_code ( button_id ) ;
2023-02-03 18:03:19 +01:00
// TODO: SDL does not support DS3 button intensity in the current version
values [ key_code ] = value ? 255 : 0 ;
2022-10-15 21:01:38 +02:00
}
2025-01-24 08:27:08 +01:00
for ( SDL_GamepadAxis axis_id : dev - > sdl . axis_ids )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
const s16 value = SDL_GetGamepadAxis ( dev - > sdl . gamepad , axis_id ) ;
2022-10-15 21:01:38 +02:00
switch ( axis_id )
{
2025-01-24 08:27:08 +01:00
case SDL_GamepadAxis : : SDL_GAMEPAD_AXIS_LEFT_TRIGGER :
2022-10-15 21:01:38 +02:00
values [ SDLKeyCodes : : LT ] = std : : max < u16 > ( 0 , value ) ;
break ;
2025-01-24 08:27:08 +01:00
case SDL_GamepadAxis : : SDL_GAMEPAD_AXIS_RIGHT_TRIGGER :
2022-10-15 21:01:38 +02:00
values [ SDLKeyCodes : : RT ] = std : : max < u16 > ( 0 , value ) ;
break ;
2025-01-24 08:27:08 +01:00
case SDL_GamepadAxis : : SDL_GAMEPAD_AXIS_LEFTX :
2022-10-15 21:01:38 +02:00
values [ SDLKeyCodes : : LSXNeg ] = value < 0 ? std : : abs ( value ) - 1 : 0 ;
values [ SDLKeyCodes : : LSXPos ] = value > 0 ? value : 0 ;
break ;
2025-01-24 08:27:08 +01:00
case SDL_GamepadAxis : : SDL_GAMEPAD_AXIS_LEFTY :
2022-10-15 21:01:38 +02:00
values [ SDLKeyCodes : : LSYNeg ] = value > 0 ? value : 0 ;
values [ SDLKeyCodes : : LSYPos ] = value < 0 ? std : : abs ( value ) - 1 : 0 ;
break ;
2025-01-24 08:27:08 +01:00
case SDL_GamepadAxis : : SDL_GAMEPAD_AXIS_RIGHTX :
2022-10-15 21:01:38 +02:00
values [ SDLKeyCodes : : RSXNeg ] = value < 0 ? std : : abs ( value ) - 1 : 0 ;
values [ SDLKeyCodes : : RSXPos ] = value > 0 ? value : 0 ;
break ;
2025-01-24 08:27:08 +01:00
case SDL_GamepadAxis : : SDL_GAMEPAD_AXIS_RIGHTY :
2022-10-15 21:01:38 +02:00
values [ SDLKeyCodes : : RSYNeg ] = value > 0 ? value : 0 ;
values [ SDLKeyCodes : : RSYPos ] = value < 0 ? std : : abs ( value ) - 1 : 0 ;
break ;
default :
break ;
}
}
2024-07-30 22:51:17 +02:00
for ( const SDLDevice : : touchpad & touchpad : dev - > sdl . touchpads )
{
for ( const SDLDevice : : touch_point & finger : touchpad . fingers )
{
2025-04-05 21:50:45 +02:00
bool down = false ; // true means the finger is touching the pad
f32 x = 0.0f ; // 0 = left, 1 = right
f32 y = 0.0f ; // 0 = top, 1 = bottom
2024-07-30 22:51:17 +02:00
f32 pressure = 0.0f ; // In the current SDL version the pressure is always 1 if the state is 1
2025-01-24 08:27:08 +01:00
if ( ! SDL_GetGamepadTouchpadFinger ( dev - > sdl . gamepad , touchpad . index , finger . index , & down , & x , & y , & pressure ) )
2024-07-30 22:51:17 +02:00
{
sdl_log . error ( " Could not get touchpad %d finger %d data of device %d! SDL Error: %s " , touchpad . index , finger . index , dev - > player_id , SDL_GetError ( ) ) ;
}
else
{
2025-01-24 08:27:08 +01:00
sdl_log . trace ( " touchpad=%d, finger=%d, state=%d, x=%f, y=%f, pressure=%f " , touchpad . index , finger . index , down , x , y , pressure ) ;
2024-07-30 22:51:17 +02:00
2025-01-24 08:27:08 +01:00
if ( ! down )
2024-07-30 22:51:17 +02:00
{
continue ;
}
const f32 x_scaled = ScaledInput ( x , 0.0f , 1.0f , 0.0f , 255.0f ) ;
const f32 y_scaled = ScaledInput ( y , 0.0f , 1.0f , 0.0f , 255.0f ) ;
values [ SDLKeyCodes : : Touch_L ] = Clamp0To255 ( ( 127.5f - x_scaled ) * 2.0f ) ;
values [ SDLKeyCodes : : Touch_R ] = Clamp0To255 ( ( x_scaled - 127.5f ) * 2.0f ) ;
values [ SDLKeyCodes : : Touch_U ] = Clamp0To255 ( ( 127.5f - y_scaled ) * 2.0f ) ;
values [ SDLKeyCodes : : Touch_D ] = Clamp0To255 ( ( y_scaled - 127.5f ) * 2.0f ) ;
}
}
}
2022-10-15 21:01:38 +02:00
return values ;
}
pad_preview_values sdl_pad_handler : : get_preview_values ( const std : : unordered_map < u64 , u16 > & data )
{
return {
: : at32 ( data , LT ) ,
: : at32 ( data , RT ) ,
: : at32 ( data , LSXPos ) - : : at32 ( data , LSXNeg ) ,
: : at32 ( data , LSYPos ) - : : at32 ( data , LSYNeg ) ,
: : at32 ( data , RSXPos ) - : : at32 ( data , RSXNeg ) ,
2025-04-05 21:50:45 +02:00
: : at32 ( data , RSYPos ) - : : at32 ( data , RSYNeg ) } ;
2022-10-15 21:01:38 +02:00
}
2025-01-24 08:27:08 +01:00
std : : string sdl_pad_handler : : button_to_string ( SDL_GamepadButton button )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
if ( const char * name = SDL_GetGamepadStringForButton ( button ) )
2022-10-15 21:01:38 +02:00
{
return name ;
}
return { } ;
}
2025-01-24 08:27:08 +01:00
std : : string sdl_pad_handler : : axis_to_string ( SDL_GamepadAxis axis )
2022-10-15 21:01:38 +02:00
{
2025-01-24 08:27:08 +01:00
if ( const char * name = SDL_GetGamepadStringForAxis ( axis ) )
2022-10-15 21:01:38 +02:00
{
return name ;
}
return { } ;
}
2025-01-24 08:27:08 +01:00
sdl_pad_handler : : SDLKeyCodes sdl_pad_handler : : get_button_code ( SDL_GamepadButton button )
2022-10-15 21:01:38 +02:00
{
switch ( button )
{
default :
2025-01-24 08:27:08 +01:00
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_INVALID : return SDLKeyCodes : : None ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_SOUTH : return SDLKeyCodes : : South ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_EAST : return SDLKeyCodes : : East ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_WEST : return SDLKeyCodes : : West ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_NORTH : return SDLKeyCodes : : North ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_DPAD_LEFT : return SDLKeyCodes : : Left ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_DPAD_RIGHT : return SDLKeyCodes : : Right ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_DPAD_UP : return SDLKeyCodes : : Up ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_DPAD_DOWN : return SDLKeyCodes : : Down ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_LEFT_SHOULDER : return SDLKeyCodes : : LB ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER : return SDLKeyCodes : : RB ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_BACK : return SDLKeyCodes : : Back ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_START : return SDLKeyCodes : : Start ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_LEFT_STICK : return SDLKeyCodes : : LS ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_RIGHT_STICK : return SDLKeyCodes : : RS ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_GUIDE : return SDLKeyCodes : : Guide ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_MISC1 : return SDLKeyCodes : : Misc1 ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1 : return SDLKeyCodes : : RPaddle1 ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_LEFT_PADDLE1 : return SDLKeyCodes : : LPaddle1 ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2 : return SDLKeyCodes : : RPaddle2 ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_LEFT_PADDLE2 : return SDLKeyCodes : : LPaddle2 ;
case SDL_GamepadButton : : SDL_GAMEPAD_BUTTON_TOUCHPAD : return SDLKeyCodes : : Touchpad ;
2022-10-15 21:01:38 +02:00
}
}
# endif