First try at sensor API implementation

This commit is contained in:
Mikaël Fourrier
2021-08-21 13:55:56 +02:00
parent 70aa98b12f
commit 67fe292f19
7 changed files with 382 additions and 0 deletions
+3
View File
@@ -140,6 +140,9 @@ name = "renderer-yuv"
required-features = ["ttf", "image"]
name = "resource-manager"
[[example]]
name = "sensors"
[[example]]
required-features = ["ttf"]
name = "ttf-demo"
+89
View File
@@ -0,0 +1,89 @@
use std::time::{Duration, Instant};
use sdl2::{event::Event, sensor::SensorType};
extern crate sdl2;
fn main() -> Result<(), String> {
let sdl_context = sdl2::init()?;
let game_controller_subsystem = sdl_context.game_controller()?;
let available = game_controller_subsystem
.num_joysticks()
.map_err(|e| format!("can't enumerate joysticks: {}", e))?;
println!("{} joysticks available", available);
// Iterate over all available joysticks and look for game controllers.
let controller = (0..available)
.find_map(|id| {
if !game_controller_subsystem.is_game_controller(id) {
println!("{} is not a game controller", id);
return None;
}
println!("Attempting to open controller {}", id);
match game_controller_subsystem.open(id) {
Ok(c) => {
// We managed to find and open a game controller,
// exit the loop
println!("Success: opened \"{}\"", c.name());
Some(c)
}
Err(e) => {
println!("failed: {:?}", e);
None
}
}
})
.expect("Couldn't open any controller");
if !controller.has_sensor(SensorType::Accelerometer) {
return Err(format!(
"{} doesn't support the accelerometer",
controller.name()
));
}
if !controller.has_sensor(SensorType::Gyroscope) {
return Err(format!(
"{} doesn't support the gyroscope",
controller.name()
));
}
controller
.sensor_set_enabled(SensorType::Accelerometer, true)
.map_err(|e| format!("error enabling accelerometer: {}", e))?;
controller
.sensor_set_enabled(SensorType::Gyroscope, true)
.map_err(|e| format!("error enabling gyroscope: {}", e))?;
let mut now = Instant::now();
for event in sdl_context.event_pump()?.wait_iter() {
if false && now.elapsed() > Duration::from_secs(1) {
now = Instant::now();
let mut gyro_data = [0f32; 3];
let mut accel_data = [0f32; 3];
controller
.sensor_get_data(SensorType::Gyroscope, &mut gyro_data)
.map_err(|e| format!("error getting gyro data: {}", e))?;
controller
.sensor_get_data(SensorType::Accelerometer, &mut accel_data)
.map_err(|e| format!("error getting accel data: {}", e))?;
println!("gyro: {:?}, accel: {:?}", gyro_data, accel_data);
}
if let Event::ControllerSensorUpdated { .. } = event {
println!("{:?}", event);
}
if let Event::Quit { .. } = event {
break;
}
}
Ok(())
}
+77
View File
@@ -1,10 +1,13 @@
use crate::rwops::RWops;
use crate::sensor::SensorType;
use libc::c_char;
use std::convert::TryInto;
use std::error;
use std::ffi::{CStr, CString, NulError};
use std::fmt;
use std::io;
use std::path::Path;
use sys::SDL_bool;
use crate::common::{validate_int, IntegerOrSdlError};
use crate::get_error;
@@ -486,6 +489,80 @@ impl GameController {
}
}
impl GameController {
#[doc(alias = "SDL_GameControllerHasSensor")]
pub fn has_sensor(&self, sensor_type: SensorType) -> bool {
let result = unsafe { sys::SDL_GameControllerHasSensor(self.raw, sensor_type.into()) };
match result {
sys::SDL_bool::SDL_FALSE => false,
sys::SDL_bool::SDL_TRUE => true,
}
}
#[doc(alias = "SDL_GameControllerIsSensorEnabled")]
pub fn sensor_enabled(&self, sensor_type: SensorType) -> bool {
let result =
unsafe { sys::SDL_GameControllerIsSensorEnabled(self.raw, sensor_type.into()) };
match result {
sys::SDL_bool::SDL_FALSE => false,
sys::SDL_bool::SDL_TRUE => true,
}
}
#[doc(alias = "SDL_GameControllerHasSensor")]
pub fn sensor_set_enabled(
&self,
sensor_type: SensorType,
enabled: bool,
) -> Result<(), IntegerOrSdlError> {
let result = unsafe {
sys::SDL_GameControllerSetSensorEnabled(
self.raw,
sensor_type.into(),
if enabled {
SDL_bool::SDL_TRUE
} else {
SDL_bool::SDL_FALSE
},
)
};
if result != 0 {
Err(IntegerOrSdlError::SdlError(get_error()))
} else {
Ok(())
}
}
/// Get data from a sensor.
///
/// The number of data points depends on the sensor. Both Gyroscope and
/// Accelerometer return 3 values, one for each axis.
#[doc(alias = "SDL_GameControllerGetSensorData")]
pub fn sensor_get_data(
&self,
sensor_type: SensorType,
data: &mut [f32],
) -> Result<(), IntegerOrSdlError> {
let result = unsafe {
sys::SDL_GameControllerGetSensorData(
self.raw,
sensor_type.into(),
data.as_mut_ptr(),
data.len().try_into().unwrap(),
)
};
if result != 0 {
Err(IntegerOrSdlError::SdlError(get_error()))
} else {
Ok(())
}
}
}
impl Drop for GameController {
#[doc(alias = "SDL_GameControllerClose")]
fn drop(&mut self) {
+25
View File
@@ -298,6 +298,7 @@ pub enum EventType {
ControllerDeviceAdded = SDL_EventType::SDL_CONTROLLERDEVICEADDED as u32,
ControllerDeviceRemoved = SDL_EventType::SDL_CONTROLLERDEVICEREMOVED as u32,
ControllerDeviceRemapped = SDL_EventType::SDL_CONTROLLERDEVICEREMAPPED as u32,
ControllerSensorUpdated = SDL_EventType::SDL_CONTROLLERSENSORUPDATE as u32,
FingerDown = SDL_EventType::SDL_FINGERDOWN as u32,
FingerUp = SDL_EventType::SDL_FINGERUP as u32,
@@ -366,6 +367,7 @@ impl TryFrom<u32> for EventType {
SDL_CONTROLLERDEVICEADDED => ControllerDeviceAdded,
SDL_CONTROLLERDEVICEREMOVED => ControllerDeviceRemoved,
SDL_CONTROLLERDEVICEREMAPPED => ControllerDeviceRemapped,
SDL_CONTROLLERSENSORUPDATE => ControllerSensorUpdated,
SDL_FINGERDOWN => FingerDown,
SDL_FINGERUP => FingerUp,
@@ -674,6 +676,18 @@ pub enum Event {
which: u32,
},
/// Triggered when the gyroscope or accelerometer is updated
ControllerSensorUpdated {
timestamp: u32,
which: u32,
/// The type of the sensor, see SensorType.
sensor: i32,
/// Data from the sensor.
///
/// See the `sensor` module for more information.
data: [f32; 3],
},
FingerDown {
timestamp: u32,
touch_id: i64,
@@ -1612,6 +1626,15 @@ impl Event {
which: event.which as u32,
}
}
EventType::ControllerSensorUpdated => {
let event = raw.csensor;
Event::ControllerSensorUpdated {
timestamp: event.timestamp,
which: event.which as u32,
sensor: event.sensor,
data: event.data,
}
}
EventType::FingerDown => {
let event = raw.tfinger;
@@ -1881,6 +1904,7 @@ impl Event {
| (Self::ControllerDeviceAdded { .. }, Self::ControllerDeviceAdded { .. })
| (Self::ControllerDeviceRemoved { .. }, Self::ControllerDeviceRemoved { .. })
| (Self::ControllerDeviceRemapped { .. }, Self::ControllerDeviceRemapped { .. })
| (Self::ControllerSensorUpdated { .. }, Self::ControllerSensorUpdated { .. })
| (Self::FingerDown { .. }, Self::FingerDown { .. })
| (Self::FingerUp { .. }, Self::FingerUp { .. })
| (Self::FingerMotion { .. }, Self::FingerMotion { .. })
@@ -1947,6 +1971,7 @@ impl Event {
Self::ControllerDeviceAdded { timestamp, .. } => timestamp,
Self::ControllerDeviceRemoved { timestamp, .. } => timestamp,
Self::ControllerDeviceRemapped { timestamp, .. } => timestamp,
Self::ControllerSensorUpdated { timestamp, .. } => timestamp,
Self::FingerDown { timestamp, .. } => timestamp,
Self::FingerUp { timestamp, .. } => timestamp,
Self::FingerMotion { timestamp, .. } => timestamp,
+1
View File
@@ -77,6 +77,7 @@ pub mod joystick;
pub mod keyboard;
pub mod log;
pub mod messagebox;
pub mod sensor;
pub mod mouse;
pub mod pixels;
pub mod rect;
+7
View File
@@ -121,6 +121,12 @@ impl Sdl {
GameControllerSubsystem::new(self)
}
/// Initializes the game controller subsystem.
#[inline]
pub fn sensor(&self) -> Result<SensorSubsystem, String> {
SensorSubsystem::new(self)
}
/// Initializes the timer subsystem.
#[inline]
pub fn timer(&self) -> Result<TimerSubsystem, String> {
@@ -278,6 +284,7 @@ subsystem!(VideoSubsystem, sys::SDL_INIT_VIDEO, nosync);
subsystem!(TimerSubsystem, sys::SDL_INIT_TIMER, sync);
// The event queue can be read from other threads.
subsystem!(EventSubsystem, sys::SDL_INIT_EVENTS, sync);
subsystem!(SensorSubsystem, sys::SDL_INIT_SENSOR, sync);
static mut IS_EVENT_PUMP_ALIVE: bool = false;
+180
View File
@@ -0,0 +1,180 @@
/// Access to gyroscope and accelerometer on the controller.
///
/// Compatible controllers including Playstation, Switch and Steam controllers include a gyroscope
/// and accelerometer to get the movement in space of the device.
///
/// Units used by SDL:
/// - Accelerometer is in m/s²
/// - Gyroscope is in radian per second
///
/// Axis when holding the controller:
/// - -x ... +x is left ... right
/// - -y ... +y is down ... up
/// - -z ... +z is forward ... backward
///
/// Rotations uses the standard anti-clockwise direction around the corresponding axis from above:
/// - -x ... +x is pitch towards up
/// - -y ... +y is yaw from right to left
/// - -z ... +z is roll from right to left
use crate::sys;
use crate::common::{validate_int, IntegerOrSdlError};
use crate::get_error;
use crate::SensorSubsystem;
use libc::c_char;
use std::ffi::CStr;
use sys::SDL_SensorGetData;
use sys::SDL_SensorType;
impl SensorSubsystem {
/// Retrieve the total number of attached joysticks *and* controllers identified by SDL.
#[doc(alias = "SDL_NumSensors")]
pub fn num_sensors(&self) -> Result<u32, String> {
let result = unsafe { sys::SDL_NumSensors() };
if result >= 0 {
Ok(result as u32)
} else {
Err(get_error())
}
}
/// Attempt to open the joystick at index `joystick_index` and return it.
#[doc(alias = "SDL_SensorOpen")]
pub fn open(&self, sensor_index: u32) -> Result<Sensor, IntegerOrSdlError> {
use crate::common::IntegerOrSdlError::*;
let sensor_index = validate_int(sensor_index, "sensor_index")?;
let sensor = unsafe { sys::SDL_SensorOpen(sensor_index) };
if sensor.is_null() {
Err(SdlError(get_error()))
} else {
Ok(Sensor {
subsystem: self.clone(),
raw: sensor,
})
}
}
/// Force joystick update when not using the event loop
#[inline]
#[doc(alias = "SDL_SensorUpdate")]
pub fn update(&self) {
unsafe { sys::SDL_SensorUpdate() };
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SensorType {
Unknown,
Gyroscope,
Accelerometer,
}
impl Into<SDL_SensorType> for SensorType {
fn into(self) -> SDL_SensorType {
match self {
SensorType::Unknown => SDL_SensorType::SDL_SENSOR_UNKNOWN,
SensorType::Gyroscope => SDL_SensorType::SDL_SENSOR_GYRO,
SensorType::Accelerometer => SDL_SensorType::SDL_SENSOR_ACCEL,
}
}
}
/// Wrapper around the `SDL_Sensor` object
pub struct Sensor {
subsystem: SensorSubsystem,
raw: *mut sys::SDL_Sensor,
}
impl Sensor {
#[inline]
pub const fn subsystem(&self) -> &SensorSubsystem {
&self.subsystem
}
/// Return the name of the sensor or an empty string if no name
/// is found.
#[doc(alias = "SDL_SensorGetName")]
pub fn name(&self) -> String {
let name = unsafe { sys::SDL_SensorGetName(self.raw) };
c_str_to_string(name)
}
#[doc(alias = "SDL_SensorGetInstanceID")]
pub fn instance_id(&self) -> u32 {
let result = unsafe { sys::SDL_SensorGetInstanceID(self.raw) };
if result < 0 {
// Should only fail if the joystick is NULL.
panic!("{}", get_error())
} else {
result as u32
}
}
/// Return the type of the sensor or `Unknown` if unsupported.
#[doc(alias = "SDL_SensorGetType")]
pub fn sensor_type(&self) -> SensorType {
let result = unsafe { sys::SDL_SensorGetType(self.raw) };
match result {
sys::SDL_SensorType::SDL_SENSOR_INVALID => {
panic!("{}", get_error())
}
sys::SDL_SensorType::SDL_SENSOR_UNKNOWN => SensorType::Unknown,
sys::SDL_SensorType::SDL_SENSOR_ACCEL => SensorType::Accelerometer,
sys::SDL_SensorType::SDL_SENSOR_GYRO => SensorType::Gyroscope,
}
}
/// Get the current data from the sensor.
///
/// Output depends on the type of the sensor. See module documentation for units and axis.
#[doc(alias = "SDL_SensorGetType")]
pub fn get_data(&self) -> Result<SensorData, IntegerOrSdlError> {
let mut data = [0f32; 16];
let result = unsafe { SDL_SensorGetData(self.raw, data.as_mut_ptr(), data.len() as i32) };
if result != 0 {
Err(IntegerOrSdlError::SdlError(get_error()))
} else {
Ok(match self.sensor_type() {
SensorType::Gyroscope => SensorData::Accel([data[0], data[1], data[2]]),
SensorType::Accelerometer => SensorData::Accel([data[0], data[1], data[2]]),
SensorType::Unknown => SensorData::Unknown(data),
})
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum SensorData {
Gyro([f32; 3]),
Accel([f32; 3]),
Unknown([f32; 16]),
}
impl Drop for Sensor {
#[doc(alias = "SDL_SensorClose")]
fn drop(&mut self) {
unsafe { sys::SDL_SensorClose(self.raw) }
}
}
/// Convert C string `c_str` to a String. Return an empty string if
/// `c_str` is NULL.
fn c_str_to_string(c_str: *const c_char) -> String {
if c_str.is_null() {
String::new()
} else {
unsafe {
CStr::from_ptr(c_str as *const _)
.to_str()
.unwrap()
.to_owned()
}
}
}