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.
This commit is contained in:
Ryan C. Gordon
2026-08-27 10:18:03 -04:00
parent 4f477c807c
commit 4f3d7017b0
7 changed files with 283 additions and 161 deletions

View File

@@ -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.
*

View File

@@ -1302,3 +1302,8 @@ _SDL_GetDeviceFormFactor
_SDL_GetDeviceFormFactorName
_SDL_IsUbuntuTouch
_SDL_GetNumProperties
_SDL_JoystickHasSensor
_SDL_SetJoystickSensorEnabled
_SDL_JoystickSensorEnabled
_SDL_GetJoystickSensorDataRate
_SDL_GetJoystickSensorData

View File

@@ -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: *;
};

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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.