From 4f3d7017b0c380f06cce5febfd5f79f2be6a0ce8 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Thu, 27 Aug 2026 10:18:03 -0400
Subject: [PATCH] joystick: Expose sensor data at the SDL_Joystick level.
Previously this was only available through the gamepad API, but this state has
always actually lived on the lower-level joystick objects, so there's no sense
in not offering a public API for that level, as well.
Closes #16202.
---
include/SDL3/SDL_joystick.h | 87 +++++++++++++++
src/dynapi/SDL_dynapi.exports | 5 +
src/dynapi/SDL_dynapi.sym | 5 +
src/dynapi/SDL_dynapi_overrides.h | 5 +
src/dynapi/SDL_dynapi_procs.h | 5 +
src/joystick/SDL_gamepad.c | 166 +----------------------------
src/joystick/SDL_joystick.c | 171 ++++++++++++++++++++++++++++++
7 files changed, 283 insertions(+), 161 deletions(-)
diff --git a/include/SDL3/SDL_joystick.h b/include/SDL3/SDL_joystick.h
index e3bf5988c27ff..be9a8817346da 100644
--- a/include/SDL3/SDL_joystick.h
+++ b/include/SDL3/SDL_joystick.h
@@ -1250,6 +1250,93 @@ extern SDL_DECLSPEC Uint8 SDLCALL SDL_GetJoystickHat(SDL_Joystick *joystick, int
*/
extern SDL_DECLSPEC bool SDLCALL SDL_GetJoystickButton(SDL_Joystick *joystick, int button);
+/**
+ * Return whether a joystick has a particular sensor.
+ *
+ * Sensors are disabled by default and SDL_SetJoystickSensorEnabled() is used
+ * to enable them.
+ *
+ * \param joystick the joystick to query.
+ * \param type the type of sensor to query.
+ * \returns true if the sensor exists, false otherwise.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.2.0.
+ *
+ * \sa SDL_GetJoystickSensorData
+ * \sa SDL_GetJoystickSensorDataRate
+ * \sa SDL_SetJoystickSensorEnabled
+ */
+extern SDL_DECLSPEC bool SDLCALL SDL_JoystickHasSensor(SDL_Joystick *joystick, SDL_SensorType type);
+
+/**
+ * Set whether data reporting for a joystick sensor is enabled.
+ *
+ * Sensors are disabled by default and this function is used to enable them.
+ *
+ * \param joystick the joystick to update.
+ * \param type the type of sensor to enable/disable.
+ * \param enabled whether data reporting should be enabled.
+ * \returns true on success or false on failure; call SDL_GetError() for more
+ * information.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.2.0.
+ *
+ * \sa SDL_JoystickHasSensor
+ * \sa SDL_JoystickSensorEnabled
+ */
+extern SDL_DECLSPEC bool SDLCALL SDL_SetJoystickSensorEnabled(SDL_Joystick *joystick, SDL_SensorType type, bool enabled);
+
+/**
+ * Query whether sensor data reporting is enabled for a joystick.
+ *
+ * \param joystick the joystick to query.
+ * \param type the type of sensor to query.
+ * \returns true if the sensor is enabled, false otherwise.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.2.0.
+ *
+ * \sa SDL_SetJoystickSensorEnabled
+ */
+extern SDL_DECLSPEC bool SDLCALL SDL_JoystickSensorEnabled(SDL_Joystick *joystick, SDL_SensorType type);
+
+/**
+ * Get the data rate (number of events per second) of a joystick sensor.
+ *
+ * \param joystick the joystick to query.
+ * \param type the type of sensor to query.
+ * \returns the data rate, or 0.0f if the data rate is not available.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.2.0.
+ */
+extern SDL_DECLSPEC float SDLCALL SDL_GetJoystickSensorDataRate(SDL_Joystick *joystick, SDL_SensorType type);
+
+/**
+ * Get the current state of a joystick sensor.
+ *
+ * The number of values and interpretation of the data is sensor dependent.
+ * See the remarks in SDL_SensorType for details for each type of sensor.
+ *
+ * \param joystick the joystick to query.
+ * \param type the type of sensor to query.
+ * \param data a pointer filled with the current sensor state.
+ * \param num_values the number of values to write to data.
+ * \returns true on success or false on failure; call SDL_GetError() for more
+ * information.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.2.0.
+ */
+extern SDL_DECLSPEC bool SDLCALL SDL_GetJoystickSensorData(SDL_Joystick *joystick, SDL_SensorType type, float *data, int num_values);
+
/**
* Start a rumble effect.
*
diff --git a/src/dynapi/SDL_dynapi.exports b/src/dynapi/SDL_dynapi.exports
index f0c685c4be7d5..a06f0f21d6434 100644
--- a/src/dynapi/SDL_dynapi.exports
+++ b/src/dynapi/SDL_dynapi.exports
@@ -1302,3 +1302,8 @@ _SDL_GetDeviceFormFactor
_SDL_GetDeviceFormFactorName
_SDL_IsUbuntuTouch
_SDL_GetNumProperties
+_SDL_JoystickHasSensor
+_SDL_SetJoystickSensorEnabled
+_SDL_JoystickSensorEnabled
+_SDL_GetJoystickSensorDataRate
+_SDL_GetJoystickSensorData
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index f30ea16eb5c00..6e0354148122d 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -1303,6 +1303,11 @@ SDL3_0.0.0 {
SDL_GetDeviceFormFactorName;
SDL_IsUbuntuTouch;
SDL_GetNumProperties;
+ SDL_JoystickHasSensor;
+ SDL_SetJoystickSensorEnabled;
+ SDL_JoystickSensorEnabled;
+ SDL_GetJoystickSensorDataRate;
+ SDL_GetJoystickSensorData;
# extra symbols go here (don't modify this line)
local: *;
};
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index c7ce948a96c86..1f8ba18a01e59 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -1329,3 +1329,8 @@
#define SDL_GetDeviceFormFactorName SDL_GetDeviceFormFactorName_REAL
#define SDL_IsUbuntuTouch SDL_IsUbuntuTouch_REAL
#define SDL_GetNumProperties SDL_GetNumProperties_REAL
+#define SDL_JoystickHasSensor SDL_JoystickHasSensor_REAL
+#define SDL_SetJoystickSensorEnabled SDL_SetJoystickSensorEnabled_REAL
+#define SDL_JoystickSensorEnabled SDL_JoystickSensorEnabled_REAL
+#define SDL_GetJoystickSensorDataRate SDL_GetJoystickSensorDataRate_REAL
+#define SDL_GetJoystickSensorData SDL_GetJoystickSensorData_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 299f77f2e89cd..6bf988441a21b 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -1337,3 +1337,8 @@ SDL_DYNAPI_PROC(SDL_FormFactor,SDL_GetDeviceFormFactor,(void),(),return)
SDL_DYNAPI_PROC(const char*,SDL_GetDeviceFormFactorName,(SDL_FormFactor a),(a),return)
SDL_DYNAPI_PROC(bool,SDL_IsUbuntuTouch,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetNumProperties,(SDL_PropertiesID a),(a),return)
+SDL_DYNAPI_PROC(bool,SDL_JoystickHasSensor,(SDL_Joystick *a,SDL_SensorType b),(a,b),return)
+SDL_DYNAPI_PROC(bool,SDL_SetJoystickSensorEnabled,(SDL_Joystick *a,SDL_SensorType b,bool c),(a,b,c),return)
+SDL_DYNAPI_PROC(bool,SDL_JoystickSensorEnabled,(SDL_Joystick *a,SDL_SensorType b),(a,b),return)
+SDL_DYNAPI_PROC(float,SDL_GetJoystickSensorDataRate,(SDL_Joystick *a,SDL_SensorType b),(a,b),return)
+SDL_DYNAPI_PROC(bool,SDL_GetJoystickSensorData,(SDL_Joystick *a,SDL_SensorType b,float *c,int d),(a,b,c,d),return)
diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c
index afcf6c5d1800e..5065e277266dd 100644
--- a/src/joystick/SDL_gamepad.c
+++ b/src/joystick/SDL_gamepad.c
@@ -3780,185 +3780,29 @@ bool SDL_GetGamepadTouchpadFinger(SDL_Gamepad *gamepad, int touchpad, int finger
return result;
}
-/**
- * Return whether a gamepad has a particular sensor.
- */
bool SDL_GamepadHasSensor(SDL_Gamepad *gamepad, SDL_SensorType type)
{
- bool result = false;
-
- SDL_LockJoysticks();
- {
- SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
- if (joystick) {
- int i;
- for (i = 0; i < joystick->nsensors; ++i) {
- if (joystick->sensors[i].type == type) {
- result = true;
- break;
- }
- }
- }
- }
- SDL_UnlockJoysticks();
-
- return result;
+ return SDL_JoystickHasSensor(SDL_GetGamepadJoystick(gamepad), type);
}
-/*
- * Set whether data reporting for a gamepad sensor is enabled
- */
bool SDL_SetGamepadSensorEnabled(SDL_Gamepad *gamepad, SDL_SensorType type, bool enabled)
{
- SDL_LockJoysticks();
- {
- SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
- if (joystick) {
- int i;
- for (i = 0; i < joystick->nsensors; ++i) {
- SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];
-
- if (sensor->type == type) {
- if (sensor->enabled == (enabled != false)) {
- SDL_UnlockJoysticks();
- return true;
- }
-
- if (type == SDL_SENSOR_ACCEL && joystick->accel_sensor) {
- if (enabled) {
- joystick->accel = SDL_OpenSensor(joystick->accel_sensor);
- if (!joystick->accel) {
- SDL_UnlockJoysticks();
- return false;
- }
- } else {
- if (joystick->accel) {
- SDL_CloseSensor(joystick->accel);
- joystick->accel = NULL;
- }
- }
- } else if (type == SDL_SENSOR_GYRO && joystick->gyro_sensor) {
- if (enabled) {
- joystick->gyro = SDL_OpenSensor(joystick->gyro_sensor);
- if (!joystick->gyro) {
- SDL_UnlockJoysticks();
- return false;
- }
- } else {
- if (joystick->gyro) {
- SDL_CloseSensor(joystick->gyro);
- joystick->gyro = NULL;
- }
- }
- } else {
- if (enabled) {
- if (joystick->nsensors_enabled == 0) {
- if (!joystick->driver->SetSensorsEnabled(joystick, true)) {
- SDL_UnlockJoysticks();
- return false;
- }
- }
- ++joystick->nsensors_enabled;
- } else {
- if (joystick->nsensors_enabled == 1) {
- if (!joystick->driver->SetSensorsEnabled(joystick, false)) {
- SDL_UnlockJoysticks();
- return false;
- }
- }
- --joystick->nsensors_enabled;
- }
- }
-
- sensor->enabled = enabled;
- SDL_UnlockJoysticks();
- return true;
- }
- }
- }
- }
- SDL_UnlockJoysticks();
-
- return SDL_Unsupported();
+ return SDL_SetJoystickSensorEnabled(SDL_GetGamepadJoystick(gamepad), type, enabled);
}
-/*
- * Query whether sensor data reporting is enabled for a gamepad
- */
bool SDL_GamepadSensorEnabled(SDL_Gamepad *gamepad, SDL_SensorType type)
{
- bool result = false;
-
- SDL_LockJoysticks();
- {
- SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
- if (joystick) {
- int i;
- for (i = 0; i < joystick->nsensors; ++i) {
- if (joystick->sensors[i].type == type) {
- result = joystick->sensors[i].enabled;
- break;
- }
- }
- }
- }
- SDL_UnlockJoysticks();
-
- return result;
+ return SDL_JoystickSensorEnabled(SDL_GetGamepadJoystick(gamepad), type);
}
-/*
- * Get the data rate of a gamepad sensor.
- */
float SDL_GetGamepadSensorDataRate(SDL_Gamepad *gamepad, SDL_SensorType type)
{
- float result = 0.0f;
-
- SDL_LockJoysticks();
- {
- SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
- if (joystick) {
- int i;
- for (i = 0; i < joystick->nsensors; ++i) {
- SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];
-
- if (sensor->type == type) {
- result = sensor->rate;
- break;
- }
- }
- }
- }
- SDL_UnlockJoysticks();
-
- return result;
+ return SDL_GetJoystickSensorDataRate(SDL_GetGamepadJoystick(gamepad), type);
}
-/*
- * Get the current state of a gamepad sensor.
- */
bool SDL_GetGamepadSensorData(SDL_Gamepad *gamepad, SDL_SensorType type, float *data, int num_values)
{
- SDL_LockJoysticks();
- {
- SDL_Joystick *joystick = SDL_GetGamepadJoystick(gamepad);
- if (joystick) {
- int i;
- for (i = 0; i < joystick->nsensors; ++i) {
- SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];
-
- if (sensor->type == type) {
- num_values = SDL_min(num_values, SDL_arraysize(sensor->data));
- SDL_memcpy(data, sensor->data, num_values * sizeof(*data));
- SDL_UnlockJoysticks();
- return true;
- }
- }
- }
- }
- SDL_UnlockJoysticks();
-
- return SDL_Unsupported();
+ return SDL_GetJoystickSensorData(SDL_GetGamepadJoystick(gamepad), type, data, num_values);
}
bool SDL_GamepadHasCapSense(SDL_Gamepad *gamepad, SDL_GamepadCapSenseType type)
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index af4211dcd5f9c..4fbe70b0abc94 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -1889,6 +1889,177 @@ bool SDL_GetJoystickButton(SDL_Joystick *joystick, int button)
return down;
}
+static bool ErrorNoSuchSensor(void)
+{
+ return SDL_SetError("No such sensor on this device");
+}
+
+/**
+ * Return whether a joystick has a particular sensor.
+ */
+bool SDL_JoystickHasSensor(SDL_Joystick *joystick, SDL_SensorType type)
+{
+ bool result = false;
+
+ SDL_LockJoysticks();
+ {
+ CHECK_JOYSTICK_MAGIC(joystick, false);
+ for (int i = 0; i < joystick->nsensors; ++i) {
+ if (joystick->sensors[i].type == type) {
+ result = true;
+ break;
+ }
+ }
+ }
+ SDL_UnlockJoysticks();
+
+ return result;
+}
+
+/*
+ * Set whether data reporting for a joystick sensor is enabled
+ */
+bool SDL_SetJoystickSensorEnabled(SDL_Joystick *joystick, SDL_SensorType type, bool enabled)
+{
+ SDL_LockJoysticks();
+ {
+ CHECK_JOYSTICK_MAGIC(joystick, false);
+ for (int i = 0; i < joystick->nsensors; ++i) {
+ SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];
+
+ if (sensor->type == type) {
+ if (sensor->enabled == (enabled != false)) {
+ SDL_UnlockJoysticks();
+ return true;
+ }
+
+ if (type == SDL_SENSOR_ACCEL && joystick->accel_sensor) {
+ if (enabled) {
+ joystick->accel = SDL_OpenSensor(joystick->accel_sensor);
+ if (!joystick->accel) {
+ SDL_UnlockJoysticks();
+ return false;
+ }
+ } else {
+ if (joystick->accel) {
+ SDL_CloseSensor(joystick->accel);
+ joystick->accel = NULL;
+ }
+ }
+ } else if (type == SDL_SENSOR_GYRO && joystick->gyro_sensor) {
+ if (enabled) {
+ joystick->gyro = SDL_OpenSensor(joystick->gyro_sensor);
+ if (!joystick->gyro) {
+ SDL_UnlockJoysticks();
+ return false;
+ }
+ } else {
+ if (joystick->gyro) {
+ SDL_CloseSensor(joystick->gyro);
+ joystick->gyro = NULL;
+ }
+ }
+ } else {
+ if (enabled) {
+ if (joystick->nsensors_enabled == 0) {
+ if (!joystick->driver->SetSensorsEnabled(joystick, true)) {
+ SDL_UnlockJoysticks();
+ return false;
+ }
+ }
+ ++joystick->nsensors_enabled;
+ } else {
+ if (joystick->nsensors_enabled == 1) {
+ if (!joystick->driver->SetSensorsEnabled(joystick, false)) {
+ SDL_UnlockJoysticks();
+ return false;
+ }
+ }
+ --joystick->nsensors_enabled;
+ }
+ }
+
+ sensor->enabled = enabled;
+ SDL_UnlockJoysticks();
+ return true;
+ }
+ }
+ }
+ SDL_UnlockJoysticks();
+
+ return ErrorNoSuchSensor();
+}
+
+/*
+ * Query whether sensor data reporting is enabled for a joystick
+ */
+bool SDL_JoystickSensorEnabled(SDL_Joystick *joystick, SDL_SensorType type)
+{
+ bool result = false;
+
+ SDL_LockJoysticks();
+ {
+ CHECK_JOYSTICK_MAGIC(joystick, false);
+ for (int i = 0; i < joystick->nsensors; ++i) {
+ if (joystick->sensors[i].type == type) {
+ result = joystick->sensors[i].enabled;
+ break;
+ }
+ }
+ }
+ SDL_UnlockJoysticks();
+
+ return result;
+}
+
+/*
+ * Get the data rate of a joystick sensor.
+ */
+float SDL_GetJoystickSensorDataRate(SDL_Joystick *joystick, SDL_SensorType type)
+{
+ float result = 0.0f;
+
+ SDL_LockJoysticks();
+ {
+ CHECK_JOYSTICK_MAGIC(joystick, 0.0f);
+ for (int i = 0; i < joystick->nsensors; ++i) {
+ SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];
+
+ if (sensor->type == type) {
+ result = sensor->rate;
+ break;
+ }
+ }
+ }
+ SDL_UnlockJoysticks();
+
+ return result;
+}
+
+/*
+ * Get the current state of a joystick sensor.
+ */
+bool SDL_GetJoystickSensorData(SDL_Joystick *joystick, SDL_SensorType type, float *data, int num_values)
+{
+ SDL_LockJoysticks();
+ {
+ CHECK_JOYSTICK_MAGIC(joystick, false);
+ for (int i = 0; i < joystick->nsensors; ++i) {
+ SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];
+
+ if (sensor->type == type) {
+ num_values = SDL_min(num_values, SDL_arraysize(sensor->data));
+ SDL_memcpy(data, sensor->data, num_values * sizeof(*data));
+ SDL_UnlockJoysticks();
+ return true;
+ }
+ }
+ }
+ SDL_UnlockJoysticks();
+
+ return ErrorNoSuchSensor();
+}
+
/*
* Return if the joystick in question is currently attached to the system,
* \return false if not plugged in, true if still present.