From 6228e2e1dadadb2dee6867e3766c25dfc35411d3 Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Sun, 14 Sep 2025 15:28:11 -0400 Subject: [PATCH] sdl: add support for controller sensors --- input/drivers_joypad/sdl_joypad.c | 92 +++++++++++++++++++++++++++++-- input/input_driver.c | 31 +++++++---- input/input_driver.h | 5 +- 3 files changed, 110 insertions(+), 18 deletions(-) diff --git a/input/drivers_joypad/sdl_joypad.c b/input/drivers_joypad/sdl_joypad.c index 719ce55ab1fd..845741a39f84 100644 --- a/input/drivers_joypad/sdl_joypad.c +++ b/input/drivers_joypad/sdl_joypad.c @@ -27,6 +27,9 @@ #include "../../tasks/tasks_internal.h" #include "../../verbosity.h" +#define SDL_SUPPORTS_RUMBLE SDL_VERSION_ATLEAST(2, 0, 9) +#define SDL_SUPPORTS_SENSORS SDL_VERSION_ATLEAST(2, 0, 14) + typedef struct _sdl_joypad { SDL_Joystick *joypad; @@ -216,7 +219,7 @@ static void sdl_pad_connect(unsigned id) RARCH_WARN("[SDL] Device #%u does not support leftright haptic effect.\n", id); } } -#if SDL_VERSION_ATLEAST(2, 0, 9) +#if SDL_SUPPORTS_RUMBLE if (!pad->haptic || pad->rumble_effect == -2) { pad->rumble_effect = -3; @@ -299,7 +302,7 @@ static void *sdl_joypad_init(void *data) else g_has_haptic = true; -#if SDL_VERSION_ATLEAST(2, 0, 9) +#if SDL_SUPPORTS_RUMBLE /* enable extended hid reports to support ps4/ps5 rumble over bluetooth */ SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); #endif @@ -504,7 +507,7 @@ static bool sdl_joypad_set_rumble(unsigned pad, enum retro_rumble_effect effect, return false; } -#if SDL_VERSION_ATLEAST(2, 0, 9) +#if SDK_SUPPORTS_RUMBLE if (joypad->rumble_effect == -3) { if (SDL_JoystickRumble(joypad->joypad, efx.leftright.large_magnitude, efx.leftright.small_magnitude, efx.leftright.length) == -1) @@ -548,6 +551,85 @@ static bool sdl_joypad_set_rumble(unsigned pad, enum retro_rumble_effect effect, } #endif +static bool sdl_joypad_set_sensor_state(unsigned pad, enum retro_sensor_action action, unsigned rate) +{ + sdl_joypad_t *joypad = (sdl_joypad_t*)&sdl_pads[pad]; + (void)joypad; /* maybe unused */ + + switch (action) + { +#if SDL_SUPPORTS_SENSORS + case RETRO_SENSOR_GYROSCOPE_DISABLE: + case RETRO_SENSOR_GYROSCOPE_ENABLE: + if (SDL_GameControllerHasSensor(joypad->controller, SDL_SENSOR_GYRO)) + return !SDL_GameControllerSetSensorEnabled(joypad->controller, SDL_SENSOR_GYRO, + action == RETRO_SENSOR_GYROSCOPE_ENABLE); + else + return false; + + case RETRO_SENSOR_ACCELEROMETER_DISABLE: + case RETRO_SENSOR_ACCELEROMETER_ENABLE: + if (SDL_GameControllerHasSensor(joypad->controller, SDL_SENSOR_ACCEL)) + return !SDL_GameControllerSetSensorEnabled(joypad->controller, SDL_SENSOR_ACCEL, + action == RETRO_SENSOR_ACCELEROMETER_ENABLE); + else + return false; +#endif + + default: + return false; + } + return false; +} + +static bool sdl_joypad_get_sensor_input(unsigned pad, unsigned id, float *value) +{ +#if SDL_SUPPORTS_SENSORS + sdl_joypad_t *joypad = (sdl_joypad_t*)&sdl_pads[pad]; + SDL_SensorType sensor_type; + float sensor_data[3]; + + if (!joypad->controller) + return false; + + if ((id >= RETRO_SENSOR_ACCELEROMETER_X) && (id <= RETRO_SENSOR_ACCELEROMETER_Z)) + sensor_type = SDL_SENSOR_ACCEL; + else if ((id >= RETRO_SENSOR_GYROSCOPE_X) && (id <= RETRO_SENSOR_GYROSCOPE_Z)) + sensor_type = SDL_SENSOR_GYRO; + else + return false; + + if (SDL_GameControllerGetSensorData(joypad->controller, sensor_type, sensor_data, 3) < 0) + return false; + + switch (id) + { + case RETRO_SENSOR_ACCELEROMETER_X: + *value = sensor_data[0] / SDL_STANDARD_GRAVITY; + break; + case RETRO_SENSOR_ACCELEROMETER_Y: + *value = sensor_data[2] / SDL_STANDARD_GRAVITY; + break; + case RETRO_SENSOR_ACCELEROMETER_Z: + *value = sensor_data[1] / SDL_STANDARD_GRAVITY; + break; + case RETRO_SENSOR_GYROSCOPE_X: + *value = sensor_data[0]; + break; + case RETRO_SENSOR_GYROSCOPE_Y: + *value = -sensor_data[2]; + break; + case RETRO_SENSOR_GYROSCOPE_Z: + *value = sensor_data[1]; + break; + } + + return true; +#else + return false; +#endif +} + static bool sdl_joypad_query_pad(unsigned pad) { return pad < MAX_USERS && sdl_pads[pad].joypad; @@ -568,8 +650,8 @@ input_device_driver_t sdl_joypad = { NULL, /* set_rumble */ #endif NULL, /* set_rumble_gain */ - NULL, /* set_sensor_state */ - NULL, /* get_sensor_input */ + sdl_joypad_set_sensor_state, + sdl_joypad_get_sensor_input, sdl_joypad_name, #ifdef HAVE_SDL2 "sdl2", diff --git a/input/input_driver.c b/input/input_driver.c index 2c9a38087cc5..28203b95d4b3 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -511,6 +511,7 @@ bool input_driver_set_sensor( enum retro_sensor_action action, unsigned rate) { const input_driver_t *current_driver; + bool enabled = false; if (!input_driver_st.current_data) return false; @@ -521,17 +522,19 @@ bool input_driver_set_sensor( || (action == RETRO_SENSOR_GYROSCOPE_ENABLE) || (action == RETRO_SENSOR_ILLUMINANCE_ENABLE))) return false; - if ( (current_driver = input_driver_st.current_driver) + + if (input_driver_st.primary_joypad && input_driver_st.primary_joypad->set_sensor_state) + enabled = input_driver_st.primary_joypad->set_sensor_state(port, action, rate); + + if ( !enabled + && (current_driver = input_driver_st.current_driver) && current_driver->set_sensor_state) { void *current_data = input_driver_st.current_data; - return current_driver->set_sensor_state(current_data, + enabled |= current_driver->set_sensor_state(current_data, port, action, rate); } - else if (input_driver_st.primary_joypad && input_driver_st.primary_joypad->set_sensor_state) - return input_driver_st.primary_joypad->set_sensor_state(NULL, - port, action, rate); - return false; + return enabled; } /**************************************/ @@ -539,18 +542,24 @@ bool input_driver_set_sensor( float input_driver_get_sensor( unsigned port, bool sensors_enable, unsigned id) { + if (!sensors_enable) + return 0.0f; + + if (input_driver_st.primary_joypad && input_driver_st.primary_joypad->get_sensor_input) + { + float value; + /* if joypad driver's get_sensor_input returns false, let input driver try */ + if (input_driver_st.primary_joypad->get_sensor_input(port, id, &value)) + return value; + } if (input_driver_st.current_data) { const input_driver_t *current_driver = input_driver_st.current_driver; - if (sensors_enable && current_driver->get_sensor_input) + if (current_driver->get_sensor_input) { void *current_data = input_driver_st.current_data; return current_driver->get_sensor_input(current_data, port, id); } - else if (sensors_enable && input_driver_st.primary_joypad && - input_driver_st.primary_joypad->get_sensor_input) - return input_driver_st.primary_joypad->get_sensor_input(NULL, - port, id); } return 0.0f; diff --git a/input/input_driver.h b/input/input_driver.h index b03162995a74..b02046e258a9 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -548,9 +548,10 @@ struct rarch_joypad_driver void (*poll)(void); bool (*set_rumble)(unsigned, enum retro_rumble_effect, uint16_t); bool (*set_rumble_gain)(unsigned, unsigned); - bool (*set_sensor_state)(void *data, unsigned port, + bool (*set_sensor_state)(unsigned port, enum retro_sensor_action action, unsigned rate); - float (*get_sensor_input)(void *data, unsigned port, unsigned id); + /* return true if handled; false to fall back to input driver */ + bool (*get_sensor_input)(unsigned port, unsigned id, float *value); const char *(*name)(unsigned); const char *ident;