From 49ce1a712c852cb35be3870943c478c1b0bcb699 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Thu, 13 Aug 2026 23:20:16 -0400 Subject: [PATCH 01/11] Add SDL3 device sensor support --- input/drivers/sdl3_input.c | 133 +++++++++++++++++++++++++++++++++++-- 1 file changed, 128 insertions(+), 5 deletions(-) diff --git a/input/drivers/sdl3_input.c b/input/drivers/sdl3_input.c index c61d15fe1475..937623db0b6d 100644 --- a/input/drivers/sdl3_input.c +++ b/input/drivers/sdl3_input.c @@ -100,6 +100,14 @@ typedef struct sdl3_input /* The SDL_Window input is read against. */ SDL_Window *window; + + /* Host device sensors, opened on demand by sdl3_set_sensor_state. + * Gamepad gyro/accel are handled by the SDL3 joypad driver. */ + SDL_Sensor *accel; + SDL_Sensor *gyro; + float accel_data[3]; + float gyro_data[3]; + bool sensors_init; } sdl3_input_t; #ifdef WEBOS @@ -635,6 +643,13 @@ static void sdl3_input_free(void *data) SDL_FlushEvents(SDL_EVENT_FINGER_DOWN, SDL_EVENT_FINGER_CANCELED); SDL_FlushEvents(SDL_EVENT_PEN_PROXIMITY_IN, SDL_EVENT_PEN_AXIS); + if (sdl->accel) + SDL_CloseSensor(sdl->accel); + if (sdl->gyro) + SDL_CloseSensor(sdl->gyro); + if (sdl->sensors_init) + SDL_QuitSubSystem(SDL_INIT_SENSOR); + /* Nothing polls after this point, so the flags would stay raised * across a runtime driver switch. */ input_state_get_ptr()->flags &= @@ -644,16 +659,77 @@ static void sdl3_input_free(void *data) free(sdl); } +/* Opens the first host sensor of the given type, if any. */ +static SDL_Sensor *sdl3_open_sensor(SDL_SensorType type) +{ + int i; + int num_sensors = 0; + SDL_Sensor *sensor = NULL; + SDL_SensorID *sensors = SDL_GetSensors(&num_sensors); + + if (!sensors) + return NULL; + + for (i = 0; i < num_sensors; i++) + { + if (SDL_GetSensorTypeForID(sensors[i]) == type) + { + sensor = SDL_OpenSensor(sensors[i]); + break; + } + } + + SDL_free(sensors); + return sensor; +} + +/* Enables the host device's own accelerometer/gyroscope. The requested + * rate is ignored - SDL3 offers no sensor rate control. */ static bool sdl3_set_sensor_state(void *data, unsigned port, enum retro_sensor_action action, unsigned rate) { - /* Sensors are not exposed through the SDL3 keyboard/mouse driver. - * Gamepad gyro/accel are handled by the SDL3 joypad driver. */ + sdl3_input_t *sdl = (sdl3_input_t*)data; + switch (action) { - case RETRO_SENSOR_ILLUMINANCE_DISABLE: - case RETRO_SENSOR_GYROSCOPE_DISABLE: + case RETRO_SENSOR_ACCELEROMETER_ENABLE: + case RETRO_SENSOR_GYROSCOPE_ENABLE: + { + bool accel = action == RETRO_SENSOR_ACCELEROMETER_ENABLE; + SDL_Sensor **sensor = accel ? &sdl->accel : &sdl->gyro; + + if (*sensor) + return true; + + /* Deferred so launches that never touch sensors don't pay + * for sensor enumeration. */ + if (!sdl->sensors_init) + { + if (!SDL_InitSubSystem(SDL_INIT_SENSOR)) + return false; + sdl->sensors_init = true; + } + + return (*sensor = sdl3_open_sensor( + accel ? SDL_SENSOR_ACCEL : SDL_SENSOR_GYRO)) != NULL; + } case RETRO_SENSOR_ACCELEROMETER_DISABLE: + if (sdl->accel) + { + SDL_CloseSensor(sdl->accel); + sdl->accel = NULL; + memset(sdl->accel_data, 0, sizeof(sdl->accel_data)); + } + return true; + case RETRO_SENSOR_GYROSCOPE_DISABLE: + if (sdl->gyro) + { + SDL_CloseSensor(sdl->gyro); + sdl->gyro = NULL; + memset(sdl->gyro_data, 0, sizeof(sdl->gyro_data)); + } + return true; + case RETRO_SENSOR_ILLUMINANCE_DISABLE: /* Disabling an unsupported sensor shouldn't fail. */ return true; default: @@ -678,6 +754,37 @@ static float sdl3_window_pixel_density(sdl3_input_t *sdl) return 1.0f; } + +static float sdl3_get_sensor_input(void *data, unsigned port, unsigned id) +{ + sdl3_input_t *sdl = (sdl3_input_t*)data; + + /* The host device's sensors only ever map to port 0. */ + if (port != 0) + return 0.0f; + + switch (id) + { + /* SDL reports acceleration in m/s^2; libretro expects g. */ + case RETRO_SENSOR_ACCELEROMETER_X: + return sdl->accel_data[0] / SDL_STANDARD_GRAVITY; + case RETRO_SENSOR_ACCELEROMETER_Y: + return sdl->accel_data[1] / SDL_STANDARD_GRAVITY; + case RETRO_SENSOR_ACCELEROMETER_Z: + return sdl->accel_data[2] / SDL_STANDARD_GRAVITY; + /* Both sides use radians per second. */ + case RETRO_SENSOR_GYROSCOPE_X: + return sdl->gyro_data[0]; + case RETRO_SENSOR_GYROSCOPE_Y: + return sdl->gyro_data[1]; + case RETRO_SENSOR_GYROSCOPE_Z: + return sdl->gyro_data[2]; + } + + return 0.0f; +} + + static void sdl3_poll_mouse(sdl3_input_t *sdl) { float density; @@ -1023,6 +1130,17 @@ static void sdl3_input_poll(void *data) sdl3_poll_mouse(sdl); sdl3_poll_touch(sdl); + if (sdl->accel || sdl->gyro) + { + /* The event pump only refreshes sensor state alongside window + * events; SDL_UpdateSensors works without a focused window. */ + SDL_UpdateSensors(); + if (sdl->accel) + SDL_GetSensorData(sdl->accel, sdl->accel_data, 3); + if (sdl->gyro) + SDL_GetSensorData(sdl->gyro, sdl->gyro_data, 3); + } + sdl->mouse_wu = false; sdl->mouse_wd = false; sdl->mouse_wl = false; @@ -1175,6 +1293,11 @@ static void sdl3_input_poll(void *data) SDL_FlushEvents(SDL_EVENT_FINGER_DOWN, SDL_EVENT_FINGER_CANCELED); sdl3_poll_pen(sdl); + + /* Sensor updates arrive at device rate and are read by polling + * above (SDL_GetSensorData), so flush the events. */ + if (sdl->sensors_init) + SDL_FlushEvent(SDL_EVENT_SENSOR_UPDATE); } static void sdl3_grab_mouse(void *data, bool state) @@ -1206,7 +1329,7 @@ input_driver_t input_sdl3 = { sdl3_input_state, sdl3_input_free, sdl3_set_sensor_state, - NULL, /* get_sensor_input */ + sdl3_get_sensor_input, sdl3_get_capabilities, "sdl3", sdl3_grab_mouse, From 399537b348cf9cf82d95e25030ae212ed47ac7f8 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Tue, 18 Aug 2026 02:14:31 -0400 Subject: [PATCH 02/11] Gate sensors to port 0, zero on failure --- input/drivers/sdl3_input.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/input/drivers/sdl3_input.c b/input/drivers/sdl3_input.c index 937623db0b6d..9ec66904e02a 100644 --- a/input/drivers/sdl3_input.c +++ b/input/drivers/sdl3_input.c @@ -690,6 +690,10 @@ static bool sdl3_set_sensor_state(void *data, unsigned port, { sdl3_input_t *sdl = (sdl3_input_t*)data; + /* The host device's sensors only ever map to port 0. */ + if (port != 0) + return false; + switch (action) { case RETRO_SENSOR_ACCELEROMETER_ENABLE: @@ -1135,10 +1139,12 @@ static void sdl3_input_poll(void *data) /* The event pump only refreshes sensor state alongside window * events; SDL_UpdateSensors works without a focused window. */ SDL_UpdateSensors(); - if (sdl->accel) - SDL_GetSensorData(sdl->accel, sdl->accel_data, 3); - if (sdl->gyro) - SDL_GetSensorData(sdl->gyro, sdl->gyro_data, 3); + /* Zero on failure so a vanished sensor reads as at rest + * rather than frozen at its last reported values. */ + if (sdl->accel && !SDL_GetSensorData(sdl->accel, sdl->accel_data, 3)) + memset(sdl->accel_data, 0, sizeof(sdl->accel_data)); + if (sdl->gyro && !SDL_GetSensorData(sdl->gyro, sdl->gyro_data, 3)) + memset(sdl->gyro_data, 0, sizeof(sdl->gyro_data)); } sdl->mouse_wu = false; From c46a0e45f541d907162a685bab906282d84ce06f Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Tue, 18 Aug 2026 03:00:16 -0400 Subject: [PATCH 03/11] sdl3: Sensor comments --- input/drivers/sdl3_input.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/input/drivers/sdl3_input.c b/input/drivers/sdl3_input.c index 9ec66904e02a..09b3cec35b39 100644 --- a/input/drivers/sdl3_input.c +++ b/input/drivers/sdl3_input.c @@ -101,8 +101,7 @@ typedef struct sdl3_input /* The SDL_Window input is read against. */ SDL_Window *window; - /* Host device sensors, opened on demand by sdl3_set_sensor_state. - * Gamepad gyro/accel are handled by the SDL3 joypad driver. */ + /* Sensors. Used if the SDL3 joypad driver isn't active. */ SDL_Sensor *accel; SDL_Sensor *gyro; float accel_data[3]; @@ -663,9 +662,9 @@ static void sdl3_input_free(void *data) static SDL_Sensor *sdl3_open_sensor(SDL_SensorType type) { int i; - int num_sensors = 0; - SDL_Sensor *sensor = NULL; - SDL_SensorID *sensors = SDL_GetSensors(&num_sensors); + int num_sensors = 0; + SDL_Sensor *sensor = NULL; + SDL_SensorID *sensors = SDL_GetSensors(&num_sensors); if (!sensors) return NULL; @@ -683,8 +682,7 @@ static SDL_Sensor *sdl3_open_sensor(SDL_SensorType type) return sensor; } -/* Enables the host device's own accelerometer/gyroscope. The requested - * rate is ignored - SDL3 offers no sensor rate control. */ +/* Enables the accelerometer/gyroscope. */ static bool sdl3_set_sensor_state(void *data, unsigned port, enum retro_sensor_action action, unsigned rate) { @@ -699,14 +697,13 @@ static bool sdl3_set_sensor_state(void *data, unsigned port, case RETRO_SENSOR_ACCELEROMETER_ENABLE: case RETRO_SENSOR_GYROSCOPE_ENABLE: { - bool accel = action == RETRO_SENSOR_ACCELEROMETER_ENABLE; - SDL_Sensor **sensor = accel ? &sdl->accel : &sdl->gyro; + bool accelerometer = action == RETRO_SENSOR_ACCELEROMETER_ENABLE; + SDL_Sensor **sensor = accelerometer ? &sdl->accel : &sdl->gyro; if (*sensor) return true; - /* Deferred so launches that never touch sensors don't pay - * for sensor enumeration. */ + /* Make sure the Sensor subsystem is available. */ if (!sdl->sensors_init) { if (!SDL_InitSubSystem(SDL_INIT_SENSOR)) @@ -714,8 +711,7 @@ static bool sdl3_set_sensor_state(void *data, unsigned port, sdl->sensors_init = true; } - return (*sensor = sdl3_open_sensor( - accel ? SDL_SENSOR_ACCEL : SDL_SENSOR_GYRO)) != NULL; + return (*sensor = sdl3_open_sensor(accelerometer ? SDL_SENSOR_ACCEL : SDL_SENSOR_GYRO)) != NULL; } case RETRO_SENSOR_ACCELEROMETER_DISABLE: if (sdl->accel) @@ -1136,8 +1132,7 @@ static void sdl3_input_poll(void *data) if (sdl->accel || sdl->gyro) { - /* The event pump only refreshes sensor state alongside window - * events; SDL_UpdateSensors works without a focused window. */ + /* SDL_UpdateSensors works without a focused window. */ SDL_UpdateSensors(); /* Zero on failure so a vanished sensor reads as at rest * rather than frozen at its last reported values. */ From 6c440c2e48454be55c79091b48bff2842538afeb Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Tue, 18 Aug 2026 12:47:51 -0400 Subject: [PATCH 04/11] sdl3: simplify sensor input --- input/drivers/sdl3_input.c | 60 ++++++++++++++------------------------ 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/input/drivers/sdl3_input.c b/input/drivers/sdl3_input.c index 09b3cec35b39..bfdef21b9080 100644 --- a/input/drivers/sdl3_input.c +++ b/input/drivers/sdl3_input.c @@ -104,8 +104,6 @@ typedef struct sdl3_input /* Sensors. Used if the SDL3 joypad driver isn't active. */ SDL_Sensor *accel; SDL_Sensor *gyro; - float accel_data[3]; - float gyro_data[3]; bool sensors_init; } sdl3_input_t; @@ -714,21 +712,18 @@ static bool sdl3_set_sensor_state(void *data, unsigned port, return (*sensor = sdl3_open_sensor(accelerometer ? SDL_SENSOR_ACCEL : SDL_SENSOR_GYRO)) != NULL; } case RETRO_SENSOR_ACCELEROMETER_DISABLE: - if (sdl->accel) - { - SDL_CloseSensor(sdl->accel); - sdl->accel = NULL; - memset(sdl->accel_data, 0, sizeof(sdl->accel_data)); - } - return true; case RETRO_SENSOR_GYROSCOPE_DISABLE: - if (sdl->gyro) { - SDL_CloseSensor(sdl->gyro); - sdl->gyro = NULL; - memset(sdl->gyro_data, 0, sizeof(sdl->gyro_data)); + SDL_Sensor **sensor = action == RETRO_SENSOR_ACCELEROMETER_DISABLE + ? &sdl->accel : &sdl->gyro; + + if (*sensor) + { + SDL_CloseSensor(*sensor); + *sensor = NULL; + } + return true; } - return true; case RETRO_SENSOR_ILLUMINANCE_DISABLE: /* Disabling an unsupported sensor shouldn't fail. */ return true; @@ -758,27 +753,24 @@ static float sdl3_window_pixel_density(sdl3_input_t *sdl) static float sdl3_get_sensor_input(void *data, unsigned port, unsigned id) { sdl3_input_t *sdl = (sdl3_input_t*)data; + float v[3]; /* The host device's sensors only ever map to port 0. */ if (port != 0) return 0.0f; - switch (id) + /* SDL reports acceleration in m/s^2; libretro expects g. Both + * sides use radians per second for the gyroscope. Reading fails + * once a sensor vanishes, leaving the at-rest 0. */ + if (id <= RETRO_SENSOR_ACCELEROMETER_Z) + { + if (sdl->accel && SDL_GetSensorData(sdl->accel, v, 3)) + return v[id - RETRO_SENSOR_ACCELEROMETER_X] / SDL_STANDARD_GRAVITY; + } + else if (id >= RETRO_SENSOR_GYROSCOPE_X && id <= RETRO_SENSOR_GYROSCOPE_Z) { - /* SDL reports acceleration in m/s^2; libretro expects g. */ - case RETRO_SENSOR_ACCELEROMETER_X: - return sdl->accel_data[0] / SDL_STANDARD_GRAVITY; - case RETRO_SENSOR_ACCELEROMETER_Y: - return sdl->accel_data[1] / SDL_STANDARD_GRAVITY; - case RETRO_SENSOR_ACCELEROMETER_Z: - return sdl->accel_data[2] / SDL_STANDARD_GRAVITY; - /* Both sides use radians per second. */ - case RETRO_SENSOR_GYROSCOPE_X: - return sdl->gyro_data[0]; - case RETRO_SENSOR_GYROSCOPE_Y: - return sdl->gyro_data[1]; - case RETRO_SENSOR_GYROSCOPE_Z: - return sdl->gyro_data[2]; + if (sdl->gyro && SDL_GetSensorData(sdl->gyro, v, 3)) + return v[id - RETRO_SENSOR_GYROSCOPE_X]; } return 0.0f; @@ -1130,17 +1122,9 @@ static void sdl3_input_poll(void *data) sdl3_poll_mouse(sdl); sdl3_poll_touch(sdl); + /* SDL_UpdateSensors works without a focused window. */ if (sdl->accel || sdl->gyro) - { - /* SDL_UpdateSensors works without a focused window. */ SDL_UpdateSensors(); - /* Zero on failure so a vanished sensor reads as at rest - * rather than frozen at its last reported values. */ - if (sdl->accel && !SDL_GetSensorData(sdl->accel, sdl->accel_data, 3)) - memset(sdl->accel_data, 0, sizeof(sdl->accel_data)); - if (sdl->gyro && !SDL_GetSensorData(sdl->gyro, sdl->gyro_data, 3)) - memset(sdl->gyro_data, 0, sizeof(sdl->gyro_data)); - } sdl->mouse_wu = false; sdl->mouse_wd = false; From 6daf593c36561e17203d65e54ae6723c6c1abaa0 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sat, 22 Aug 2026 22:48:32 -0400 Subject: [PATCH 05/11] sdl3: Fix comments --- input/drivers/sdl3_input.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/input/drivers/sdl3_input.c b/input/drivers/sdl3_input.c index bfdef21b9080..2e994bbd0e65 100644 --- a/input/drivers/sdl3_input.c +++ b/input/drivers/sdl3_input.c @@ -759,9 +759,8 @@ static float sdl3_get_sensor_input(void *data, unsigned port, unsigned id) if (port != 0) return 0.0f; - /* SDL reports acceleration in m/s^2; libretro expects g. Both - * sides use radians per second for the gyroscope. Reading fails - * once a sensor vanishes, leaving the at-rest 0. */ + /* Acceleration is m/s^2, though libretro expects gravity. The + * gyroscope uses radians per second. */ if (id <= RETRO_SENSOR_ACCELEROMETER_Z) { if (sdl->accel && SDL_GetSensorData(sdl->accel, v, 3)) From de5566a7cc0d974b84ff36b6f74bc36ec66a34f6 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sun, 23 Aug 2026 00:17:58 -0400 Subject: [PATCH 06/11] sdl3: Fix comment --- input/drivers/sdl3_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input/drivers/sdl3_input.c b/input/drivers/sdl3_input.c index 2e994bbd0e65..c2fd9e4253ec 100644 --- a/input/drivers/sdl3_input.c +++ b/input/drivers/sdl3_input.c @@ -656,7 +656,7 @@ static void sdl3_input_free(void *data) free(sdl); } -/* Opens the first host sensor of the given type, if any. */ +/* Opens the first sensor of the given type. */ static SDL_Sensor *sdl3_open_sensor(SDL_SensorType type) { int i; From 447960e7c246d5b805975e84bdd560178cb539ef Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sun, 23 Aug 2026 19:51:05 -0400 Subject: [PATCH 07/11] SDL3: Log sensors on joypad connect --- input/drivers_joypad/sdl3_joypad.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/input/drivers_joypad/sdl3_joypad.c b/input/drivers_joypad/sdl3_joypad.c index f643392871d8..74b7f5d790cb 100644 --- a/input/drivers_joypad/sdl3_joypad.c +++ b/input/drivers_joypad/sdl3_joypad.c @@ -264,6 +264,17 @@ static void sdl3_joypad_connect(SDL_JoystickID jid) pad->num_buttons = (num_buttons > 0) ? (unsigned)num_buttons : 0; pad->num_hats = (num_hats > 0) ? (unsigned)num_hats : 0; } + + if (gamepad) + { + bool has_accel = SDL_GamepadHasSensor(gamepad, SDL_SENSOR_ACCEL); + bool has_gyro = SDL_GamepadHasSensor(gamepad, SDL_SENSOR_GYRO); + if (has_accel || has_gyro) + RARCH_LOG("[SDL3] Pad #%d: found sensors (accel=%s, gyro=%s).\n", + slot, + has_accel ? "yes" : "no", + has_gyro ? "yes" : "no"); + } } static void sdl3_joypad_disconnect(SDL_JoystickID jid) From 52449e35e99c77638fd222a6110482147d4d7c69 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sun, 23 Aug 2026 22:50:30 -0400 Subject: [PATCH 08/11] SDL3: Restore sensor state on reconnect --- input/drivers_joypad/sdl3_joypad.c | 54 ++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/input/drivers_joypad/sdl3_joypad.c b/input/drivers_joypad/sdl3_joypad.c index 74b7f5d790cb..92149b054c32 100644 --- a/input/drivers_joypad/sdl3_joypad.c +++ b/input/drivers_joypad/sdl3_joypad.c @@ -50,6 +50,16 @@ typedef struct _sdl3_joypad */ static sdl3_joypad_t sdl3_joypads[MAX_USERS]; +/** + * The sensor state requested for each pad. Kept outside of sdl3_joypads + * so it survives a disconnect and can be restored when the pad reconnects. + */ +static struct +{ + bool accel; + bool gyro; +} sdl3_sensors_wanted[MAX_USERS]; + static const char *sdl3_joypad_name(unsigned pad) { if (pad >= MAX_USERS || !sdl3_joypads[pad].jid) @@ -274,6 +284,13 @@ static void sdl3_joypad_connect(SDL_JoystickID jid) slot, has_accel ? "yes" : "no", has_gyro ? "yes" : "no"); + + /* Restore any sensor state that was requested before connecting, + * e.g. when the pad is unplugged and plugged back in. */ + if (has_accel && sdl3_sensors_wanted[slot].accel) + SDL_SetGamepadSensorEnabled(gamepad, SDL_SENSOR_ACCEL, true); + if (has_gyro && sdl3_sensors_wanted[slot].gyro) + SDL_SetGamepadSensorEnabled(gamepad, SDL_SENSOR_GYRO, true); } } @@ -310,6 +327,7 @@ static void sdl3_joypad_destroy(void) } memset(sdl3_joypads, 0, sizeof(sdl3_joypads)); + memset(sdl3_sensors_wanted, 0, sizeof(sdl3_sensors_wanted)); SDL_QuitSubSystem(SDL_INIT_GAMEPAD); } @@ -570,6 +588,9 @@ static bool sdl3_joypad_set_rumble(unsigned pad, /** * Enables or disables a sensor on the specified gamepad. * + * The requested state is remembered so it can be re-applied if the + * pad disconnects and reconnects. + * * @param pad Index of the gamepad. * @param action Sensor action to perform (enable/disable gyroscope or accelerometer). * @param rate Requested sensor update rate (unused). @@ -578,27 +599,40 @@ static bool sdl3_joypad_set_rumble(unsigned pad, static bool sdl3_joypad_set_sensor_state(unsigned pad, enum retro_sensor_action action, unsigned rate) { + SDL_Gamepad *gamepad; + if (pad >= MAX_USERS) return false; - if (!sdl3_joypads[pad].gamepad) - return false; + gamepad = sdl3_joypads[pad].gamepad; switch (action) { case RETRO_SENSOR_GYROSCOPE_ENABLE: case RETRO_SENSOR_GYROSCOPE_DISABLE: - if (SDL_GamepadHasSensor(sdl3_joypads[pad].gamepad, SDL_SENSOR_GYRO)) - return SDL_SetGamepadSensorEnabled(sdl3_joypads[pad].gamepad, SDL_SENSOR_GYRO, - action == RETRO_SENSOR_GYROSCOPE_ENABLE); - return false; + { + bool enable = action == RETRO_SENSOR_GYROSCOPE_ENABLE; + sdl3_sensors_wanted[pad].gyro = enable; + if (gamepad && SDL_GamepadHasSensor(gamepad, SDL_SENSOR_GYRO)) + return SDL_SetGamepadSensorEnabled(gamepad, SDL_SENSOR_GYRO, enable); + /* Disabling a missing sensor shouldn't fail. */ + return !enable; + } case RETRO_SENSOR_ACCELEROMETER_ENABLE: case RETRO_SENSOR_ACCELEROMETER_DISABLE: - if (SDL_GamepadHasSensor(sdl3_joypads[pad].gamepad, SDL_SENSOR_ACCEL)) - return SDL_SetGamepadSensorEnabled(sdl3_joypads[pad].gamepad, SDL_SENSOR_ACCEL, - action == RETRO_SENSOR_ACCELEROMETER_ENABLE); - return false; + { + bool enable = action == RETRO_SENSOR_ACCELEROMETER_ENABLE; + sdl3_sensors_wanted[pad].accel = enable; + if (gamepad && SDL_GamepadHasSensor(gamepad, SDL_SENSOR_ACCEL)) + return SDL_SetGamepadSensorEnabled(gamepad, SDL_SENSOR_ACCEL, enable); + /* Disabling a missing sensor shouldn't fail. */ + return !enable; + } + + case RETRO_SENSOR_ILLUMINANCE_DISABLE: + /* Disabling an unsupported sensor shouldn't fail. */ + return true; default: return false; From fbedf32bac1cc00bd940fdf0bb4169b0f528cb55 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sun, 23 Aug 2026 22:54:47 -0400 Subject: [PATCH 09/11] SDL3: Track sensor state in pad struct --- input/drivers_joypad/sdl3_joypad.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/input/drivers_joypad/sdl3_joypad.c b/input/drivers_joypad/sdl3_joypad.c index 92149b054c32..9436ff7ba412 100644 --- a/input/drivers_joypad/sdl3_joypad.c +++ b/input/drivers_joypad/sdl3_joypad.c @@ -41,6 +41,8 @@ typedef struct _sdl3_joypad unsigned num_hats; uint16_t rumble_gain; /* 0-100 */ uint16_t rumble[2]; /* raw magnitude per retro_rumble_effect (strong/weak) */ + bool sensor_accel; /* Requested sensor state; survives reconnects */ + bool sensor_gyro; } sdl3_joypad_t; /** @@ -50,16 +52,6 @@ typedef struct _sdl3_joypad */ static sdl3_joypad_t sdl3_joypads[MAX_USERS]; -/** - * The sensor state requested for each pad. Kept outside of sdl3_joypads - * so it survives a disconnect and can be restored when the pad reconnects. - */ -static struct -{ - bool accel; - bool gyro; -} sdl3_sensors_wanted[MAX_USERS]; - static const char *sdl3_joypad_name(unsigned pad) { if (pad >= MAX_USERS || !sdl3_joypads[pad].jid) @@ -287,9 +279,9 @@ static void sdl3_joypad_connect(SDL_JoystickID jid) /* Restore any sensor state that was requested before connecting, * e.g. when the pad is unplugged and plugged back in. */ - if (has_accel && sdl3_sensors_wanted[slot].accel) + if (has_accel && pad->sensor_accel) SDL_SetGamepadSensorEnabled(gamepad, SDL_SENSOR_ACCEL, true); - if (has_gyro && sdl3_sensors_wanted[slot].gyro) + if (has_gyro && pad->sensor_gyro) SDL_SetGamepadSensorEnabled(gamepad, SDL_SENSOR_GYRO, true); } } @@ -310,7 +302,14 @@ static void sdl3_joypad_disconnect(SDL_JoystickID jid) input_autoconfigure_disconnect(i, sdl3_joypad.ident); - memset(&sdl3_joypads[i], 0, sizeof(sdl3_joypads[i])); + /* Keep the requested sensor state so it can be restored on reconnect. */ + { + bool sensor_accel = sdl3_joypads[i].sensor_accel; + bool sensor_gyro = sdl3_joypads[i].sensor_gyro; + memset(&sdl3_joypads[i], 0, sizeof(sdl3_joypads[i])); + sdl3_joypads[i].sensor_accel = sensor_accel; + sdl3_joypads[i].sensor_gyro = sensor_gyro; + } return; } } @@ -327,7 +326,6 @@ static void sdl3_joypad_destroy(void) } memset(sdl3_joypads, 0, sizeof(sdl3_joypads)); - memset(sdl3_sensors_wanted, 0, sizeof(sdl3_sensors_wanted)); SDL_QuitSubSystem(SDL_INIT_GAMEPAD); } @@ -612,7 +610,7 @@ static bool sdl3_joypad_set_sensor_state(unsigned pad, case RETRO_SENSOR_GYROSCOPE_DISABLE: { bool enable = action == RETRO_SENSOR_GYROSCOPE_ENABLE; - sdl3_sensors_wanted[pad].gyro = enable; + sdl3_joypads[pad].sensor_gyro = enable; if (gamepad && SDL_GamepadHasSensor(gamepad, SDL_SENSOR_GYRO)) return SDL_SetGamepadSensorEnabled(gamepad, SDL_SENSOR_GYRO, enable); /* Disabling a missing sensor shouldn't fail. */ @@ -623,7 +621,7 @@ static bool sdl3_joypad_set_sensor_state(unsigned pad, case RETRO_SENSOR_ACCELEROMETER_DISABLE: { bool enable = action == RETRO_SENSOR_ACCELEROMETER_ENABLE; - sdl3_sensors_wanted[pad].accel = enable; + sdl3_joypads[pad].sensor_accel = enable; if (gamepad && SDL_GamepadHasSensor(gamepad, SDL_SENSOR_ACCEL)) return SDL_SetGamepadSensorEnabled(gamepad, SDL_SENSOR_ACCEL, enable); /* Disabling a missing sensor shouldn't fail. */ From 0d78c74e5e626fe008f88601b8b8890743730e2f Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sun, 23 Aug 2026 22:59:27 -0400 Subject: [PATCH 10/11] sdl3: Update comment --- input/drivers/sdl3_input.c | 1 - input/drivers_joypad/sdl3_joypad.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/input/drivers/sdl3_input.c b/input/drivers/sdl3_input.c index c2fd9e4253ec..760bdb21443e 100644 --- a/input/drivers/sdl3_input.c +++ b/input/drivers/sdl3_input.c @@ -775,7 +775,6 @@ static float sdl3_get_sensor_input(void *data, unsigned port, unsigned id) return 0.0f; } - static void sdl3_poll_mouse(sdl3_input_t *sdl) { float density; diff --git a/input/drivers_joypad/sdl3_joypad.c b/input/drivers_joypad/sdl3_joypad.c index 9436ff7ba412..ec6c3a9df213 100644 --- a/input/drivers_joypad/sdl3_joypad.c +++ b/input/drivers_joypad/sdl3_joypad.c @@ -41,7 +41,7 @@ typedef struct _sdl3_joypad unsigned num_hats; uint16_t rumble_gain; /* 0-100 */ uint16_t rumble[2]; /* raw magnitude per retro_rumble_effect (strong/weak) */ - bool sensor_accel; /* Requested sensor state; survives reconnects */ + bool sensor_accel; /* Whether or not the sensor has been connected. */ bool sensor_gyro; } sdl3_joypad_t; From 0bc6fd5791558aea0eb29c92016c1a51ac16837b Mon Sep 17 00:00:00 2001 From: LibretroAdmin Date: Sat, 26 Sep 2026 18:49:06 +0000 Subject: [PATCH 11/11] input: route sensor disable requests to both the joypad and input drivers input_driver_set_sensor stops at the first driver that reports the action as handled. That is right for an enable, where the sensor ends up held by exactly one driver, but wrong for a disable: joypad drivers are documented to report the disable of a sensor they never had as a success, so when the host sensor is actually held open by the input driver (e.g. the SDL3 input driver's accelerometer/gyro fallback for a pad with no sensors), the disable never reaches it. The sensor stays open, keeps being polled, and keeps feeding the core after it asked for it to stop. Send disable actions to both drivers. Disabling a sensor a driver does not hold is already defined as a no-op success, so this is safe for every existing driver pair. --- input/input_driver.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/input/input_driver.c b/input/input_driver.c index 863b80e8e0f9..6d9d4891c3fd 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -668,22 +668,30 @@ bool input_driver_set_sensor( enum retro_sensor_action action, unsigned rate) { const input_driver_t *current_driver; - bool enabled = false; + bool enabled = false; + bool is_disable = + (action == RETRO_SENSOR_ACCELEROMETER_DISABLE) + || (action == RETRO_SENSOR_GYROSCOPE_DISABLE) + || (action == RETRO_SENSOR_ILLUMINANCE_DISABLE); if (!input_driver_st.current_data) return false; /* If sensors are disabled, inhibit any enable * actions (but always allow disable actions) */ - if (!sensors_enable - && ((action == RETRO_SENSOR_ACCELEROMETER_ENABLE) - || (action == RETRO_SENSOR_GYROSCOPE_ENABLE) - || (action == RETRO_SENSOR_ILLUMINANCE_ENABLE))) + if (!sensors_enable && !is_disable) return false; 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 + /* An enable stops at the first driver that takes it, so a sensor + * is only ever held by one of them. A disable has to reach both: + * a joypad driver that reports the disable of a sensor it never + * had as a success (as they are documented to) would otherwise + * hide the disable from the input driver that is actually + * holding the host sensor open, leaving it enabled and still + * feeding the core after it asked for it to stop. */ + if ( (!enabled || is_disable) && (current_driver = input_driver_st.current_driver) && current_driver->set_sensor_state) {