From 9a8ac8de2f3227463ead9d10895edee2a750c28b Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Thu, 13 Aug 2026 23:20:16 -0400 Subject: [PATCH 1/7] Add SDL3 device sensor support --- input/drivers/sdl3_input.c | 131 +++++++++++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 6 deletions(-) diff --git a/input/drivers/sdl3_input.c b/input/drivers/sdl3_input.c index f10fbf7cdfc..d971e877702 100644 --- a/input/drivers/sdl3_input.c +++ b/input/drivers/sdl3_input.c @@ -75,6 +75,14 @@ typedef struct sdl3_input float x; float y; } touches[SDL3_MAX_TOUCH]; + + /* 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; /* Rebuilt on SDL_EVENT_KEYMAP_CHANGED (e.g. system layout switch). */ @@ -445,20 +453,88 @@ 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); + SDL_QuitSubSystem(SDL_INIT_EVENTS); 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: @@ -468,6 +544,35 @@ static bool sdl3_set_sensor_state(void *data, unsigned port, return false; } +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; +} + /* Gets the SDL_Window, if it exists. */ static SDL_Window *sdl3_input_window(void) { @@ -676,6 +781,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; @@ -739,9 +855,12 @@ static void sdl3_input_poll(void *data) * all. Both fire at device rate for as long as there's contact, so * left in the queue they grow until SDL's queue fills and starts * refusing pushes - at which point the events that do matter (quit, - * keys) get dropped along with them. */ + * keys) get dropped along with them. Sensor updates likewise arrive + * at device rate and are read by polling above. */ SDL_FlushEvents(SDL_EVENT_FINGER_DOWN, SDL_EVENT_FINGER_CANCELED); SDL_FlushEvents(SDL_EVENT_PEN_PROXIMITY_IN, SDL_EVENT_PEN_AXIS); + if (sdl->sensors_init) + SDL_FlushEvent(SDL_EVENT_SENSOR_UPDATE); } static void sdl3_grab_mouse(void *data, bool state) @@ -773,7 +892,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 066e70a7bc24ba0f601f258b1bd6f5e4b0965043 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Tue, 18 Aug 2026 02:14:31 -0400 Subject: [PATCH 2/7] 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 d971e877702..09567b51b6b 100644 --- a/input/drivers/sdl3_input.c +++ b/input/drivers/sdl3_input.c @@ -495,6 +495,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: @@ -786,10 +790,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 35da56fa72ee292874031621e67220bf0aee92f7 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Tue, 18 Aug 2026 03:00:16 -0400 Subject: [PATCH 3/7] sdl3: Sensor comments --- input/drivers/sdl3_input.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/input/drivers/sdl3_input.c b/input/drivers/sdl3_input.c index 09567b51b6b..14f30d8f6f6 100644 --- a/input/drivers/sdl3_input.c +++ b/input/drivers/sdl3_input.c @@ -76,8 +76,7 @@ typedef struct sdl3_input float y; } touches[SDL3_MAX_TOUCH]; - /* 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]; @@ -468,9 +467,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; @@ -488,8 +487,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) { @@ -504,14 +502,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)) @@ -519,8 +516,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) @@ -787,8 +783,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. */ @@ -861,8 +856,7 @@ static void sdl3_input_poll(void *data) * all. Both fire at device rate for as long as there's contact, so * left in the queue they grow until SDL's queue fills and starts * refusing pushes - at which point the events that do matter (quit, - * keys) get dropped along with them. Sensor updates likewise arrive - * at device rate and are read by polling above. */ + * keys) get dropped along with them. */ SDL_FlushEvents(SDL_EVENT_FINGER_DOWN, SDL_EVENT_FINGER_CANCELED); SDL_FlushEvents(SDL_EVENT_PEN_PROXIMITY_IN, SDL_EVENT_PEN_AXIS); if (sdl->sensors_init) From 19e866ca75624251363f4690886ef688654c697c Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Tue, 18 Aug 2026 12:47:51 -0400 Subject: [PATCH 4/7] 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 14f30d8f6f6..619eb8c137f 100644 --- a/input/drivers/sdl3_input.c +++ b/input/drivers/sdl3_input.c @@ -79,8 +79,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; @@ -519,21 +517,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; @@ -547,27 +542,24 @@ static bool sdl3_set_sensor_state(void *data, unsigned port, 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; @@ -781,17 +773,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 7d704cd6a13577b54ea64b0f4a66ae5f76fd739e Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sat, 22 Aug 2026 22:48:32 -0400 Subject: [PATCH 5/7] 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 619eb8c137f..295cbc72d43 100644 --- a/input/drivers/sdl3_input.c +++ b/input/drivers/sdl3_input.c @@ -548,9 +548,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 13ba7260d20ba0c1e3766864261a70c0aa1d0fa8 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sun, 23 Aug 2026 00:17:58 -0400 Subject: [PATCH 6/7] 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 295cbc72d43..57c6a49ba2e 100644 --- a/input/drivers/sdl3_input.c +++ b/input/drivers/sdl3_input.c @@ -461,7 +461,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 4b398d01f21d9a287a94ae91ea15bcf5d815626a Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sun, 23 Aug 2026 19:51:05 -0400 Subject: [PATCH 7/7] 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 cb3d6585647..c7fc23844cf 100644 --- a/input/drivers_joypad/sdl3_joypad.c +++ b/input/drivers_joypad/sdl3_joypad.c @@ -252,6 +252,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)