2015-06-21 09:33:46 -06:00
/*
2024-01-01 14:15:26 -07:00
Copyright ( C ) 1997 - 2024 Sam Lantinga < slouken @ libsdl . org >
2015-06-21 09:33:46 -06:00
2023-06-10 09:43:43 -06:00
This software is provided ' as - is ' , without any express or implied
warranty . In no event will the authors be held liable for any damages
arising from the use of this software .
2015-06-21 09:33:46 -06:00
2023-06-10 09:43:43 -06:00
Permission is granted to anyone to use this software for any purpose ,
including commercial applications , and to alter it and redistribute it
freely .
2023-06-10 09:39:20 -06:00
*/
2015-06-21 09:33:46 -06:00
2023-07-11 15:21:35 -06:00
/* Simple program to test the SDL controller routines */
2023-06-10 09:43:43 -06:00
# include <SDL3/SDL.h>
# include <SDL3/SDL_main.h>
# include <SDL3/SDL_test.h>
2023-07-10 20:13:51 -06:00
# include <SDL3/SDL_test_font.h>
2023-07-09 20:22:29 -06:00
2024-01-23 18:40:51 -07:00
# ifdef SDL_PLATFORM_EMSCRIPTEN
2015-06-21 09:33:46 -06:00
# include <emscripten/emscripten.h>
# endif
2024-01-23 18:40:51 -07:00
# include "gamepadutils.h"
# include "testutils.h"
2023-07-15 01:00:53 -06:00
#if 0
# define DEBUG_AXIS_MAPPING
# endif
2023-07-10 20:13:51 -06:00
# define TITLE_HEIGHT 48
2023-07-10 18:06:42 -06:00
# define PANEL_SPACING 25
# define PANEL_WIDTH 250
2023-07-13 23:57:32 -06:00
# define MINIMUM_BUTTON_WIDTH 96
# define BUTTON_MARGIN 16
2023-07-11 11:04:25 -06:00
# define BUTTON_PADDING 12
2023-07-10 18:06:42 -06:00
# define GAMEPAD_WIDTH 512
2024-02-01 21:15:01 -07:00
# define GAMEPAD_HEIGHT 560
2023-07-10 18:06:42 -06:00
# define SCREEN_WIDTH (PANEL_WIDTH + PANEL_SPACING + GAMEPAD_WIDTH + PANEL_SPACING + PANEL_WIDTH)
# define SCREEN_HEIGHT (TITLE_HEIGHT + GAMEPAD_HEIGHT)
2023-06-10 09:43:43 -06:00
2023-07-15 01:00:53 -06:00
typedef struct
{
SDL_bool m_bMoving ;
int m_nLastValue ;
int m_nStartingValue ;
int m_nFarthestValue ;
} AxisState ;
2023-07-11 15:21:35 -06:00
typedef struct
2023-07-11 14:06:28 -06:00
{
SDL_JoystickID id ;
2023-07-15 01:00:53 -06:00
2023-07-11 14:06:28 -06:00
SDL_Joystick * joystick ;
2023-07-15 01:00:53 -06:00
int num_axes ;
AxisState * axis_state ;
2023-07-11 14:06:28 -06:00
SDL_Gamepad * gamepad ;
2023-07-15 01:00:53 -06:00
char * mapping ;
SDL_bool has_bindings ;
2023-07-11 14:06:28 -06:00
int trigger_effect ;
} Controller ;
2023-06-10 09:43:43 -06:00
static SDL_Window * window = NULL ;
static SDL_Renderer * screen = NULL ;
2023-07-13 23:57:32 -06:00
static ControllerDisplayMode display_mode = CONTROLLER_MODE_TESTING ;
2023-07-09 20:22:29 -06:00
static GamepadImage * image = NULL ;
2023-07-10 18:06:42 -06:00
static GamepadDisplay * gamepad_elements = NULL ;
2023-07-17 13:14:37 -06:00
static GamepadTypeDisplay * gamepad_type = NULL ;
2023-07-10 18:06:42 -06:00
static JoystickDisplay * joystick_elements = NULL ;
2023-07-13 23:57:32 -06:00
static GamepadButton * setup_mapping_button = NULL ;
2023-07-15 01:00:53 -06:00
static GamepadButton * done_mapping_button = NULL ;
2023-07-13 23:57:32 -06:00
static GamepadButton * cancel_button = NULL ;
static GamepadButton * clear_button = NULL ;
2023-07-11 11:04:25 -06:00
static GamepadButton * copy_button = NULL ;
2023-07-13 23:57:32 -06:00
static GamepadButton * paste_button = NULL ;
static char * backup_mapping = NULL ;
2023-06-10 09:43:43 -06:00
static SDL_bool done = SDL_FALSE ;
static SDL_bool set_LED = SDL_FALSE ;
2023-07-11 14:06:28 -06:00
static int num_controllers = 0 ;
static Controller * controllers ;
static Controller * controller ;
2023-07-15 01:00:53 -06:00
static SDL_JoystickID mapping_controller = 0 ;
static int binding_element = SDL_GAMEPAD_ELEMENT_INVALID ;
2023-07-16 13:14:52 -06:00
static int last_binding_element = SDL_GAMEPAD_ELEMENT_INVALID ;
static SDL_bool binding_flow = SDL_FALSE ;
2023-11-16 00:46:31 -07:00
static int binding_flow_direction = 0 ;
2023-07-15 01:00:53 -06:00
static Uint64 binding_advance_time = 0 ;
2023-07-16 16:11:24 -06:00
static SDL_FRect title_area ;
static SDL_bool title_highlighted ;
static SDL_bool title_pressed ;
2023-07-17 13:14:37 -06:00
static SDL_FRect type_area ;
static SDL_bool type_highlighted ;
static SDL_bool type_pressed ;
2023-07-16 16:11:24 -06:00
static char * controller_name ;
2023-06-10 09:43:43 -06:00
static SDL_Joystick * virtual_joystick = NULL ;
static SDL_GamepadAxis virtual_axis_active = SDL_GAMEPAD_AXIS_INVALID ;
static float virtual_axis_start_x ;
static float virtual_axis_start_y ;
static SDL_GamepadButton virtual_button_active = SDL_GAMEPAD_BUTTON_INVALID ;
2022-05-16 08:21:28 -06:00
2023-07-16 13:14:52 -06:00
static int s_arrBindingOrder [ ] = {
/* Standard sequence */
2023-11-06 14:07:12 -07:00
SDL_GAMEPAD_BUTTON_SOUTH ,
SDL_GAMEPAD_BUTTON_EAST ,
SDL_GAMEPAD_BUTTON_WEST ,
SDL_GAMEPAD_BUTTON_NORTH ,
2023-07-18 23:04:55 -06:00
SDL_GAMEPAD_BUTTON_DPAD_LEFT ,
SDL_GAMEPAD_BUTTON_DPAD_RIGHT ,
SDL_GAMEPAD_BUTTON_DPAD_UP ,
SDL_GAMEPAD_BUTTON_DPAD_DOWN ,
2023-07-16 13:14:52 -06:00
SDL_GAMEPAD_ELEMENT_AXIS_LEFTX_NEGATIVE ,
SDL_GAMEPAD_ELEMENT_AXIS_LEFTX_POSITIVE ,
SDL_GAMEPAD_ELEMENT_AXIS_LEFTY_NEGATIVE ,
SDL_GAMEPAD_ELEMENT_AXIS_LEFTY_POSITIVE ,
SDL_GAMEPAD_BUTTON_LEFT_STICK ,
SDL_GAMEPAD_ELEMENT_AXIS_RIGHTX_NEGATIVE ,
SDL_GAMEPAD_ELEMENT_AXIS_RIGHTX_POSITIVE ,
SDL_GAMEPAD_ELEMENT_AXIS_RIGHTY_NEGATIVE ,
SDL_GAMEPAD_ELEMENT_AXIS_RIGHTY_POSITIVE ,
SDL_GAMEPAD_BUTTON_RIGHT_STICK ,
SDL_GAMEPAD_BUTTON_LEFT_SHOULDER ,
SDL_GAMEPAD_ELEMENT_AXIS_LEFT_TRIGGER ,
SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER ,
SDL_GAMEPAD_ELEMENT_AXIS_RIGHT_TRIGGER ,
SDL_GAMEPAD_BUTTON_BACK ,
SDL_GAMEPAD_BUTTON_START ,
SDL_GAMEPAD_BUTTON_GUIDE ,
SDL_GAMEPAD_BUTTON_MISC1 ,
SDL_GAMEPAD_ELEMENT_INVALID ,
/* Paddle sequence */
2023-08-09 11:09:42 -06:00
SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1 ,
SDL_GAMEPAD_BUTTON_LEFT_PADDLE1 ,
SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2 ,
SDL_GAMEPAD_BUTTON_LEFT_PADDLE2 ,
2023-07-16 13:14:52 -06:00
SDL_GAMEPAD_ELEMENT_INVALID ,
} ;
2022-05-16 08:21:28 -06:00
2023-06-10 09:43:43 -06:00
static const char * GetSensorName ( SDL_SensorType sensor )
2022-05-15 21:01:12 -06:00
{
2023-06-10 09:43:43 -06:00
switch ( sensor ) {
case SDL_SENSOR_ACCEL :
return " accelerometer " ;
case SDL_SENSOR_GYRO :
return " gyro " ;
case SDL_SENSOR_ACCEL_L :
return " accelerometer (L) " ;
case SDL_SENSOR_GYRO_L :
return " gyro (L) " ;
case SDL_SENSOR_ACCEL_R :
return " accelerometer (R) " ;
case SDL_SENSOR_GYRO_R :
return " gyro (R) " ;
default :
return " UNKNOWN " ;
}
}
2022-05-15 21:01:12 -06:00
2023-07-11 14:06:28 -06:00
/* PS5 trigger effect documentation:
https : //controllers.fandom.com/wiki/Sony_DualSense#FFB_Trigger_Modes
*/
typedef struct
{
Uint8 ucEnableBits1 ; /* 0 */
Uint8 ucEnableBits2 ; /* 1 */
Uint8 ucRumbleRight ; /* 2 */
Uint8 ucRumbleLeft ; /* 3 */
Uint8 ucHeadphoneVolume ; /* 4 */
Uint8 ucSpeakerVolume ; /* 5 */
Uint8 ucMicrophoneVolume ; /* 6 */
Uint8 ucAudioEnableBits ; /* 7 */
Uint8 ucMicLightMode ; /* 8 */
Uint8 ucAudioMuteBits ; /* 9 */
Uint8 rgucRightTriggerEffect [ 11 ] ; /* 10 */
Uint8 rgucLeftTriggerEffect [ 11 ] ; /* 21 */
Uint8 rgucUnknown1 [ 6 ] ; /* 32 */
Uint8 ucLedFlags ; /* 38 */
Uint8 rgucUnknown2 [ 2 ] ; /* 39 */
Uint8 ucLedAnim ; /* 41 */
Uint8 ucLedBrightness ; /* 42 */
Uint8 ucPadLights ; /* 43 */
Uint8 ucLedRed ; /* 44 */
Uint8 ucLedGreen ; /* 45 */
Uint8 ucLedBlue ; /* 46 */
} DS5EffectsState_t ;
static void CyclePS5TriggerEffect ( Controller * device )
{
DS5EffectsState_t state ;
Uint8 effects [ 3 ] [ 11 ] = {
/* Clear trigger effect */
{ 0x05 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ,
/* Constant resistance across entire trigger pull */
{ 0x01 , 0 , 110 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ,
/* Resistance and vibration when trigger is pulled */
{ 0x06 , 15 , 63 , 128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ,
} ;
device - > trigger_effect = ( device - > trigger_effect + 1 ) % SDL_arraysize ( effects ) ;
SDL_zero ( state ) ;
state . ucEnableBits1 | = ( 0x04 | 0x08 ) ; /* Modify right and left trigger effect respectively */
SDL_memcpy ( state . rgucRightTriggerEffect , effects [ device - > trigger_effect ] , sizeof ( effects [ 0 ] ) ) ;
SDL_memcpy ( state . rgucLeftTriggerEffect , effects [ device - > trigger_effect ] , sizeof ( effects [ 0 ] ) ) ;
SDL_SendGamepadEffect ( device - > gamepad , & state , sizeof ( state ) ) ;
}
2023-07-16 13:14:52 -06:00
static void ClearButtonHighlights ( void )
2023-07-13 23:57:32 -06:00
{
2023-07-16 16:11:24 -06:00
title_highlighted = SDL_FALSE ;
title_pressed = SDL_FALSE ;
2023-07-17 13:14:37 -06:00
type_highlighted = SDL_FALSE ;
type_pressed = SDL_FALSE ;
2023-07-15 01:00:53 -06:00
ClearGamepadImage ( image ) ;
SetGamepadDisplayHighlight ( gamepad_elements , SDL_GAMEPAD_ELEMENT_INVALID , SDL_FALSE ) ;
2023-07-17 13:14:37 -06:00
SetGamepadTypeDisplayHighlight ( gamepad_type , SDL_GAMEPAD_TYPE_UNSELECTED , SDL_FALSE ) ;
2023-07-15 01:00:53 -06:00
SetGamepadButtonHighlight ( setup_mapping_button , SDL_FALSE , SDL_FALSE ) ;
SetGamepadButtonHighlight ( done_mapping_button , SDL_FALSE , SDL_FALSE ) ;
SetGamepadButtonHighlight ( cancel_button , SDL_FALSE , SDL_FALSE ) ;
SetGamepadButtonHighlight ( clear_button , SDL_FALSE , SDL_FALSE ) ;
SetGamepadButtonHighlight ( copy_button , SDL_FALSE , SDL_FALSE ) ;
SetGamepadButtonHighlight ( paste_button , SDL_FALSE , SDL_FALSE ) ;
2023-07-13 23:57:32 -06:00
}
2023-07-15 01:00:53 -06:00
static void UpdateButtonHighlights ( float x , float y , SDL_bool button_down )
2023-07-13 23:57:32 -06:00
{
ClearButtonHighlights ( ) ;
if ( display_mode = = CONTROLLER_MODE_TESTING ) {
2023-07-15 01:00:53 -06:00
SetGamepadButtonHighlight ( setup_mapping_button , GamepadButtonContains ( setup_mapping_button , x , y ) , button_down ) ;
2023-07-13 23:57:32 -06:00
} else if ( display_mode = = CONTROLLER_MODE_BINDING ) {
2023-07-16 16:11:24 -06:00
SDL_FPoint point ;
2023-07-16 13:39:05 -06:00
int gamepad_highlight_element = SDL_GAMEPAD_ELEMENT_INVALID ;
2023-07-15 01:00:53 -06:00
char * joystick_highlight_element ;
2023-07-16 16:11:24 -06:00
point . x = x ;
point . y = y ;
if ( SDL_PointInRectFloat ( & point , & title_area ) ) {
title_highlighted = SDL_TRUE ;
title_pressed = button_down ;
} else {
title_highlighted = SDL_FALSE ;
title_pressed = SDL_FALSE ;
}
2023-07-17 13:14:37 -06:00
if ( SDL_PointInRectFloat ( & point , & type_area ) ) {
type_highlighted = SDL_TRUE ;
type_pressed = button_down ;
} else {
type_highlighted = SDL_FALSE ;
type_pressed = SDL_FALSE ;
}
2023-07-16 13:39:05 -06:00
if ( controller - > joystick ! = virtual_joystick ) {
gamepad_highlight_element = GetGamepadImageElementAt ( image , x , y ) ;
}
if ( gamepad_highlight_element = = SDL_GAMEPAD_ELEMENT_INVALID ) {
gamepad_highlight_element = GetGamepadDisplayElementAt ( gamepad_elements , controller - > gamepad , x , y ) ;
}
2023-07-15 01:00:53 -06:00
SetGamepadDisplayHighlight ( gamepad_elements , gamepad_highlight_element , button_down ) ;
2023-07-17 13:14:37 -06:00
if ( binding_element = = SDL_GAMEPAD_ELEMENT_TYPE ) {
int gamepad_highlight_type = GetGamepadTypeDisplayAt ( gamepad_type , x , y ) ;
SetGamepadTypeDisplayHighlight ( gamepad_type , gamepad_highlight_type , button_down ) ;
}
2023-07-15 01:00:53 -06:00
joystick_highlight_element = GetJoystickDisplayElementAt ( joystick_elements , controller - > joystick , x , y ) ;
SetJoystickDisplayHighlight ( joystick_elements , joystick_highlight_element , button_down ) ;
SDL_free ( joystick_highlight_element ) ;
SetGamepadButtonHighlight ( done_mapping_button , GamepadButtonContains ( done_mapping_button , x , y ) , button_down ) ;
SetGamepadButtonHighlight ( cancel_button , GamepadButtonContains ( cancel_button , x , y ) , button_down ) ;
SetGamepadButtonHighlight ( clear_button , GamepadButtonContains ( clear_button , x , y ) , button_down ) ;
SetGamepadButtonHighlight ( copy_button , GamepadButtonContains ( copy_button , x , y ) , button_down ) ;
SetGamepadButtonHighlight ( paste_button , GamepadButtonContains ( paste_button , x , y ) , button_down ) ;
}
}
static int StandardizeAxisValue ( int nValue )
{
if ( nValue > SDL_JOYSTICK_AXIS_MAX / 2 ) {
return SDL_JOYSTICK_AXIS_MAX ;
} else if ( nValue < SDL_JOYSTICK_AXIS_MIN / 2 ) {
return SDL_JOYSTICK_AXIS_MIN ;
} else {
return 0 ;
}
}
2023-07-16 16:11:24 -06:00
static void RefreshControllerName ( void )
{
2023-07-17 18:35:38 -06:00
const char * name = NULL ;
2023-07-16 16:11:24 -06:00
SDL_free ( controller_name ) ;
controller_name = NULL ;
if ( controller ) {
if ( controller - > gamepad ) {
2023-07-17 18:35:38 -06:00
name = SDL_GetGamepadName ( controller - > gamepad ) ;
2023-07-16 16:11:24 -06:00
} else {
2023-07-17 18:35:38 -06:00
name = SDL_GetJoystickName ( controller - > joystick ) ;
2023-07-16 16:11:24 -06:00
}
}
2023-07-17 18:35:38 -06:00
SDL_free ( controller_name ) ;
if ( name ) {
controller_name = SDL_strdup ( name ) ;
} else {
controller_name = SDL_strdup ( " " ) ;
}
2023-07-16 16:11:24 -06:00
}
2023-07-16 10:16:16 -06:00
static void SetAndFreeGamepadMapping ( char * mapping )
2023-07-15 01:00:53 -06:00
{
SDL_SetGamepadMapping ( controller - > id , mapping ) ;
2023-07-16 10:16:16 -06:00
SDL_free ( mapping ) ;
2023-07-15 01:00:53 -06:00
}
2023-07-16 13:14:52 -06:00
static void SetCurrentBindingElement ( int element , SDL_bool flow )
2023-07-15 01:00:53 -06:00
{
int i ;
2023-07-16 16:11:24 -06:00
if ( binding_element = = SDL_GAMEPAD_ELEMENT_NAME ) {
RefreshControllerName ( ) ;
}
2023-07-16 13:14:52 -06:00
if ( element = = SDL_GAMEPAD_ELEMENT_INVALID ) {
2023-11-16 00:46:31 -07:00
binding_flow_direction = 0 ;
2023-07-16 13:14:52 -06:00
last_binding_element = SDL_GAMEPAD_ELEMENT_INVALID ;
} else {
last_binding_element = binding_element ;
}
2023-07-15 01:00:53 -06:00
binding_element = element ;
2023-11-06 14:07:12 -07:00
binding_flow = flow | | ( element = = SDL_GAMEPAD_BUTTON_SOUTH ) ;
2023-07-15 01:00:53 -06:00
binding_advance_time = 0 ;
for ( i = 0 ; i < controller - > num_axes ; + + i ) {
controller - > axis_state [ i ] . m_nFarthestValue = controller - > axis_state [ i ] . m_nStartingValue ;
2023-07-13 23:57:32 -06:00
}
2023-07-15 01:00:53 -06:00
SetGamepadDisplaySelected ( gamepad_elements , element ) ;
}
2023-07-16 13:14:52 -06:00
static void SetNextBindingElement ( void )
2023-07-15 01:00:53 -06:00
{
int i ;
if ( binding_element = = SDL_GAMEPAD_ELEMENT_INVALID ) {
return ;
}
for ( i = 0 ; i < SDL_arraysize ( s_arrBindingOrder ) ; + + i ) {
if ( binding_element = = s_arrBindingOrder [ i ] ) {
2023-11-16 00:46:31 -07:00
binding_flow_direction = 1 ;
2023-07-16 13:14:52 -06:00
SetCurrentBindingElement ( s_arrBindingOrder [ i + 1 ] , SDL_TRUE ) ;
2023-07-15 01:00:53 -06:00
return ;
}
}
2023-07-16 13:14:52 -06:00
SetCurrentBindingElement ( SDL_GAMEPAD_ELEMENT_INVALID , SDL_FALSE ) ;
2023-07-15 01:00:53 -06:00
}
2023-07-16 13:14:52 -06:00
static void SetPrevBindingElement ( void )
2023-07-15 01:00:53 -06:00
{
2023-07-16 13:14:52 -06:00
int i ;
if ( binding_element = = SDL_GAMEPAD_ELEMENT_INVALID ) {
return ;
}
for ( i = 1 ; i < SDL_arraysize ( s_arrBindingOrder ) ; + + i ) {
if ( binding_element = = s_arrBindingOrder [ i ] ) {
2023-11-16 00:46:31 -07:00
binding_flow_direction = - 1 ;
2023-07-16 13:14:52 -06:00
SetCurrentBindingElement ( s_arrBindingOrder [ i - 1 ] , SDL_TRUE ) ;
return ;
}
}
SetCurrentBindingElement ( SDL_GAMEPAD_ELEMENT_INVALID , SDL_FALSE ) ;
}
static void StopBinding ( void )
{
SetCurrentBindingElement ( SDL_GAMEPAD_ELEMENT_INVALID , SDL_FALSE ) ;
2023-07-15 01:00:53 -06:00
}
2023-07-19 10:48:32 -06:00
typedef struct
{
int axis ;
int direction ;
} AxisInfo ;
static SDL_bool ParseAxisInfo ( const char * description , AxisInfo * info )
{
if ( ! description ) {
return SDL_FALSE ;
}
if ( * description = = ' - ' ) {
info - > direction = - 1 ;
+ + description ;
} else if ( * description = = ' + ' ) {
info - > direction = 1 ;
+ + description ;
} else {
info - > direction = 0 ;
}
if ( description [ 0 ] = = ' a ' & & SDL_isdigit ( description [ 1 ] ) ) {
+ + description ;
info - > axis = SDL_atoi ( description ) ;
return SDL_TRUE ;
}
return SDL_FALSE ;
}
2023-07-15 01:00:53 -06:00
static void CommitBindingElement ( const char * binding , SDL_bool force )
{
char * mapping ;
2023-07-16 13:14:52 -06:00
int direction = 1 ;
SDL_bool ignore_binding = SDL_FALSE ;
2023-07-15 01:00:53 -06:00
if ( binding_element = = SDL_GAMEPAD_ELEMENT_INVALID ) {
return ;
}
2023-07-16 10:16:16 -06:00
if ( controller - > mapping ) {
mapping = SDL_strdup ( controller - > mapping ) ;
} else {
mapping = NULL ;
}
2023-07-15 01:00:53 -06:00
/* If the controller generates multiple events for a single element, pick the best one */
if ( ! force & & binding_advance_time ) {
char * current = GetElementBinding ( mapping , binding_element ) ;
SDL_bool native_button = ( binding_element < SDL_GAMEPAD_BUTTON_MAX ) ;
SDL_bool native_axis = ( binding_element > = SDL_GAMEPAD_BUTTON_MAX & &
binding_element < = SDL_GAMEPAD_ELEMENT_AXIS_MAX ) ;
SDL_bool native_trigger = ( binding_element = = SDL_GAMEPAD_ELEMENT_AXIS_LEFT_TRIGGER | |
binding_element = = SDL_GAMEPAD_ELEMENT_AXIS_RIGHT_TRIGGER ) ;
SDL_bool native_dpad = ( binding_element = = SDL_GAMEPAD_BUTTON_DPAD_UP | |
binding_element = = SDL_GAMEPAD_BUTTON_DPAD_DOWN | |
binding_element = = SDL_GAMEPAD_BUTTON_DPAD_LEFT | |
binding_element = = SDL_GAMEPAD_BUTTON_DPAD_RIGHT ) ;
if ( native_button ) {
SDL_bool current_button = ( current & & * current = = ' b ' ) ;
SDL_bool proposed_button = ( binding & & * binding = = ' b ' ) ;
if ( current_button & & ! proposed_button ) {
ignore_binding = SDL_TRUE ;
}
2023-07-19 10:48:32 -06:00
/* Use the lower index button (we map from lower to higher button index) */
if ( current_button & & proposed_button & & current [ 1 ] < binding [ 1 ] ) {
ignore_binding = SDL_TRUE ;
}
2023-07-15 01:00:53 -06:00
}
if ( native_axis ) {
2024-01-03 04:40:20 -07:00
AxisInfo current_axis_info = { 0 , 0 } ;
AxisInfo proposed_axis_info = { 0 , 0 } ;
2023-07-19 10:48:32 -06:00
SDL_bool current_axis = ParseAxisInfo ( current , & current_axis_info ) ;
SDL_bool proposed_axis = ParseAxisInfo ( binding , & proposed_axis_info ) ;
2023-07-15 01:00:53 -06:00
if ( current_axis ) {
/* Ignore this unless the proposed binding extends the existing axis */
ignore_binding = SDL_TRUE ;
if ( native_trigger & &
( ( * current = = ' - ' & & * binding = = ' + ' & &
SDL_strcmp ( current + 1 , binding + 1 ) = = 0 ) | |
( * current = = ' + ' & & * binding = = ' - ' & &
SDL_strcmp ( current + 1 , binding + 1 ) = = 0 ) ) ) {
/* Merge two half axes into a whole axis for a trigger */
+ + binding ;
ignore_binding = SDL_FALSE ;
}
2023-07-19 10:48:32 -06:00
/* Use the lower index axis (we map from lower to higher axis index) */
if ( proposed_axis & & proposed_axis_info . axis < current_axis_info . axis ) {
ignore_binding = SDL_FALSE ;
}
2023-07-15 01:00:53 -06:00
}
}
if ( native_dpad ) {
2023-07-19 10:48:32 -06:00
SDL_bool current_hat = ( current & & * current = = ' h ' ) ;
SDL_bool proposed_hat = ( binding & & * binding = = ' h ' ) ;
if ( current_hat & & ! proposed_hat ) {
ignore_binding = SDL_TRUE ;
}
/* Use the lower index hat (we map from lower to higher hat index) */
if ( current_hat & & proposed_hat & & current [ 1 ] < binding [ 1 ] ) {
2023-07-15 01:00:53 -06:00
ignore_binding = SDL_TRUE ;
}
}
SDL_free ( current ) ;
2023-07-16 13:14:52 -06:00
}
2023-07-15 01:00:53 -06:00
2023-07-16 13:14:52 -06:00
if ( ! ignore_binding & & binding_flow & & ! force ) {
int existing = GetElementForBinding ( mapping , binding ) ;
if ( existing ! = SDL_GAMEPAD_ELEMENT_INVALID ) {
2023-11-06 14:07:12 -07:00
SDL_GamepadButton action_forward = SDL_GAMEPAD_BUTTON_SOUTH ;
SDL_GamepadButton action_backward = SDL_GAMEPAD_BUTTON_EAST ;
2023-11-16 00:46:31 -07:00
SDL_GamepadButton action_delete = SDL_GAMEPAD_BUTTON_WEST ;
if ( binding_element = = action_forward ) {
/* Bind it! */
} else if ( binding_element = = action_backward ) {
if ( existing = = action_forward ) {
SDL_bool bound_backward = MappingHasElement ( controller - > mapping , action_backward ) ;
if ( bound_backward ) {
/* Just move on to the next one */
ignore_binding = SDL_TRUE ;
SetNextBindingElement ( ) ;
} else {
/* You can't skip the backward action, go back and start over */
ignore_binding = SDL_TRUE ;
SetPrevBindingElement ( ) ;
}
} else if ( existing = = action_backward & & binding_flow_direction = = - 1 ) {
/* Keep going backwards */
2023-07-16 13:14:52 -06:00
ignore_binding = SDL_TRUE ;
2023-11-16 00:46:31 -07:00
SetPrevBindingElement ( ) ;
2023-07-16 13:14:52 -06:00
} else {
2023-11-16 00:46:31 -07:00
/* Bind it! */
2023-07-16 13:14:52 -06:00
}
2023-11-16 00:46:31 -07:00
} else if ( existing = = action_forward ) {
/* Just move on to the next one */
ignore_binding = SDL_TRUE ;
SetNextBindingElement ( ) ;
2023-11-06 14:07:12 -07:00
} else if ( existing = = action_backward ) {
2023-11-16 00:46:31 -07:00
ignore_binding = SDL_TRUE ;
SetPrevBindingElement ( ) ;
2023-07-16 13:14:52 -06:00
} else if ( existing = = binding_element ) {
/* We're rebinding the same thing, just move to the next one */
ignore_binding = SDL_TRUE ;
SetNextBindingElement ( ) ;
2023-11-16 00:46:31 -07:00
} else if ( existing = = action_delete ) {
/* Clear the current binding and move to the next one */
binding = NULL ;
direction = 1 ;
force = SDL_TRUE ;
2023-11-06 14:07:12 -07:00
} else if ( binding_element ! = action_forward & &
binding_element ! = action_backward ) {
2023-11-16 00:46:31 -07:00
/* Actually, we'll just clear the existing binding */
/*ignore_binding = SDL_TRUE;*/
2023-07-16 13:14:52 -06:00
}
2023-07-15 01:00:53 -06:00
}
}
2023-07-16 13:14:52 -06:00
if ( ignore_binding ) {
SDL_free ( mapping ) ;
return ;
}
2023-07-15 01:00:53 -06:00
mapping = ClearMappingBinding ( mapping , binding ) ;
mapping = SetElementBinding ( mapping , binding_element , binding ) ;
2023-07-16 10:16:16 -06:00
SetAndFreeGamepadMapping ( mapping ) ;
2023-07-15 01:00:53 -06:00
if ( force ) {
2023-07-16 13:14:52 -06:00
if ( binding_flow ) {
if ( direction > 0 ) {
SetNextBindingElement ( ) ;
} else if ( direction < 0 ) {
SetPrevBindingElement ( ) ;
}
} else {
StopBinding ( ) ;
}
2023-07-15 01:00:53 -06:00
} else {
/* Wait to see if any more bindings come in */
binding_advance_time = SDL_GetTicks ( ) + 30 ;
}
}
static void ClearBinding ( void )
{
CommitBindingElement ( NULL , SDL_TRUE ) ;
2023-07-13 23:57:32 -06:00
}
static void SetDisplayMode ( ControllerDisplayMode mode )
{
float x , y ;
2023-07-15 01:00:53 -06:00
Uint32 button_state ;
2023-07-13 23:57:32 -06:00
if ( mode = = CONTROLLER_MODE_BINDING ) {
/* Make a backup of the current mapping */
2023-07-15 01:00:53 -06:00
if ( controller - > mapping ) {
backup_mapping = SDL_strdup ( controller - > mapping ) ;
}
mapping_controller = controller - > id ;
if ( MappingHasBindings ( backup_mapping ) ) {
2023-07-16 13:14:52 -06:00
SetCurrentBindingElement ( SDL_GAMEPAD_ELEMENT_INVALID , SDL_FALSE ) ;
2023-07-15 01:00:53 -06:00
} else {
2023-11-06 14:07:12 -07:00
SetCurrentBindingElement ( SDL_GAMEPAD_BUTTON_SOUTH , SDL_TRUE ) ;
2023-07-15 01:00:53 -06:00
}
} else {
if ( backup_mapping ) {
SDL_free ( backup_mapping ) ;
backup_mapping = NULL ;
}
mapping_controller = 0 ;
2023-07-16 13:14:52 -06:00
StopBinding ( ) ;
2023-07-13 23:57:32 -06:00
}
display_mode = mode ;
SetGamepadImageDisplayMode ( image , mode ) ;
SetGamepadDisplayDisplayMode ( gamepad_elements , mode ) ;
2023-07-15 01:00:53 -06:00
button_state = SDL_GetMouseState ( & x , & y ) ;
2023-07-13 23:57:32 -06:00
SDL_RenderCoordinatesFromWindow ( screen , x , y , & x , & y ) ;
2023-07-15 01:00:53 -06:00
UpdateButtonHighlights ( x , y , button_state ? SDL_TRUE : SDL_FALSE ) ;
2023-07-13 23:57:32 -06:00
}
static void CancelMapping ( void )
{
2023-07-16 10:16:16 -06:00
SetAndFreeGamepadMapping ( backup_mapping ) ;
backup_mapping = NULL ;
2023-07-13 23:57:32 -06:00
SetDisplayMode ( CONTROLLER_MODE_TESTING ) ;
}
static void ClearMapping ( void )
{
2023-07-16 10:16:16 -06:00
SetAndFreeGamepadMapping ( NULL ) ;
2023-07-16 13:14:52 -06:00
SetCurrentBindingElement ( SDL_GAMEPAD_ELEMENT_INVALID , SDL_FALSE ) ;
2023-07-13 23:57:32 -06:00
}
static void CopyMapping ( void )
{
2023-07-15 01:00:53 -06:00
if ( controller & & controller - > mapping ) {
2023-07-16 10:16:16 -06:00
SDL_SetClipboardText ( controller - > mapping ) ;
2023-07-13 23:57:32 -06:00
}
}
static void PasteMapping ( void )
{
if ( controller ) {
char * mapping = SDL_GetClipboardText ( ) ;
2023-07-15 01:00:53 -06:00
if ( MappingHasBindings ( mapping ) ) {
2023-07-16 13:14:52 -06:00
StopBinding ( ) ;
2023-07-16 10:16:16 -06:00
SetAndFreeGamepadMapping ( mapping ) ;
2023-07-16 16:11:24 -06:00
RefreshControllerName ( ) ;
2023-07-15 01:00:53 -06:00
} else {
/* Not a valid mapping, ignore it */
SDL_free ( mapping ) ;
}
}
}
2023-07-16 16:11:24 -06:00
static void CommitControllerName ( void )
{
char * mapping = NULL ;
if ( controller - > mapping ) {
mapping = SDL_strdup ( controller - > mapping ) ;
} else {
mapping = NULL ;
}
mapping = SetMappingName ( mapping , controller_name ) ;
SetAndFreeGamepadMapping ( mapping ) ;
}
static void AddControllerNameText ( const char * text )
{
size_t current_length = ( controller_name ? SDL_strlen ( controller_name ) : 0 ) ;
size_t text_length = SDL_strlen ( text ) ;
size_t size = current_length + text_length + 1 ;
char * name = ( char * ) SDL_realloc ( controller_name , size ) ;
if ( name ) {
SDL_memcpy ( & name [ current_length ] , text , text_length + 1 ) ;
controller_name = name ;
}
CommitControllerName ( ) ;
}
static void BackspaceControllerName ( void )
{
size_t length = ( controller_name ? SDL_strlen ( controller_name ) : 0 ) ;
if ( length > 0 ) {
controller_name [ length - 1 ] = ' \0 ' ;
}
CommitControllerName ( ) ;
}
static void ClearControllerName ( void )
{
if ( controller_name ) {
* controller_name = ' \0 ' ;
}
CommitControllerName ( ) ;
}
static void CopyControllerName ( void )
{
SDL_SetClipboardText ( controller_name ) ;
}
static void PasteControllerName ( void )
{
SDL_free ( controller_name ) ;
controller_name = SDL_GetClipboardText ( ) ;
CommitControllerName ( ) ;
}
2023-07-17 13:14:37 -06:00
static void CommitGamepadType ( SDL_GamepadType type )
{
char * mapping = NULL ;
if ( controller - > mapping ) {
mapping = SDL_strdup ( controller - > mapping ) ;
} else {
mapping = NULL ;
}
mapping = SetMappingType ( mapping , type ) ;
SetAndFreeGamepadMapping ( mapping ) ;
}
2023-07-15 01:00:53 -06:00
static const char * GetBindingInstruction ( void )
{
switch ( binding_element ) {
case SDL_GAMEPAD_ELEMENT_INVALID :
return " Select an element to bind from the list on the left " ;
2023-11-06 14:07:12 -07:00
case SDL_GAMEPAD_BUTTON_SOUTH :
case SDL_GAMEPAD_BUTTON_EAST :
case SDL_GAMEPAD_BUTTON_WEST :
case SDL_GAMEPAD_BUTTON_NORTH :
switch ( SDL_GetGamepadButtonLabelForType ( GetGamepadImageType ( image ) , ( SDL_GamepadButton ) binding_element ) ) {
case SDL_GAMEPAD_BUTTON_LABEL_A :
2023-07-15 01:00:53 -06:00
return " Press the A button " ;
2023-11-06 14:07:12 -07:00
case SDL_GAMEPAD_BUTTON_LABEL_B :
2023-07-15 01:00:53 -06:00
return " Press the B button " ;
2023-11-06 14:07:12 -07:00
case SDL_GAMEPAD_BUTTON_LABEL_X :
2023-07-15 01:00:53 -06:00
return " Press the X button " ;
2023-11-06 14:07:12 -07:00
case SDL_GAMEPAD_BUTTON_LABEL_Y :
2023-07-15 01:00:53 -06:00
return " Press the Y button " ;
2023-11-06 14:07:12 -07:00
case SDL_GAMEPAD_BUTTON_LABEL_CROSS :
return " Press the Cross (X) button " ;
case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE :
return " Press the Circle button " ;
case SDL_GAMEPAD_BUTTON_LABEL_SQUARE :
return " Press the Square button " ;
case SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE :
return " Press the Triangle button " ;
default :
return " " ;
2023-07-13 23:57:32 -06:00
}
2023-11-06 14:07:12 -07:00
break ;
2023-07-15 01:00:53 -06:00
case SDL_GAMEPAD_BUTTON_BACK :
return " Press the left center button (Back/View/Share) " ;
case SDL_GAMEPAD_BUTTON_GUIDE :
return " Press the center button (Home/Guide) " ;
case SDL_GAMEPAD_BUTTON_START :
return " Press the right center button (Start/Menu/Options) " ;
case SDL_GAMEPAD_BUTTON_LEFT_STICK :
return " Press the left thumbstick button (LSB/L3) " ;
case SDL_GAMEPAD_BUTTON_RIGHT_STICK :
return " Press the right thumbstick button (RSB/R3) " ;
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER :
return " Press the left shoulder button (LB/L1) " ;
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER :
return " Press the right shoulder button (RB/R1) " ;
case SDL_GAMEPAD_BUTTON_DPAD_UP :
return " Press the D-Pad up " ;
case SDL_GAMEPAD_BUTTON_DPAD_DOWN :
return " Press the D-Pad down " ;
case SDL_GAMEPAD_BUTTON_DPAD_LEFT :
return " Press the D-Pad left " ;
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT :
return " Press the D-Pad right " ;
case SDL_GAMEPAD_BUTTON_MISC1 :
return " Press the bottom center button (Share/Capture) " ;
2023-08-09 11:09:42 -06:00
case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1 :
2023-07-15 01:00:53 -06:00
return " Press the upper paddle under your right hand " ;
2023-08-09 11:09:42 -06:00
case SDL_GAMEPAD_BUTTON_LEFT_PADDLE1 :
2023-07-15 01:00:53 -06:00
return " Press the upper paddle under your left hand " ;
2023-08-09 11:09:42 -06:00
case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2 :
2023-07-15 01:00:53 -06:00
return " Press the lower paddle under your right hand " ;
2023-08-09 11:09:42 -06:00
case SDL_GAMEPAD_BUTTON_LEFT_PADDLE2 :
2023-07-15 01:00:53 -06:00
return " Press the lower paddle under your left hand " ;
case SDL_GAMEPAD_BUTTON_TOUCHPAD :
return " Press down on the touchpad " ;
2024-02-01 21:15:01 -07:00
case SDL_GAMEPAD_BUTTON_MISC2 :
case SDL_GAMEPAD_BUTTON_MISC3 :
case SDL_GAMEPAD_BUTTON_MISC4 :
2024-02-01 21:34:05 -07:00
case SDL_GAMEPAD_BUTTON_MISC5 :
case SDL_GAMEPAD_BUTTON_MISC6 :
2024-02-01 21:15:01 -07:00
return " Press any additional button not already bound " ;
2023-07-15 01:00:53 -06:00
case SDL_GAMEPAD_ELEMENT_AXIS_LEFTX_NEGATIVE :
return " Move the left thumbstick to the left " ;
case SDL_GAMEPAD_ELEMENT_AXIS_LEFTX_POSITIVE :
return " Move the left thumbstick to the right " ;
case SDL_GAMEPAD_ELEMENT_AXIS_LEFTY_NEGATIVE :
return " Move the left thumbstick up " ;
case SDL_GAMEPAD_ELEMENT_AXIS_LEFTY_POSITIVE :
return " Move the left thumbstick down " ;
case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTX_NEGATIVE :
return " Move the right thumbstick to the left " ;
case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTX_POSITIVE :
return " Move the right thumbstick to the right " ;
case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTY_NEGATIVE :
return " Move the right thumbstick up " ;
case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTY_POSITIVE :
return " Move the right thumbstick down " ;
case SDL_GAMEPAD_ELEMENT_AXIS_LEFT_TRIGGER :
return " Pull the left trigger (LT/L2) " ;
case SDL_GAMEPAD_ELEMENT_AXIS_RIGHT_TRIGGER :
return " Pull the right trigger (RT/R2) " ;
case SDL_GAMEPAD_ELEMENT_NAME :
return " Type the name of your controller " ;
case SDL_GAMEPAD_ELEMENT_TYPE :
return " Select the type of your controller " ;
default :
return " " ;
2023-07-13 23:57:32 -06:00
}
}
2023-07-11 14:06:28 -06:00
static int FindController ( SDL_JoystickID id )
2023-06-10 09:43:43 -06:00
{
int i ;
2022-05-15 21:01:12 -06:00
2023-07-11 14:06:28 -06:00
for ( i = 0 ; i < num_controllers ; + + i ) {
if ( id = = controllers [ i ] . id ) {
2023-06-10 09:43:43 -06:00
return i ;
}
}
return - 1 ;
}
2023-07-11 14:06:28 -06:00
static void SetController ( SDL_JoystickID id )
{
int i = FindController ( id ) ;
if ( i < 0 & & num_controllers > 0 ) {
i = 0 ;
}
if ( i > = 0 ) {
controller = & controllers [ i ] ;
} else {
controller = NULL ;
}
2023-07-16 16:11:24 -06:00
RefreshControllerName ( ) ;
2023-07-11 14:06:28 -06:00
}
2023-07-18 12:52:56 -06:00
static void AddController ( SDL_JoystickID id , SDL_bool verbose )
{
Controller * new_controllers ;
Controller * new_controller ;
SDL_Joystick * joystick ;
if ( FindController ( id ) > = 0 ) {
/* We already have this controller */
return ;
}
new_controllers = ( Controller * ) SDL_realloc ( controllers , ( num_controllers + 1 ) * sizeof ( * controllers ) ) ;
2023-11-09 14:29:15 -07:00
if ( ! new_controllers ) {
2023-07-18 12:52:56 -06:00
return ;
}
controller = NULL ;
controllers = new_controllers ;
new_controller = & new_controllers [ num_controllers + + ] ;
SDL_zerop ( new_controller ) ;
new_controller - > id = id ;
new_controller - > joystick = SDL_OpenJoystick ( id ) ;
new_controller - > num_axes = SDL_GetNumJoystickAxes ( new_controller - > joystick ) ;
new_controller - > axis_state = ( AxisState * ) SDL_calloc ( new_controller - > num_axes , sizeof ( * new_controller - > axis_state ) ) ;
joystick = new_controller - > joystick ;
if ( joystick ) {
if ( verbose & & ! SDL_IsGamepad ( id ) ) {
2023-11-15 21:54:38 -07:00
const char * name = SDL_GetJoystickName ( joystick ) ;
const char * path = SDL_GetJoystickPath ( joystick ) ;
char guid [ 33 ] ;
2023-07-18 12:52:56 -06:00
SDL_Log ( " Opened joystick %s%s%s \n " , name , path ? " , " : " " , path ? path : " " ) ;
2023-11-15 21:54:38 -07:00
SDL_GetJoystickGUIDString ( SDL_GetJoystickGUID ( joystick ) , guid , sizeof ( guid ) ) ;
SDL_Log ( " No gamepad mapping for %s \n " , guid ) ;
2023-07-18 12:52:56 -06:00
}
} else {
SDL_Log ( " Couldn't open joystick: %s " , SDL_GetError ( ) ) ;
}
if ( mapping_controller ) {
SetController ( mapping_controller ) ;
} else {
SetController ( id ) ;
}
}
static void DelController ( SDL_JoystickID id )
{
int i = FindController ( id ) ;
if ( i < 0 ) {
return ;
}
if ( display_mode = = CONTROLLER_MODE_BINDING & & id = = controller - > id ) {
SetDisplayMode ( CONTROLLER_MODE_TESTING ) ;
}
/* Reset trigger state */
if ( controllers [ i ] . trigger_effect ! = 0 ) {
controllers [ i ] . trigger_effect = - 1 ;
CyclePS5TriggerEffect ( & controllers [ i ] ) ;
}
SDL_assert ( controllers [ i ] . gamepad = = NULL ) ;
if ( controllers [ i ] . axis_state ) {
SDL_free ( controllers [ i ] . axis_state ) ;
}
if ( controllers [ i ] . joystick ) {
SDL_CloseJoystick ( controllers [ i ] . joystick ) ;
}
- - num_controllers ;
if ( i < num_controllers ) {
SDL_memcpy ( & controllers [ i ] , & controllers [ i + 1 ] , ( num_controllers - i ) * sizeof ( * controllers ) ) ;
}
if ( mapping_controller ) {
SetController ( mapping_controller ) ;
} else {
SetController ( id ) ;
}
}
2023-07-16 10:16:16 -06:00
static void HandleGamepadRemapped ( SDL_JoystickID id )
{
char * mapping ;
int i = FindController ( id ) ;
2023-07-18 12:52:56 -06:00
SDL_assert ( i > = 0 ) ;
2023-07-16 10:16:16 -06:00
if ( i < 0 ) {
return ;
}
if ( ! controllers [ i ] . gamepad ) {
2023-07-18 12:52:56 -06:00
/* Failed to open this controller */
return ;
2023-07-16 10:16:16 -06:00
}
/* Get the current mapping */
mapping = SDL_GetGamepadMapping ( controllers [ i ] . gamepad ) ;
/* Make sure the mapping has a valid name */
if ( mapping & & ! MappingHasName ( mapping ) ) {
mapping = SetMappingName ( mapping , SDL_GetJoystickName ( controllers [ i ] . joystick ) ) ;
}
SDL_free ( controllers [ i ] . mapping ) ;
controllers [ i ] . mapping = mapping ;
controllers [ i ] . has_bindings = MappingHasBindings ( mapping ) ;
}
2023-07-18 12:52:56 -06:00
static void HandleGamepadAdded ( SDL_JoystickID id , SDL_bool verbose )
2023-06-10 09:43:43 -06:00
{
2023-07-18 12:52:56 -06:00
SDL_Gamepad * gamepad ;
2023-06-10 09:43:43 -06:00
Uint16 firmware_version ;
SDL_SensorType sensors [ ] = {
SDL_SENSOR_ACCEL ,
SDL_SENSOR_GYRO ,
SDL_SENSOR_ACCEL_L ,
SDL_SENSOR_GYRO_L ,
SDL_SENSOR_ACCEL_R ,
SDL_SENSOR_GYRO_R
} ;
2023-07-18 12:52:56 -06:00
int i ;
2023-06-10 09:43:43 -06:00
2023-07-18 12:52:56 -06:00
i = FindController ( id ) ;
if ( i < 0 ) {
2023-06-10 09:43:43 -06:00
return ;
}
2023-07-18 12:52:56 -06:00
SDL_assert ( ! controllers [ i ] . gamepad ) ;
controllers [ i ] . gamepad = SDL_OpenGamepad ( id ) ;
2023-06-10 09:43:43 -06:00
2023-07-18 12:52:56 -06:00
gamepad = controllers [ i ] . gamepad ;
if ( gamepad ) {
2023-06-10 09:43:43 -06:00
if ( verbose ) {
2024-01-27 15:37:30 -07:00
SDL_PropertiesID props = SDL_GetGamepadProperties ( gamepad ) ;
2023-07-11 14:06:28 -06:00
const char * name = SDL_GetGamepadName ( gamepad ) ;
const char * path = SDL_GetGamepadPath ( gamepad ) ;
SDL_Log ( " Opened gamepad %s%s%s \n " , name , path ? " , " : " " , path ? path : " " ) ;
2022-05-15 21:01:12 -06:00
2023-07-11 14:06:28 -06:00
firmware_version = SDL_GetGamepadFirmwareVersion ( gamepad ) ;
if ( firmware_version ) {
SDL_Log ( " Firmware version: 0x%x (%d) \n " , firmware_version , firmware_version ) ;
}
2023-06-10 09:43:43 -06:00
2024-01-27 15:37:30 -07:00
if ( SDL_GetBooleanProperty ( props , SDL_PROP_GAMEPAD_CAP_PLAYER_LED_BOOLEAN , SDL_FALSE ) ) {
SDL_Log ( " Has player LED " ) ;
}
if ( SDL_GetBooleanProperty ( props , SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN , SDL_FALSE ) ) {
2023-07-11 14:06:28 -06:00
SDL_Log ( " Rumble supported " ) ;
}
2024-01-27 15:37:30 -07:00
if ( SDL_GetBooleanProperty ( props , SDL_PROP_GAMEPAD_CAP_TRIGGER_RUMBLE_BOOLEAN , SDL_FALSE ) ) {
2023-07-11 14:06:28 -06:00
SDL_Log ( " Trigger rumble supported " ) ;
2023-06-10 09:43:43 -06:00
}
2023-12-09 22:00:11 -07:00
if ( SDL_GetGamepadPlayerIndex ( gamepad ) > = 0 ) {
SDL_Log ( " Player index: %d \n " , SDL_GetGamepadPlayerIndex ( gamepad ) ) ;
}
2022-05-15 21:01:12 -06:00
}
2022-07-24 14:19:02 -06:00
2023-07-11 14:06:28 -06:00
for ( i = 0 ; i < SDL_arraysize ( sensors ) ; + + i ) {
SDL_SensorType sensor = sensors [ i ] ;
if ( SDL_GamepadHasSensor ( gamepad , sensor ) ) {
if ( verbose ) {
SDL_Log ( " Enabling %s at %.2f Hz \n " , GetSensorName ( sensor ) , SDL_GetGamepadSensorDataRate ( gamepad , sensor ) ) ;
}
SDL_SetGamepadSensorEnabled ( gamepad , sensor , SDL_TRUE ) ;
}
}
2023-11-08 13:38:54 -07:00
if ( verbose ) {
char * mapping = SDL_GetGamepadMapping ( gamepad ) ;
if ( mapping ) {
SDL_Log ( " Mapping: %s \n " , mapping ) ;
SDL_free ( mapping ) ;
}
}
2023-07-11 14:06:28 -06:00
} else {
2023-07-18 12:52:56 -06:00
SDL_Log ( " Couldn't open gamepad: %s " , SDL_GetError ( ) ) ;
2023-07-15 01:00:53 -06:00
}
2023-07-16 10:16:16 -06:00
HandleGamepadRemapped ( id ) ;
2023-06-10 09:43:43 -06:00
}
2023-07-18 12:52:56 -06:00
static void HandleGamepadRemoved ( SDL_JoystickID id )
2023-06-10 09:43:43 -06:00
{
2023-07-11 14:06:28 -06:00
int i = FindController ( id ) ;
2023-06-10 09:43:43 -06:00
2023-07-18 12:52:56 -06:00
SDL_assert ( i > = 0 ) ;
2023-06-10 09:43:43 -06:00
if ( i < 0 ) {
return ;
}
2023-07-15 01:00:53 -06:00
if ( controllers [ i ] . mapping ) {
SDL_free ( controllers [ i ] . mapping ) ;
2023-07-18 12:52:56 -06:00
controllers [ i ] . mapping = NULL ;
2023-07-15 01:00:53 -06:00
}
2023-07-11 14:06:28 -06:00
if ( controllers [ i ] . gamepad ) {
SDL_CloseGamepad ( controllers [ i ] . gamepad ) ;
2023-07-18 12:52:56 -06:00
controllers [ i ] . gamepad = NULL ;
2023-07-15 01:00:53 -06:00
}
2023-06-10 09:43:43 -06:00
}
static Uint16 ConvertAxisToRumble ( Sint16 axisval )
{
/* Only start rumbling if the axis is past the halfway point */
const Sint16 half_axis = ( Sint16 ) SDL_ceil ( SDL_JOYSTICK_AXIS_MAX / 2.0f ) ;
if ( axisval > half_axis ) {
return ( Uint16 ) ( axisval - half_axis ) * 4 ;
} else {
return 0 ;
}
}
static SDL_bool ShowingFront ( void )
{
SDL_bool showing_front = SDL_TRUE ;
int i ;
2023-07-15 01:00:53 -06:00
/* Show the back of the gamepad if the paddles are being held or bound */
2023-08-09 11:09:42 -06:00
for ( i = SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1 ; i < = SDL_GAMEPAD_BUTTON_LEFT_PADDLE2 ; + + i ) {
2023-07-15 01:00:53 -06:00
if ( SDL_GetGamepadButton ( controller - > gamepad , ( SDL_GamepadButton ) i ) = = SDL_PRESSED | |
binding_element = = i ) {
showing_front = SDL_FALSE ;
break ;
2023-06-10 09:43:43 -06:00
}
}
2023-07-16 16:11:24 -06:00
if ( ( SDL_GetModState ( ) & SDL_KMOD_SHIFT ) & & binding_element ! = SDL_GAMEPAD_ELEMENT_NAME ) {
2023-06-10 09:43:43 -06:00
showing_front = SDL_FALSE ;
}
return showing_front ;
}
static void SDLCALL VirtualGamepadSetPlayerIndex ( void * userdata , int player_index )
{
SDL_Log ( " Virtual Gamepad: player index set to %d \n " , player_index ) ;
}
static int SDLCALL VirtualGamepadRumble ( void * userdata , Uint16 low_frequency_rumble , Uint16 high_frequency_rumble )
{
SDL_Log ( " Virtual Gamepad: rumble set to %d/%d \n " , low_frequency_rumble , high_frequency_rumble ) ;
return 0 ;
}
static int SDLCALL VirtualGamepadRumbleTriggers ( void * userdata , Uint16 left_rumble , Uint16 right_rumble )
{
SDL_Log ( " Virtual Gamepad: trigger rumble set to %d/%d \n " , left_rumble , right_rumble ) ;
return 0 ;
}
static int SDLCALL VirtualGamepadSetLED ( void * userdata , Uint8 red , Uint8 green , Uint8 blue )
{
SDL_Log ( " Virtual Gamepad: LED set to RGB %d,%d,%d \n " , red , green , blue ) ;
return 0 ;
}
static void OpenVirtualGamepad ( void )
{
SDL_VirtualJoystickDesc desc ;
SDL_JoystickID virtual_id ;
2023-07-13 23:57:32 -06:00
if ( virtual_joystick ) {
return ;
}
2023-06-10 09:43:43 -06:00
SDL_zero ( desc ) ;
desc . version = SDL_VIRTUAL_JOYSTICK_DESC_VERSION ;
desc . type = SDL_JOYSTICK_TYPE_GAMEPAD ;
desc . naxes = SDL_GAMEPAD_AXIS_MAX ;
desc . nbuttons = SDL_GAMEPAD_BUTTON_MAX ;
desc . SetPlayerIndex = VirtualGamepadSetPlayerIndex ;
desc . Rumble = VirtualGamepadRumble ;
desc . RumbleTriggers = VirtualGamepadRumbleTriggers ;
desc . SetLED = VirtualGamepadSetLED ;
virtual_id = SDL_AttachVirtualJoystickEx ( & desc ) ;
if ( virtual_id = = 0 ) {
SDL_Log ( " Couldn't attach virtual device: %s \n " , SDL_GetError ( ) ) ;
} else {
virtual_joystick = SDL_OpenJoystick ( virtual_id ) ;
2023-11-09 14:29:15 -07:00
if ( ! virtual_joystick ) {
2023-06-10 09:43:43 -06:00
SDL_Log ( " Couldn't open virtual device: %s \n " , SDL_GetError ( ) ) ;
}
}
}
static void CloseVirtualGamepad ( void )
{
int i ;
SDL_JoystickID * joysticks = SDL_GetJoysticks ( NULL ) ;
if ( joysticks ) {
for ( i = 0 ; joysticks [ i ] ; + + i ) {
SDL_JoystickID instance_id = joysticks [ i ] ;
if ( SDL_IsJoystickVirtual ( instance_id ) ) {
SDL_DetachVirtualJoystick ( instance_id ) ;
}
2015-06-21 09:33:46 -06:00
}
2023-06-10 09:43:43 -06:00
SDL_free ( joysticks ) ;
2015-06-21 09:33:46 -06:00
}
2023-06-10 09:43:43 -06:00
if ( virtual_joystick ) {
SDL_CloseJoystick ( virtual_joystick ) ;
virtual_joystick = NULL ;
}
}
static void VirtualGamepadMouseMotion ( float x , float y )
{
if ( virtual_button_active ! = SDL_GAMEPAD_BUTTON_INVALID ) {
if ( virtual_axis_active ! = SDL_GAMEPAD_AXIS_INVALID ) {
const float MOVING_DISTANCE = 2.0f ;
if ( SDL_fabs ( x - virtual_axis_start_x ) > = MOVING_DISTANCE | |
SDL_fabs ( y - virtual_axis_start_y ) > = MOVING_DISTANCE ) {
SDL_SetJoystickVirtualButton ( virtual_joystick , virtual_button_active , SDL_RELEASED ) ;
virtual_button_active = SDL_GAMEPAD_BUTTON_INVALID ;
}
}
}
if ( virtual_axis_active ! = SDL_GAMEPAD_AXIS_INVALID ) {
if ( virtual_axis_active = = SDL_GAMEPAD_AXIS_LEFT_TRIGGER | |
virtual_axis_active = = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER ) {
int range = ( SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN ) ;
2023-07-09 20:22:29 -06:00
float distance = SDL_clamp ( ( y - virtual_axis_start_y ) / GetGamepadImageAxisHeight ( image ) , 0.0f , 1.0f ) ;
2023-06-10 09:43:43 -06:00
Sint16 value = ( Sint16 ) ( SDL_JOYSTICK_AXIS_MIN + ( distance * range ) ) ;
SDL_SetJoystickVirtualAxis ( virtual_joystick , virtual_axis_active , value ) ;
} else {
2023-07-09 20:22:29 -06:00
float distanceX = SDL_clamp ( ( x - virtual_axis_start_x ) / GetGamepadImageAxisWidth ( image ) , - 1.0f , 1.0f ) ;
float distanceY = SDL_clamp ( ( y - virtual_axis_start_y ) / GetGamepadImageAxisHeight ( image ) , - 1.0f , 1.0f ) ;
2023-06-10 09:43:43 -06:00
Sint16 valueX , valueY ;
if ( distanceX > = 0 ) {
valueX = ( Sint16 ) ( distanceX * SDL_JOYSTICK_AXIS_MAX ) ;
} else {
valueX = ( Sint16 ) ( distanceX * - SDL_JOYSTICK_AXIS_MIN ) ;
}
if ( distanceY > = 0 ) {
valueY = ( Sint16 ) ( distanceY * SDL_JOYSTICK_AXIS_MAX ) ;
} else {
valueY = ( Sint16 ) ( distanceY * - SDL_JOYSTICK_AXIS_MIN ) ;
}
SDL_SetJoystickVirtualAxis ( virtual_joystick , virtual_axis_active , valueX ) ;
SDL_SetJoystickVirtualAxis ( virtual_joystick , virtual_axis_active + 1 , valueY ) ;
}
}
}
static void VirtualGamepadMouseDown ( float x , float y )
{
2023-07-15 01:00:53 -06:00
int element = GetGamepadImageElementAt ( image , x , y ) ;
2023-06-10 09:43:43 -06:00
2023-07-15 01:00:53 -06:00
if ( element = = SDL_GAMEPAD_ELEMENT_INVALID ) {
return ;
2023-06-10 09:43:43 -06:00
}
2023-07-15 01:00:53 -06:00
if ( element < SDL_GAMEPAD_BUTTON_MAX ) {
virtual_button_active = ( SDL_GamepadButton ) element ;
SDL_SetJoystickVirtualButton ( virtual_joystick , virtual_button_active , SDL_PRESSED ) ;
} else {
switch ( element ) {
case SDL_GAMEPAD_ELEMENT_AXIS_LEFTX_NEGATIVE :
case SDL_GAMEPAD_ELEMENT_AXIS_LEFTX_POSITIVE :
case SDL_GAMEPAD_ELEMENT_AXIS_LEFTY_NEGATIVE :
case SDL_GAMEPAD_ELEMENT_AXIS_LEFTY_POSITIVE :
virtual_axis_active = SDL_GAMEPAD_AXIS_LEFTX ;
break ;
case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTX_NEGATIVE :
case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTX_POSITIVE :
case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTY_NEGATIVE :
case SDL_GAMEPAD_ELEMENT_AXIS_RIGHTY_POSITIVE :
virtual_axis_active = SDL_GAMEPAD_AXIS_RIGHTX ;
break ;
case SDL_GAMEPAD_ELEMENT_AXIS_LEFT_TRIGGER :
virtual_axis_active = SDL_GAMEPAD_AXIS_LEFT_TRIGGER ;
break ;
case SDL_GAMEPAD_ELEMENT_AXIS_RIGHT_TRIGGER :
virtual_axis_active = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER ;
break ;
}
2023-06-10 09:43:43 -06:00
virtual_axis_start_x = x ;
virtual_axis_start_y = y ;
}
}
static void VirtualGamepadMouseUp ( float x , float y )
{
if ( virtual_button_active ! = SDL_GAMEPAD_BUTTON_INVALID ) {
SDL_SetJoystickVirtualButton ( virtual_joystick , virtual_button_active , SDL_RELEASED ) ;
virtual_button_active = SDL_GAMEPAD_BUTTON_INVALID ;
}
if ( virtual_axis_active ! = SDL_GAMEPAD_AXIS_INVALID ) {
if ( virtual_axis_active = = SDL_GAMEPAD_AXIS_LEFT_TRIGGER | |
virtual_axis_active = = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER ) {
SDL_SetJoystickVirtualAxis ( virtual_joystick , virtual_axis_active , SDL_JOYSTICK_AXIS_MIN ) ;
} else {
SDL_SetJoystickVirtualAxis ( virtual_joystick , virtual_axis_active , 0 ) ;
SDL_SetJoystickVirtualAxis ( virtual_joystick , virtual_axis_active + 1 , 0 ) ;
}
virtual_axis_active = SDL_GAMEPAD_AXIS_INVALID ;
}
}
2023-07-10 20:13:51 -06:00
static void DrawGamepadWaiting ( SDL_Renderer * renderer )
{
const char * text = " Waiting for gamepad, press A to add a virtual controller " ;
float x , y ;
x = ( float ) SCREEN_WIDTH / 2 - ( FONT_CHARACTER_SIZE * SDL_strlen ( text ) ) / 2 ;
y = ( float ) TITLE_HEIGHT / 2 - FONT_CHARACTER_SIZE / 2 ;
SDLTest_DrawString ( renderer , x , y , text ) ;
}
2023-07-10 20:41:45 -06:00
static void DrawGamepadInfo ( SDL_Renderer * renderer )
2023-07-10 20:13:51 -06:00
{
2023-07-17 13:14:37 -06:00
const char * type ;
2023-07-10 20:13:51 -06:00
const char * serial ;
char text [ 128 ] ;
float x , y ;
2023-07-16 16:11:24 -06:00
if ( title_highlighted ) {
Uint8 r , g , b , a ;
SDL_GetRenderDrawColor ( renderer , & r , & g , & b , & a ) ;
if ( title_pressed ) {
SDL_SetRenderDrawColor ( renderer , PRESSED_COLOR ) ;
} else {
SDL_SetRenderDrawColor ( renderer , HIGHLIGHT_COLOR ) ;
}
SDL_RenderFillRect ( renderer , & title_area ) ;
SDL_SetRenderDrawColor ( renderer , r , g , b , a ) ;
2023-07-11 14:06:28 -06:00
}
2023-07-16 16:11:24 -06:00
2023-07-17 13:14:37 -06:00
if ( type_highlighted ) {
Uint8 r , g , b , a ;
SDL_GetRenderDrawColor ( renderer , & r , & g , & b , & a ) ;
if ( type_pressed ) {
SDL_SetRenderDrawColor ( renderer , PRESSED_COLOR ) ;
} else {
SDL_SetRenderDrawColor ( renderer , HIGHLIGHT_COLOR ) ;
}
SDL_RenderFillRect ( renderer , & type_area ) ;
SDL_SetRenderDrawColor ( renderer , r , g , b , a ) ;
}
2023-12-10 00:05:34 -07:00
if ( controller - > joystick ) {
SDL_snprintf ( text , sizeof ( text ) , " (% " SDL_PRIu32 " ) " , SDL_GetJoystickInstanceID ( controller - > joystick ) ) ;
x = ( float ) SCREEN_WIDTH - ( FONT_CHARACTER_SIZE * SDL_strlen ( text ) ) - 8.0f ;
y = 8.0f ;
SDLTest_DrawString ( renderer , x , y , text ) ;
}
2023-07-16 16:11:24 -06:00
if ( controller_name & & * controller_name ) {
2023-07-17 13:14:37 -06:00
x = title_area . x + title_area . w / 2 - ( FONT_CHARACTER_SIZE * SDL_strlen ( controller_name ) ) / 2 ;
y = title_area . y + title_area . h / 2 - FONT_CHARACTER_SIZE / 2 ;
2023-07-16 16:11:24 -06:00
SDLTest_DrawString ( renderer , x , y , controller_name ) ;
2023-07-10 20:13:51 -06:00
}
2023-07-11 14:06:28 -06:00
if ( SDL_IsJoystickVirtual ( controller - > id ) ) {
2023-07-10 20:13:51 -06:00
SDL_strlcpy ( text , " Click on the gamepad image below to generate input " , sizeof ( text ) ) ;
x = ( float ) SCREEN_WIDTH / 2 - ( FONT_CHARACTER_SIZE * SDL_strlen ( text ) ) / 2 ;
y = ( float ) TITLE_HEIGHT / 2 - FONT_CHARACTER_SIZE / 2 + FONT_LINE_HEIGHT + 2.0f ;
SDLTest_DrawString ( renderer , x , y , text ) ;
}
2023-07-17 13:14:37 -06:00
type = GetGamepadTypeString ( SDL_GetGamepadType ( controller - > gamepad ) ) ;
x = type_area . x + type_area . w / 2 - ( FONT_CHARACTER_SIZE * SDL_strlen ( type ) ) / 2 ;
y = type_area . y + type_area . h / 2 - FONT_CHARACTER_SIZE / 2 ;
SDLTest_DrawString ( renderer , x , y , type ) ;
2023-07-13 23:57:32 -06:00
if ( display_mode = = CONTROLLER_MODE_TESTING ) {
2023-12-10 00:05:34 -07:00
Uint64 steam_handle = SDL_GetGamepadSteamHandle ( controller - > gamepad ) ;
if ( steam_handle ) {
2024-01-18 07:25:50 -07:00
SDL_snprintf ( text , SDL_arraysize ( text ) , " Steam: 0x%.16 " SDL_PRIx64 , steam_handle ) ;
2023-12-10 00:05:34 -07:00
y = ( float ) SCREEN_HEIGHT - 2 * ( 8.0f + FONT_LINE_HEIGHT ) ;
x = ( float ) SCREEN_WIDTH - 8.0f - ( FONT_CHARACTER_SIZE * SDL_strlen ( text ) ) ;
SDLTest_DrawString ( renderer , x , y , text ) ;
}
2023-07-13 23:57:32 -06:00
SDL_snprintf ( text , SDL_arraysize ( text ) , " VID: 0x%.4x PID: 0x%.4x " ,
SDL_GetJoystickVendor ( controller - > joystick ) ,
SDL_GetJoystickProduct ( controller - > joystick ) ) ;
2023-07-10 20:13:51 -06:00
y = ( float ) SCREEN_HEIGHT - 8.0f - FONT_LINE_HEIGHT ;
2023-07-13 23:57:32 -06:00
x = ( float ) SCREEN_WIDTH - 8.0f - ( FONT_CHARACTER_SIZE * SDL_strlen ( text ) ) ;
2023-07-10 20:13:51 -06:00
SDLTest_DrawString ( renderer , x , y , text ) ;
2023-07-13 23:57:32 -06:00
serial = SDL_GetJoystickSerial ( controller - > joystick ) ;
if ( serial & & * serial ) {
SDL_snprintf ( text , SDL_arraysize ( text ) , " Serial: %s " , serial ) ;
x = ( float ) SCREEN_WIDTH / 2 - ( FONT_CHARACTER_SIZE * SDL_strlen ( text ) ) / 2 ;
y = ( float ) SCREEN_HEIGHT - 8.0f - FONT_LINE_HEIGHT ;
SDLTest_DrawString ( renderer , x , y , text ) ;
}
2023-07-10 20:13:51 -06:00
}
}
2023-11-06 14:07:12 -07:00
static const char * GetButtonLabel ( SDL_GamepadType type , SDL_GamepadButton button )
{
switch ( SDL_GetGamepadButtonLabelForType ( type , button ) ) {
case SDL_GAMEPAD_BUTTON_LABEL_A :
return " A " ;
case SDL_GAMEPAD_BUTTON_LABEL_B :
return " B " ;
case SDL_GAMEPAD_BUTTON_LABEL_X :
return " X " ;
case SDL_GAMEPAD_BUTTON_LABEL_Y :
return " Y " ;
case SDL_GAMEPAD_BUTTON_LABEL_CROSS :
return " Cross (X) " ;
case SDL_GAMEPAD_BUTTON_LABEL_CIRCLE :
return " Circle " ;
case SDL_GAMEPAD_BUTTON_LABEL_SQUARE :
return " Square " ;
case SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE :
return " Triangle " ;
default :
return " UNKNOWN " ;
}
}
2023-07-15 01:00:53 -06:00
static void DrawBindingTips ( SDL_Renderer * renderer )
{
const char * text ;
SDL_Rect image_area , button_area ;
int x , y ;
GetGamepadImageArea ( image , & image_area ) ;
GetGamepadButtonArea ( done_mapping_button , & button_area ) ;
x = image_area . x + image_area . w / 2 ;
y = image_area . y + image_area . h ;
y + = ( button_area . y - y - FONT_CHARACTER_SIZE ) / 2 ;
text = GetBindingInstruction ( ) ;
if ( binding_element = = SDL_GAMEPAD_ELEMENT_INVALID ) {
SDLTest_DrawString ( renderer , ( float ) x - ( FONT_CHARACTER_SIZE * SDL_strlen ( text ) ) / 2 , ( float ) y , text ) ;
} else {
Uint8 r , g , b , a ;
SDL_FRect rect ;
2023-11-16 00:46:31 -07:00
SDL_GamepadButton action_forward = SDL_GAMEPAD_BUTTON_SOUTH ;
SDL_bool bound_forward = MappingHasElement ( controller - > mapping , action_forward ) ;
SDL_GamepadButton action_backward = SDL_GAMEPAD_BUTTON_EAST ;
SDL_bool bound_backward = MappingHasElement ( controller - > mapping , action_backward ) ;
SDL_GamepadButton action_delete = SDL_GAMEPAD_BUTTON_WEST ;
SDL_bool bound_delete = MappingHasElement ( controller - > mapping , action_delete ) ;
2023-07-15 01:00:53 -06:00
y - = ( FONT_CHARACTER_SIZE + BUTTON_MARGIN ) / 2 ;
rect . w = 2.0f + ( FONT_CHARACTER_SIZE * SDL_strlen ( text ) ) + 2.0f ;
rect . h = 2.0f + FONT_CHARACTER_SIZE + 2.0f ;
rect . x = ( float ) x - rect . w / 2 ;
rect . y = ( float ) y - 2.0f ;
SDL_GetRenderDrawColor ( renderer , & r , & g , & b , & a ) ;
SDL_SetRenderDrawColor ( renderer , SELECTED_COLOR ) ;
SDL_RenderFillRect ( renderer , & rect ) ;
SDL_SetRenderDrawColor ( renderer , r , g , b , a ) ;
SDLTest_DrawString ( renderer , ( float ) x - ( FONT_CHARACTER_SIZE * SDL_strlen ( text ) ) / 2 , ( float ) y , text ) ;
y + = ( FONT_CHARACTER_SIZE + BUTTON_MARGIN ) ;
2023-07-16 13:14:52 -06:00
2023-07-16 16:11:24 -06:00
if ( binding_element = = SDL_GAMEPAD_ELEMENT_NAME ) {
text = " (press RETURN to complete) " ;
2023-11-16 00:46:31 -07:00
} else if ( binding_element = = SDL_GAMEPAD_ELEMENT_TYPE | |
binding_element = = action_forward | |
binding_element = = action_backward ) {
2023-07-17 13:14:37 -06:00
text = " (press ESC to cancel) " ;
2023-07-16 13:14:52 -06:00
} else {
2023-11-16 00:46:31 -07:00
static char dynamic_text [ 128 ] ;
2023-11-06 14:07:12 -07:00
SDL_GamepadType type = GetGamepadImageType ( image ) ;
if ( binding_flow & & bound_forward & & bound_backward ) {
2023-11-16 00:46:31 -07:00
if ( binding_element ! = action_delete & & bound_delete ) {
SDL_snprintf ( dynamic_text , sizeof ( dynamic_text ) , " (press %s to skip, %s to go back, %s to delete, and ESC to cancel) " , GetButtonLabel ( type , action_forward ) , GetButtonLabel ( type , action_backward ) , GetButtonLabel ( type , action_delete ) ) ;
} else {
2023-11-16 00:58:39 -07:00
SDL_snprintf ( dynamic_text , sizeof ( dynamic_text ) , " (press %s to skip, %s to go back, SPACE to delete, and ESC to cancel) " , GetButtonLabel ( type , action_forward ) , GetButtonLabel ( type , action_backward ) ) ;
2023-11-16 00:46:31 -07:00
}
2023-11-06 14:07:12 -07:00
text = dynamic_text ;
2023-07-16 16:11:24 -06:00
} else {
2023-11-16 00:46:31 -07:00
text = " (press SPACE to delete and ESC to cancel) " ;
2023-07-16 16:11:24 -06:00
}
2023-07-16 13:14:52 -06:00
}
2023-07-15 01:00:53 -06:00
SDLTest_DrawString ( renderer , ( float ) x - ( FONT_CHARACTER_SIZE * SDL_strlen ( text ) ) / 2 , ( float ) y , text ) ;
}
}
2023-07-13 23:57:32 -06:00
static void UpdateGamepadEffects ( void )
2023-07-11 11:04:25 -06:00
{
2023-07-13 23:57:32 -06:00
if ( display_mode ! = CONTROLLER_MODE_TESTING | | ! controller - > gamepad ) {
return ;
}
2023-07-11 14:06:28 -06:00
2023-07-13 23:57:32 -06:00
/* Update LED based on left thumbstick position */
{
Sint16 x = SDL_GetGamepadAxis ( controller - > gamepad , SDL_GAMEPAD_AXIS_LEFTX ) ;
Sint16 y = SDL_GetGamepadAxis ( controller - > gamepad , SDL_GAMEPAD_AXIS_LEFTY ) ;
if ( ! set_LED ) {
set_LED = ( x < - 8000 | | x > 8000 | | y > 8000 ) ;
}
if ( set_LED ) {
Uint8 r , g , b ;
if ( x < 0 ) {
r = ( Uint8 ) ( ( ( ~ x ) * 255 ) / 32767 ) ;
b = 0 ;
2023-07-11 14:06:28 -06:00
} else {
2023-07-13 23:57:32 -06:00
r = 0 ;
b = ( Uint8 ) ( ( ( int ) ( x ) * 255 ) / 32767 ) ;
2023-07-11 11:04:25 -06:00
}
2023-07-13 23:57:32 -06:00
if ( y > 0 ) {
g = ( Uint8 ) ( ( ( int ) ( y ) * 255 ) / 32767 ) ;
} else {
g = 0 ;
}
SDL_SetGamepadLED ( controller - > gamepad , r , g , b ) ;
}
}
if ( controller - > trigger_effect = = 0 ) {
/* Update rumble based on trigger state */
{
Sint16 left = SDL_GetGamepadAxis ( controller - > gamepad , SDL_GAMEPAD_AXIS_LEFT_TRIGGER ) ;
Sint16 right = SDL_GetGamepadAxis ( controller - > gamepad , SDL_GAMEPAD_AXIS_RIGHT_TRIGGER ) ;
Uint16 low_frequency_rumble = ConvertAxisToRumble ( left ) ;
Uint16 high_frequency_rumble = ConvertAxisToRumble ( right ) ;
SDL_RumbleGamepad ( controller - > gamepad , low_frequency_rumble , high_frequency_rumble , 250 ) ;
}
/* Update trigger rumble based on thumbstick state */
{
Sint16 left = SDL_GetGamepadAxis ( controller - > gamepad , SDL_GAMEPAD_AXIS_LEFTY ) ;
Sint16 right = SDL_GetGamepadAxis ( controller - > gamepad , SDL_GAMEPAD_AXIS_RIGHTY ) ;
Uint16 left_rumble = ConvertAxisToRumble ( ~ left ) ;
Uint16 right_rumble = ConvertAxisToRumble ( ~ right ) ;
SDL_RumbleGamepadTriggers ( controller - > gamepad , left_rumble , right_rumble , 250 ) ;
2023-07-11 11:04:25 -06:00
}
}
}
2023-06-10 09:43:43 -06:00
static void loop ( void * arg )
{
SDL_Event event ;
/* Update to get the current event state */
SDL_PumpEvents ( ) ;
/* Process all currently pending events */
while ( SDL_PeepEvents ( & event , 1 , SDL_GETEVENT , SDL_EVENT_FIRST , SDL_EVENT_LAST ) = = 1 ) {
2023-07-10 10:14:26 -06:00
SDL_ConvertEventToRenderCoordinates ( screen , & event ) ;
2023-06-10 09:43:43 -06:00
switch ( event . type ) {
case SDL_EVENT_JOYSTICK_ADDED :
2023-07-11 14:06:28 -06:00
AddController ( event . jdevice . which , SDL_TRUE ) ;
2023-06-10 09:43:43 -06:00
break ;
2023-07-11 14:06:28 -06:00
case SDL_EVENT_JOYSTICK_REMOVED :
DelController ( event . jdevice . which ) ;
2023-06-10 09:43:43 -06:00
break ;
2023-07-11 14:06:28 -06:00
case SDL_EVENT_JOYSTICK_AXIS_MOTION :
2023-07-15 01:00:53 -06:00
if ( display_mode = = CONTROLLER_MODE_TESTING ) {
if ( event . jaxis . value < = ( - SDL_JOYSTICK_AXIS_MAX / 2 ) | | event . jaxis . value > = ( SDL_JOYSTICK_AXIS_MAX / 2 ) ) {
SetController ( event . jaxis . which ) ;
}
} else if ( display_mode = = CONTROLLER_MODE_BINDING & &
event . jaxis . which = = controller - > id & &
event . jaxis . axis < controller - > num_axes & &
binding_element ! = SDL_GAMEPAD_ELEMENT_INVALID ) {
const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80 ; /* ShanWan PS3 gamepad needed 96 */
AxisState * pAxisState = & controller - > axis_state [ event . jaxis . axis ] ;
int nValue = event . jaxis . value ;
int nCurrentDistance , nFarthestDistance ;
if ( ! pAxisState - > m_bMoving ) {
Sint16 nInitialValue ;
pAxisState - > m_bMoving = SDL_GetJoystickAxisInitialState ( controller - > joystick , event . jaxis . axis , & nInitialValue ) ;
pAxisState - > m_nLastValue = nValue ;
pAxisState - > m_nStartingValue = nInitialValue ;
pAxisState - > m_nFarthestValue = nInitialValue ;
} else if ( SDL_abs ( nValue - pAxisState - > m_nLastValue ) < = MAX_ALLOWED_JITTER ) {
break ;
} else {
pAxisState - > m_nLastValue = nValue ;
}
nCurrentDistance = SDL_abs ( nValue - pAxisState - > m_nStartingValue ) ;
nFarthestDistance = SDL_abs ( pAxisState - > m_nFarthestValue - pAxisState - > m_nStartingValue ) ;
if ( nCurrentDistance > nFarthestDistance ) {
pAxisState - > m_nFarthestValue = nValue ;
nFarthestDistance = SDL_abs ( pAxisState - > m_nFarthestValue - pAxisState - > m_nStartingValue ) ;
}
# ifdef DEBUG_AXIS_MAPPING
SDL_Log ( " AXIS %d nValue %d nCurrentDistance %d nFarthestDistance %d \n " , event . jaxis . axis , nValue , nCurrentDistance , nFarthestDistance ) ;
# endif
/* If we've gone out far enough and started to come back, let's bind this axis */
if ( nFarthestDistance > = 16000 & & nCurrentDistance < = 10000 ) {
char binding [ 12 ] ;
int axis_min = StandardizeAxisValue ( pAxisState - > m_nStartingValue ) ;
int axis_max = StandardizeAxisValue ( pAxisState - > m_nFarthestValue ) ;
if ( axis_min = = 0 & & axis_max = = SDL_JOYSTICK_AXIS_MIN ) {
/* The negative half axis */
( void ) SDL_snprintf ( binding , sizeof ( binding ) , " -a%d " , event . jaxis . axis ) ;
} else if ( axis_min = = 0 & & axis_max = = SDL_JOYSTICK_AXIS_MAX ) {
/* The positive half axis */
( void ) SDL_snprintf ( binding , sizeof ( binding ) , " +a%d " , event . jaxis . axis ) ;
} else {
( void ) SDL_snprintf ( binding , sizeof ( binding ) , " a%d " , event . jaxis . axis ) ;
if ( axis_min > axis_max ) {
/* Invert the axis */
SDL_strlcat ( binding , " ~ " , SDL_arraysize ( binding ) ) ;
}
}
# ifdef DEBUG_AXIS_MAPPING
SDL_Log ( " AXIS %d axis_min = %d, axis_max = %d, binding = %s \n " , event . jaxis . axis , axis_min , axis_max , binding ) ;
# endif
CommitBindingElement ( binding , SDL_FALSE ) ;
}
2023-07-11 14:06:28 -06:00
}
break ;
case SDL_EVENT_JOYSTICK_BUTTON_DOWN :
2023-07-15 01:00:53 -06:00
if ( display_mode = = CONTROLLER_MODE_TESTING ) {
SetController ( event . jbutton . which ) ;
}
break ;
case SDL_EVENT_JOYSTICK_BUTTON_UP :
if ( display_mode = = CONTROLLER_MODE_BINDING & &
event . jbutton . which = = controller - > id & &
binding_element ! = SDL_GAMEPAD_ELEMENT_INVALID ) {
char binding [ 12 ] ;
SDL_snprintf ( binding , sizeof ( binding ) , " b%d " , event . jbutton . button ) ;
CommitBindingElement ( binding , SDL_FALSE ) ;
}
break ;
case SDL_EVENT_JOYSTICK_HAT_MOTION :
if ( display_mode = = CONTROLLER_MODE_BINDING & &
event . jhat . which = = controller - > id & &
event . jhat . value ! = SDL_HAT_CENTERED & &
binding_element ! = SDL_GAMEPAD_ELEMENT_INVALID ) {
char binding [ 12 ] ;
SDL_snprintf ( binding , sizeof ( binding ) , " h%d.%d " , event . jhat . hat , event . jhat . value ) ;
CommitBindingElement ( binding , SDL_FALSE ) ;
}
2023-06-10 09:43:43 -06:00
break ;
2023-07-18 12:52:56 -06:00
case SDL_EVENT_GAMEPAD_ADDED :
HandleGamepadAdded ( event . gdevice . which , SDL_TRUE ) ;
break ;
case SDL_EVENT_GAMEPAD_REMOVED :
HandleGamepadRemoved ( event . gdevice . which ) ;
break ;
2023-07-13 23:57:32 -06:00
case SDL_EVENT_GAMEPAD_REMAPPED :
HandleGamepadRemapped ( event . gdevice . which ) ;
break ;
2023-12-10 00:05:34 -07:00
case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED :
RefreshControllerName ( ) ;
break ;
2023-07-15 02:18:55 -06:00
# ifdef VERBOSE_TOUCHPAD
2023-06-10 09:43:43 -06:00
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN :
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION :
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP :
SDL_Log ( " Gamepad % " SDL_PRIu32 " touchpad % " SDL_PRIs32 " finger % " SDL_PRIs32 " %s %.2f, %.2f, %.2f \n " ,
event . gtouchpad . which ,
event . gtouchpad . touchpad ,
event . gtouchpad . finger ,
( event . type = = SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN ? " pressed at " : ( event . type = = SDL_EVENT_GAMEPAD_TOUCHPAD_UP ? " released at " : " moved to " ) ) ,
event . gtouchpad . x ,
event . gtouchpad . y ,
event . gtouchpad . pressure ) ;
break ;
2023-07-15 02:18:55 -06:00
# endif /* VERBOSE_TOUCHPAD */
2023-06-10 09:43:43 -06:00
# ifdef VERBOSE_SENSORS
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE :
SDL_Log ( " Gamepad % " SDL_PRIu32 " sensor %s: %.2f, %.2f, %.2f (% " SDL_PRIu64 " ) \n " ,
event . gsensor . which ,
GetSensorName ( ( SDL_SensorType ) event . gsensor . sensor ) ,
event . gsensor . data [ 0 ] ,
event . gsensor . data [ 1 ] ,
event . gsensor . data [ 2 ] ,
event . gsensor . sensor_timestamp ) ;
break ;
# endif /* VERBOSE_SENSORS */
# ifdef VERBOSE_AXES
case SDL_EVENT_GAMEPAD_AXIS_MOTION :
2023-07-15 01:00:53 -06:00
if ( display_mode = = CONTROLLER_MODE_TESTING ) {
if ( event . gaxis . value < = ( - SDL_JOYSTICK_AXIS_MAX / 2 ) | | event . gaxis . value > = ( SDL_JOYSTICK_AXIS_MAX / 2 ) ) {
SetController ( event . gaxis . which ) ;
}
2023-06-10 09:43:43 -06:00
}
SDL_Log ( " Gamepad % " SDL_PRIu32 " axis %s changed to %d \n " ,
event . gaxis . which ,
SDL_GetGamepadStringForAxis ( ( SDL_GamepadAxis ) event . gaxis . axis ) ,
event . gaxis . value ) ;
break ;
# endif /* VERBOSE_AXES */
case SDL_EVENT_GAMEPAD_BUTTON_DOWN :
case SDL_EVENT_GAMEPAD_BUTTON_UP :
2023-07-15 01:00:53 -06:00
if ( display_mode = = CONTROLLER_MODE_TESTING ) {
if ( event . type = = SDL_EVENT_GAMEPAD_BUTTON_DOWN ) {
SetController ( event . gbutton . which ) ;
}
2023-06-10 09:43:43 -06:00
}
2023-07-15 02:18:55 -06:00
# ifdef VERBOSE_BUTTONS
2023-06-10 09:43:43 -06:00
SDL_Log ( " Gamepad % " SDL_PRIu32 " button %s %s \n " ,
event . gbutton . which ,
SDL_GetGamepadStringForButton ( ( SDL_GamepadButton ) event . gbutton . button ) ,
event . gbutton . state ? " pressed " : " released " ) ;
2023-07-15 02:18:55 -06:00
# endif /* VERBOSE_BUTTONS */
2023-06-10 09:43:43 -06:00
2023-07-13 23:57:32 -06:00
if ( display_mode = = CONTROLLER_MODE_TESTING ) {
/* Cycle PS5 trigger effects when the microphone button is pressed */
if ( event . type = = SDL_EVENT_GAMEPAD_BUTTON_DOWN & &
controller & & SDL_GetGamepadType ( controller - > gamepad ) = = SDL_GAMEPAD_TYPE_PS5 & &
event . gbutton . button = = SDL_GAMEPAD_BUTTON_MISC1 ) {
CyclePS5TriggerEffect ( controller ) ;
}
2023-06-10 09:43:43 -06:00
}
break ;
case SDL_EVENT_MOUSE_BUTTON_DOWN :
2023-07-15 01:00:53 -06:00
if ( virtual_joystick & & controller & & controller - > joystick = = virtual_joystick ) {
2023-06-10 09:43:43 -06:00
VirtualGamepadMouseDown ( event . button . x , event . button . y ) ;
}
2023-07-15 01:00:53 -06:00
UpdateButtonHighlights ( event . button . x , event . button . y , event . button . state ? SDL_TRUE : SDL_FALSE ) ;
2023-06-10 09:43:43 -06:00
break ;
2020-11-11 19:57:37 -07:00
2023-06-10 09:43:43 -06:00
case SDL_EVENT_MOUSE_BUTTON_UP :
2023-07-15 01:00:53 -06:00
if ( virtual_joystick & & controller & & controller - > joystick = = virtual_joystick ) {
2023-06-10 09:43:43 -06:00
VirtualGamepadMouseUp ( event . button . x , event . button . y ) ;
}
2023-07-13 23:57:32 -06:00
if ( display_mode = = CONTROLLER_MODE_TESTING ) {
if ( GamepadButtonContains ( setup_mapping_button , event . button . x , event . button . y ) ) {
SetDisplayMode ( CONTROLLER_MODE_BINDING ) ;
}
} else if ( display_mode = = CONTROLLER_MODE_BINDING ) {
2023-07-15 01:00:53 -06:00
if ( GamepadButtonContains ( done_mapping_button , event . button . x , event . button . y ) ) {
if ( controller - > mapping ) {
SDL_Log ( " Mapping complete: \n " ) ;
SDL_Log ( " %s \n " , controller - > mapping ) ;
}
2023-07-13 23:57:32 -06:00
SetDisplayMode ( CONTROLLER_MODE_TESTING ) ;
} else if ( GamepadButtonContains ( cancel_button , event . button . x , event . button . y ) ) {
CancelMapping ( ) ;
} else if ( GamepadButtonContains ( clear_button , event . button . x , event . button . y ) ) {
ClearMapping ( ) ;
2023-07-15 01:00:53 -06:00
} else if ( controller - > has_bindings & &
GamepadButtonContains ( copy_button , event . button . x , event . button . y ) ) {
2023-07-13 23:57:32 -06:00
CopyMapping ( ) ;
} else if ( GamepadButtonContains ( paste_button , event . button . x , event . button . y ) ) {
PasteMapping ( ) ;
2023-07-16 16:11:24 -06:00
} else if ( title_pressed ) {
SetCurrentBindingElement ( SDL_GAMEPAD_ELEMENT_NAME , SDL_FALSE ) ;
2023-07-17 13:14:37 -06:00
} else if ( type_pressed ) {
SetCurrentBindingElement ( SDL_GAMEPAD_ELEMENT_TYPE , SDL_FALSE ) ;
} else if ( binding_element = = SDL_GAMEPAD_ELEMENT_TYPE ) {
int type = GetGamepadTypeDisplayAt ( gamepad_type , event . button . x , event . button . y ) ;
if ( type ! = SDL_GAMEPAD_TYPE_UNSELECTED ) {
CommitGamepadType ( ( SDL_GamepadType ) type ) ;
StopBinding ( ) ;
}
2023-07-15 01:00:53 -06:00
} else {
2023-07-16 13:39:05 -06:00
int gamepad_element = SDL_GAMEPAD_ELEMENT_INVALID ;
2023-07-15 01:00:53 -06:00
char * joystick_element ;
2023-07-16 13:39:05 -06:00
if ( controller - > joystick ! = virtual_joystick ) {
gamepad_element = GetGamepadImageElementAt ( image , event . button . x , event . button . y ) ;
}
if ( gamepad_element = = SDL_GAMEPAD_ELEMENT_INVALID ) {
gamepad_element = GetGamepadDisplayElementAt ( gamepad_elements , controller - > gamepad , event . button . x , event . button . y ) ;
}
2023-07-15 01:00:53 -06:00
if ( gamepad_element ! = SDL_GAMEPAD_ELEMENT_INVALID ) {
2023-07-16 13:14:52 -06:00
/* Set this to SDL_FALSE if you don't want to start the binding flow at this point */
const SDL_bool should_start_flow = SDL_TRUE ;
SetCurrentBindingElement ( gamepad_element , should_start_flow ) ;
2023-07-15 01:00:53 -06:00
}
joystick_element = GetJoystickDisplayElementAt ( joystick_elements , controller - > joystick , event . button . x , event . button . y ) ;
if ( joystick_element ) {
CommitBindingElement ( joystick_element , SDL_TRUE ) ;
SDL_free ( joystick_element ) ;
}
2023-07-13 23:57:32 -06:00
}
2023-07-11 11:04:25 -06:00
}
2023-07-15 01:00:53 -06:00
UpdateButtonHighlights ( event . button . x , event . button . y , event . button . state ? SDL_TRUE : SDL_FALSE ) ;
2023-06-10 09:43:43 -06:00
break ;
case SDL_EVENT_MOUSE_MOTION :
2023-07-15 01:00:53 -06:00
if ( virtual_joystick & & controller & & controller - > joystick = = virtual_joystick ) {
2023-06-10 09:43:43 -06:00
VirtualGamepadMouseMotion ( event . motion . x , event . motion . y ) ;
}
2023-07-15 01:00:53 -06:00
UpdateButtonHighlights ( event . motion . x , event . motion . y , event . motion . state ? SDL_TRUE : SDL_FALSE ) ;
2023-06-10 09:43:43 -06:00
break ;
case SDL_EVENT_KEY_DOWN :
2023-07-13 23:57:32 -06:00
if ( display_mode = = CONTROLLER_MODE_TESTING ) {
if ( event . key . keysym . sym > = SDLK_0 & & event . key . keysym . sym < = SDLK_9 ) {
if ( controller & & controller - > gamepad ) {
int player_index = ( event . key . keysym . sym - SDLK_0 ) ;
2023-06-10 09:43:43 -06:00
2023-07-13 23:57:32 -06:00
SDL_SetGamepadPlayerIndex ( controller - > gamepad , player_index ) ;
}
break ;
} else if ( event . key . keysym . sym = = SDLK_a ) {
OpenVirtualGamepad ( ) ;
} else if ( event . key . keysym . sym = = SDLK_d ) {
CloseVirtualGamepad ( ) ;
2023-07-18 13:50:10 -06:00
} else if ( event . key . keysym . sym = = SDLK_r & & ( event . key . keysym . mod & SDL_KMOD_CTRL ) ) {
SDL_ReloadGamepadMappings ( ) ;
2023-07-13 23:57:32 -06:00
} else if ( event . key . keysym . sym = = SDLK_ESCAPE ) {
done = SDL_TRUE ;
}
} else if ( display_mode = = CONTROLLER_MODE_BINDING ) {
if ( event . key . keysym . sym = = SDLK_c & & ( event . key . keysym . mod & SDL_KMOD_CTRL ) ) {
2023-07-16 16:11:24 -06:00
if ( binding_element = = SDL_GAMEPAD_ELEMENT_NAME ) {
CopyControllerName ( ) ;
} else {
CopyMapping ( ) ;
}
2023-07-13 23:57:32 -06:00
} else if ( event . key . keysym . sym = = SDLK_v & & ( event . key . keysym . mod & SDL_KMOD_CTRL ) ) {
2023-07-16 16:11:24 -06:00
if ( binding_element = = SDL_GAMEPAD_ELEMENT_NAME ) {
ClearControllerName ( ) ;
PasteControllerName ( ) ;
} else {
PasteMapping ( ) ;
}
2023-07-13 23:57:32 -06:00
} else if ( event . key . keysym . sym = = SDLK_x & & ( event . key . keysym . mod & SDL_KMOD_CTRL ) ) {
2023-07-16 16:11:24 -06:00
if ( binding_element = = SDL_GAMEPAD_ELEMENT_NAME ) {
CopyControllerName ( ) ;
ClearControllerName ( ) ;
} else {
CopyMapping ( ) ;
ClearMapping ( ) ;
}
2023-07-15 01:00:53 -06:00
} else if ( event . key . keysym . sym = = SDLK_SPACE ) {
2023-07-16 16:11:24 -06:00
if ( binding_element ! = SDL_GAMEPAD_ELEMENT_NAME ) {
ClearBinding ( ) ;
}
} else if ( event . key . keysym . sym = = SDLK_BACKSPACE ) {
if ( binding_element = = SDL_GAMEPAD_ELEMENT_NAME ) {
BackspaceControllerName ( ) ;
}
} else if ( event . key . keysym . sym = = SDLK_RETURN ) {
if ( binding_element = = SDL_GAMEPAD_ELEMENT_NAME ) {
StopBinding ( ) ;
}
2023-07-13 23:57:32 -06:00
} else if ( event . key . keysym . sym = = SDLK_ESCAPE ) {
2023-07-15 01:00:53 -06:00
if ( binding_element ! = SDL_GAMEPAD_ELEMENT_INVALID ) {
2023-07-16 13:14:52 -06:00
StopBinding ( ) ;
2023-07-15 01:00:53 -06:00
} else {
CancelMapping ( ) ;
}
2023-06-10 09:43:43 -06:00
}
}
2023-07-11 14:06:28 -06:00
break ;
2023-07-16 16:11:24 -06:00
case SDL_EVENT_TEXT_INPUT :
if ( display_mode = = CONTROLLER_MODE_BINDING ) {
if ( binding_element = = SDL_GAMEPAD_ELEMENT_NAME ) {
AddControllerNameText ( event . text . text ) ;
}
}
break ;
2023-06-10 09:43:43 -06:00
case SDL_EVENT_QUIT :
done = SDL_TRUE ;
break ;
default :
break ;
}
}
2023-07-15 01:00:53 -06:00
/* Wait 30 ms for joystick events to stop coming in,
in case a gamepad sends multiple events for a single control ( e . g . axis and button for trigger )
*/
if ( binding_advance_time & & SDL_GetTicks ( ) > ( binding_advance_time + 30 ) ) {
2023-07-16 13:14:52 -06:00
if ( binding_flow ) {
SetNextBindingElement ( ) ;
} else {
StopBinding ( ) ;
}
2023-07-15 01:00:53 -06:00
}
2023-06-10 09:43:43 -06:00
/* blank screen, set up for drawing this frame. */
SDL_SetRenderDrawColor ( screen , 0xFF , 0xFF , 0xFF , SDL_ALPHA_OPAQUE ) ;
SDL_RenderClear ( screen ) ;
2023-07-10 20:13:51 -06:00
SDL_SetRenderDrawColor ( screen , 0x10 , 0x10 , 0x10 , SDL_ALPHA_OPAQUE ) ;
2023-06-10 09:43:43 -06:00
2023-07-11 14:06:28 -06:00
if ( controller ) {
2023-07-09 20:22:29 -06:00
SetGamepadImageShowingFront ( image , ShowingFront ( ) ) ;
2023-07-11 14:06:28 -06:00
UpdateGamepadImageFromGamepad ( image , controller - > gamepad ) ;
2023-07-15 01:00:53 -06:00
if ( display_mode = = CONTROLLER_MODE_BINDING & &
binding_element ! = SDL_GAMEPAD_ELEMENT_INVALID ) {
SetGamepadImageElement ( image , binding_element , SDL_TRUE ) ;
}
2023-07-09 20:22:29 -06:00
RenderGamepadImage ( image ) ;
2023-06-10 09:43:43 -06:00
2023-07-17 13:14:37 -06:00
if ( binding_element = = SDL_GAMEPAD_ELEMENT_TYPE ) {
SetGamepadTypeDisplayRealType ( gamepad_type , SDL_GetRealGamepadType ( controller - > gamepad ) ) ;
RenderGamepadTypeDisplay ( gamepad_type ) ;
} else {
RenderGamepadDisplay ( gamepad_elements , controller - > gamepad ) ;
}
2023-07-11 14:06:28 -06:00
RenderJoystickDisplay ( joystick_elements , controller - > joystick ) ;
2023-07-10 18:06:42 -06:00
2023-07-13 23:57:32 -06:00
if ( display_mode = = CONTROLLER_MODE_TESTING ) {
RenderGamepadButton ( setup_mapping_button ) ;
} else if ( display_mode = = CONTROLLER_MODE_BINDING ) {
2023-07-15 01:00:53 -06:00
DrawBindingTips ( screen ) ;
RenderGamepadButton ( done_mapping_button ) ;
2023-07-13 23:57:32 -06:00
RenderGamepadButton ( cancel_button ) ;
RenderGamepadButton ( clear_button ) ;
2023-07-15 01:00:53 -06:00
if ( controller - > has_bindings ) {
2023-07-13 23:57:32 -06:00
RenderGamepadButton ( copy_button ) ;
}
RenderGamepadButton ( paste_button ) ;
2023-07-11 14:06:28 -06:00
}
2023-07-11 11:04:25 -06:00
2023-07-10 20:41:45 -06:00
DrawGamepadInfo ( screen ) ;
2023-07-10 20:13:51 -06:00
2023-07-13 23:57:32 -06:00
UpdateGamepadEffects ( ) ;
2023-07-10 20:13:51 -06:00
} else {
DrawGamepadWaiting ( screen ) ;
2023-06-10 09:43:43 -06:00
}
SDL_Delay ( 16 ) ;
SDL_RenderPresent ( screen ) ;
2024-01-23 18:40:51 -07:00
# ifdef SDL_PLATFORM_EMSCRIPTEN
2023-06-10 09:43:43 -06:00
if ( done ) {
emscripten_cancel_main_loop ( ) ;
}
# endif
2015-06-21 09:33:46 -06:00
}
2022-11-30 13:51:59 -07:00
int main ( int argc , char * argv [ ] )
2015-06-21 09:33:46 -06:00
{
2023-11-29 09:15:48 -07:00
SDL_bool show_mappings = SDL_FALSE ;
2023-06-10 09:43:43 -06:00
int i ;
2023-07-10 10:14:26 -06:00
float content_scale ;
int screen_width , screen_height ;
2023-07-13 23:57:32 -06:00
SDL_Rect area ;
2023-06-10 09:43:43 -06:00
int gamepad_index = - 1 ;
2023-03-16 17:25:39 -06:00
SDLTest_CommonState * state ;
/* Initialize test framework */
state = SDLTest_CommonCreateState ( argv , 0 ) ;
2023-11-09 14:29:15 -07:00
if ( ! state ) {
2023-03-16 17:25:39 -06:00
return 1 ;
}
2015-06-21 09:33:46 -06:00
2023-06-10 09:43:43 -06:00
SDL_SetHint ( SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE , " 1 " ) ;
SDL_SetHint ( SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE , " 1 " ) ;
SDL_SetHint ( SDL_HINT_JOYSTICK_HIDAPI_STEAM , " 1 " ) ;
SDL_SetHint ( SDL_HINT_JOYSTICK_ROG_CHAKRAM , " 1 " ) ;
SDL_SetHint ( SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS , " 1 " ) ;
2024-02-11 18:57:25 -07:00
SDL_SetHint ( SDL_HINT_JOYSTICK_LINUX_DEADZONES , " 1 " ) ;
2023-06-10 09:43:43 -06:00
2015-06-21 09:33:46 -06:00
/* Enable standard application logging */
2015-11-25 13:39:28 -07:00
SDL_LogSetPriority ( SDL_LOG_CATEGORY_APPLICATION , SDL_LOG_PRIORITY_INFO ) ;
2015-06-21 09:33:46 -06:00
2023-03-16 17:25:39 -06:00
/* Parse commandline */
2023-06-10 09:43:43 -06:00
for ( i = 1 ; i < argc ; ) {
int consumed ;
consumed = SDLTest_CommonArg ( state , i ) ;
if ( ! consumed ) {
if ( SDL_strcmp ( argv [ i ] , " --mappings " ) = = 0 ) {
2023-11-29 08:33:31 -07:00
show_mappings = SDL_TRUE ;
2023-06-10 09:43:43 -06:00
consumed = 1 ;
} else if ( SDL_strcmp ( argv [ i ] , " --virtual " ) = = 0 ) {
OpenVirtualGamepad ( ) ;
consumed = 1 ;
} else if ( gamepad_index < 0 ) {
char * endptr = NULL ;
gamepad_index = ( int ) SDL_strtol ( argv [ i ] , & endptr , 0 ) ;
if ( endptr ! = argv [ i ] & & * endptr = = ' \0 ' & & gamepad_index > = 0 ) {
consumed = 1 ;
}
}
}
if ( consumed < = 0 ) {
static const char * options [ ] = { " [--mappings] " , " [--virtual] " , " [index] " , NULL } ;
SDLTest_CommonLogUsage ( state , argv [ 0 ] , options ) ;
return 1 ;
}
i + = consumed ;
}
if ( gamepad_index < 0 ) {
gamepad_index = 0 ;
2023-03-16 17:25:39 -06:00
}
2023-06-10 09:43:43 -06:00
/* Initialize SDL (Note: video is required to start event loop) */
if ( SDL_Init ( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD ) < 0 ) {
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't initialize SDL: %s \n " , SDL_GetError ( ) ) ;
2015-06-21 09:33:46 -06:00
return 1 ;
}
2021-11-23 04:22:02 -07:00
2023-06-10 09:43:43 -06:00
SDL_AddGamepadMappingsFromFile ( " gamecontrollerdb.txt " ) ;
2023-11-29 08:33:31 -07:00
if ( show_mappings ) {
2023-11-29 09:15:48 -07:00
int count = 0 ;
char * * mappings = SDL_GetGamepadMappings ( & count ) ;
2023-11-29 08:33:31 -07:00
int map_i ;
SDL_Log ( " Supported mappings: \n " ) ;
2023-11-29 09:15:48 -07:00
for ( map_i = 0 ; map_i < count ; + + map_i ) {
SDL_Log ( " \t %s \n " , mappings [ map_i ] ) ;
2023-11-29 08:33:31 -07:00
}
SDL_Log ( " \n " ) ;
2023-11-29 09:15:48 -07:00
SDL_free ( mappings ) ;
2023-11-29 08:33:31 -07:00
}
2023-06-10 09:43:43 -06:00
/* Create a window to display gamepad state */
2023-07-10 10:14:26 -06:00
content_scale = SDL_GetDisplayContentScale ( SDL_GetPrimaryDisplay ( ) ) ;
if ( content_scale = = 0.0f ) {
content_scale = 1.0f ;
}
screen_width = ( int ) SDL_ceilf ( SCREEN_WIDTH * content_scale ) ;
screen_height = ( int ) SDL_ceilf ( SCREEN_HEIGHT * content_scale ) ;
2023-07-11 15:21:35 -06:00
window = SDL_CreateWindow ( " SDL Controller Test " , screen_width , screen_height , 0 ) ;
2023-11-09 14:29:15 -07:00
if ( ! window ) {
2023-06-10 09:43:43 -06:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't create window: %s \n " , SDL_GetError ( ) ) ;
return 2 ;
2020-04-18 22:41:37 -06:00
}
2023-06-10 09:43:43 -06:00
screen = SDL_CreateRenderer ( window , NULL , 0 ) ;
2023-11-09 14:29:15 -07:00
if ( ! screen ) {
2023-06-10 09:43:43 -06:00
SDL_LogError ( SDL_LOG_CATEGORY_APPLICATION , " Couldn't create renderer: %s \n " , SDL_GetError ( ) ) ;
SDL_DestroyWindow ( window ) ;
return 2 ;
}
SDL_SetRenderDrawColor ( screen , 0x00 , 0x00 , 0x00 , SDL_ALPHA_OPAQUE ) ;
SDL_RenderClear ( screen ) ;
SDL_RenderPresent ( screen ) ;
/* scale for platforms that don't give you the window size you asked for. */
SDL_SetRenderLogicalPresentation ( screen , SCREEN_WIDTH , SCREEN_HEIGHT ,
SDL_LOGICAL_PRESENTATION_LETTERBOX ,
SDL_SCALEMODE_LINEAR ) ;
2023-07-16 16:11:24 -06:00
title_area . w = ( float ) GAMEPAD_WIDTH ;
title_area . h = ( float ) FONT_CHARACTER_SIZE + 2 * BUTTON_MARGIN ;
title_area . x = ( float ) PANEL_WIDTH + PANEL_SPACING ;
title_area . y = ( float ) TITLE_HEIGHT / 2 - title_area . h / 2 ;
2023-07-17 13:14:37 -06:00
type_area . w = ( float ) PANEL_WIDTH - 2 * BUTTON_MARGIN ;
type_area . h = ( float ) FONT_CHARACTER_SIZE + 2 * BUTTON_MARGIN ;
type_area . x = ( float ) BUTTON_MARGIN ;
type_area . y = ( float ) TITLE_HEIGHT / 2 - type_area . h / 2 ;
2023-07-09 20:22:29 -06:00
image = CreateGamepadImage ( screen ) ;
2023-11-09 14:29:15 -07:00
if ( ! image ) {
2023-06-10 09:43:43 -06:00
SDL_DestroyRenderer ( screen ) ;
SDL_DestroyWindow ( window ) ;
return 2 ;
2015-06-21 09:33:46 -06:00
}
2023-07-10 18:06:42 -06:00
SetGamepadImagePosition ( image , PANEL_WIDTH + PANEL_SPACING , TITLE_HEIGHT ) ;
gamepad_elements = CreateGamepadDisplay ( screen ) ;
2023-07-13 23:57:32 -06:00
area . x = 0 ;
area . y = TITLE_HEIGHT ;
area . w = PANEL_WIDTH ;
area . h = GAMEPAD_HEIGHT ;
SetGamepadDisplayArea ( gamepad_elements , & area ) ;
2023-07-10 18:06:42 -06:00
2023-07-17 13:14:37 -06:00
gamepad_type = CreateGamepadTypeDisplay ( screen ) ;
area . x = 0 ;
area . y = TITLE_HEIGHT ;
area . w = PANEL_WIDTH ;
area . h = GAMEPAD_HEIGHT ;
SetGamepadTypeDisplayArea ( gamepad_type , & area ) ;
2023-07-10 18:06:42 -06:00
joystick_elements = CreateJoystickDisplay ( screen ) ;
2023-07-13 23:57:32 -06:00
area . x = PANEL_WIDTH + PANEL_SPACING + GAMEPAD_WIDTH + PANEL_SPACING ;
area . y = TITLE_HEIGHT ;
area . w = PANEL_WIDTH ;
area . h = GAMEPAD_HEIGHT ;
SetJoystickDisplayArea ( joystick_elements , & area ) ;
setup_mapping_button = CreateGamepadButton ( screen , " Setup Mapping " ) ;
area . w = SDL_max ( MINIMUM_BUTTON_WIDTH , GetGamepadButtonLabelWidth ( setup_mapping_button ) + 2 * BUTTON_PADDING ) ;
area . h = GetGamepadButtonLabelHeight ( setup_mapping_button ) + 2 * BUTTON_PADDING ;
area . x = BUTTON_MARGIN ;
area . y = SCREEN_HEIGHT - BUTTON_MARGIN - area . h ;
SetGamepadButtonArea ( setup_mapping_button , & area ) ;
cancel_button = CreateGamepadButton ( screen , " Cancel " ) ;
area . w = SDL_max ( MINIMUM_BUTTON_WIDTH , GetGamepadButtonLabelWidth ( cancel_button ) + 2 * BUTTON_PADDING ) ;
area . h = GetGamepadButtonLabelHeight ( cancel_button ) + 2 * BUTTON_PADDING ;
area . x = BUTTON_MARGIN ;
area . y = SCREEN_HEIGHT - BUTTON_MARGIN - area . h ;
SetGamepadButtonArea ( cancel_button , & area ) ;
clear_button = CreateGamepadButton ( screen , " Clear " ) ;
area . x + = area . w + BUTTON_PADDING ;
area . w = SDL_max ( MINIMUM_BUTTON_WIDTH , GetGamepadButtonLabelWidth ( clear_button ) + 2 * BUTTON_PADDING ) ;
area . h = GetGamepadButtonLabelHeight ( clear_button ) + 2 * BUTTON_PADDING ;
area . y = SCREEN_HEIGHT - BUTTON_MARGIN - area . h ;
SetGamepadButtonArea ( clear_button , & area ) ;
copy_button = CreateGamepadButton ( screen , " Copy " ) ;
area . x + = area . w + BUTTON_PADDING ;
area . w = SDL_max ( MINIMUM_BUTTON_WIDTH , GetGamepadButtonLabelWidth ( copy_button ) + 2 * BUTTON_PADDING ) ;
area . h = GetGamepadButtonLabelHeight ( copy_button ) + 2 * BUTTON_PADDING ;
area . y = SCREEN_HEIGHT - BUTTON_MARGIN - area . h ;
SetGamepadButtonArea ( copy_button , & area ) ;
paste_button = CreateGamepadButton ( screen , " Paste " ) ;
area . x + = area . w + BUTTON_PADDING ;
area . w = SDL_max ( MINIMUM_BUTTON_WIDTH , GetGamepadButtonLabelWidth ( paste_button ) + 2 * BUTTON_PADDING ) ;
area . h = GetGamepadButtonLabelHeight ( paste_button ) + 2 * BUTTON_PADDING ;
area . y = SCREEN_HEIGHT - BUTTON_MARGIN - area . h ;
SetGamepadButtonArea ( paste_button , & area ) ;
2023-07-15 01:00:53 -06:00
done_mapping_button = CreateGamepadButton ( screen , " Done " ) ;
area . w = SDL_max ( MINIMUM_BUTTON_WIDTH , GetGamepadButtonLabelWidth ( done_mapping_button ) + 2 * BUTTON_PADDING ) ;
area . h = GetGamepadButtonLabelHeight ( done_mapping_button ) + 2 * BUTTON_PADDING ;
2023-07-13 23:57:32 -06:00
area . x = SCREEN_WIDTH / 2 - area . w / 2 ;
area . y = SCREEN_HEIGHT - BUTTON_MARGIN - area . h ;
2023-07-15 01:00:53 -06:00
SetGamepadButtonArea ( done_mapping_button , & area ) ;
2023-07-11 11:04:25 -06:00
2023-06-10 09:43:43 -06:00
/* Process the initial gamepad list */
loop ( NULL ) ;
2023-07-11 14:06:28 -06:00
if ( gamepad_index < num_controllers ) {
SetController ( controllers [ gamepad_index ] . id ) ;
} else if ( num_controllers > 0 ) {
SetController ( controllers [ 0 ] . id ) ;
2023-06-10 09:43:43 -06:00
}
2020-11-17 11:30:20 -07:00
2023-06-10 09:43:43 -06:00
/* Loop, getting gamepad events! */
2024-01-23 18:40:51 -07:00
# ifdef SDL_PLATFORM_EMSCRIPTEN
2023-06-10 09:43:43 -06:00
emscripten_set_main_loop_arg ( loop , NULL , 0 , 1 ) ;
2020-04-18 22:41:37 -06:00
# else
while ( ! done ) {
2023-06-10 09:43:43 -06:00
loop ( NULL ) ;
2020-04-18 22:41:37 -06:00
}
# endif
2015-06-21 09:33:46 -06:00
2023-06-10 09:43:43 -06:00
CloseVirtualGamepad ( ) ;
2023-07-11 14:06:28 -06:00
while ( num_controllers > 0 ) {
2023-07-18 12:52:56 -06:00
HandleGamepadRemoved ( controllers [ 0 ] . id ) ;
2023-07-11 14:06:28 -06:00
DelController ( controllers [ 0 ] . id ) ;
}
2023-08-22 10:44:28 -06:00
SDL_free ( controllers ) ;
SDL_free ( controller_name ) ;
2023-07-09 20:22:29 -06:00
DestroyGamepadImage ( image ) ;
2023-07-10 18:06:42 -06:00
DestroyGamepadDisplay ( gamepad_elements ) ;
2023-07-17 13:14:37 -06:00
DestroyGamepadTypeDisplay ( gamepad_type ) ;
2023-07-10 18:06:42 -06:00
DestroyJoystickDisplay ( joystick_elements ) ;
2023-08-22 10:44:28 -06:00
DestroyGamepadButton ( setup_mapping_button ) ;
DestroyGamepadButton ( done_mapping_button ) ;
DestroyGamepadButton ( cancel_button ) ;
DestroyGamepadButton ( clear_button ) ;
2023-07-11 11:04:25 -06:00
DestroyGamepadButton ( copy_button ) ;
2023-08-22 10:44:28 -06:00
DestroyGamepadButton ( paste_button ) ;
SDLTest_CleanupTextDrawing ( ) ;
2023-06-10 09:43:43 -06:00
SDL_DestroyRenderer ( screen ) ;
SDL_DestroyWindow ( window ) ;
2023-08-22 10:44:28 -06:00
SDL_Quit ( ) ;
2023-03-16 17:25:39 -06:00
SDLTest_CommonDestroyState ( state ) ;
2020-04-18 22:41:37 -06:00
return 0 ;
2015-06-21 09:33:46 -06:00
}