mirror of
https://github.com/encounter/rust-sdl2.git
synced 2026-07-10 21:18:41 -07:00
First try at sensor API implementation
This commit is contained in:
@@ -140,6 +140,9 @@ name = "renderer-yuv"
|
||||
required-features = ["ttf", "image"]
|
||||
name = "resource-manager"
|
||||
|
||||
[[example]]
|
||||
name = "sensors"
|
||||
|
||||
[[example]]
|
||||
required-features = ["ttf"]
|
||||
name = "ttf-demo"
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user