Skip to content

Commit 5473564

Browse files
author
Mikaël Fourrier
committed
First try at sensor API implementation
1 parent f6adc8d commit 5473564

File tree

6 files changed

+341
-0
lines changed

6 files changed

+341
-0
lines changed

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ name = "renderer-yuv"
140140
required-features = ["ttf", "image"]
141141
name = "resource-manager"
142142

143+
[[example]]
144+
name = "sensors"
145+
143146
[[example]]
144147
required-features = ["ttf"]
145148
name = "ttf-demo"

examples/sensors.rs

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use std::ffi::CStr;
2+
use std::time::{Duration, Instant};
3+
4+
use sdl2::sys::SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED;
5+
use sdl2::{event::Event, sensor::SensorType};
6+
7+
extern crate sdl2;
8+
9+
fn main() -> Result<(), String> {
10+
sdl2::hint::set(
11+
CStr::from_bytes_with_nul(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED)
12+
.unwrap()
13+
.to_str()
14+
.unwrap(),
15+
"1",
16+
);
17+
let sdl_context = sdl2::init()?;
18+
let game_controller_subsystem = sdl_context.game_controller()?;
19+
let _sensor_subsystem = sdl_context.sensor()?;
20+
21+
let available = game_controller_subsystem
22+
.num_joysticks()
23+
.map_err(|e| format!("can't enumerate joysticks: {}", e))?;
24+
25+
println!("{} joysticks available", available);
26+
27+
// Iterate over all available joysticks and look for game controllers.
28+
let mut controller = (0..available)
29+
.find_map(|id| {
30+
if !game_controller_subsystem.is_game_controller(id) {
31+
println!("{} is not a game controller", id);
32+
return None;
33+
}
34+
35+
println!("Attempting to open controller {}", id);
36+
37+
match game_controller_subsystem.open(id) {
38+
Ok(c) => {
39+
// We managed to find and open a game controller,
40+
// exit the loop
41+
println!("Success: opened \"{}\"", c.name());
42+
Some(c)
43+
}
44+
Err(e) => {
45+
println!("failed: {:?}", e);
46+
None
47+
}
48+
}
49+
})
50+
.expect("Couldn't open any controller");
51+
52+
if !controller.has_sensor(SensorType::Accelerometer) {
53+
return Err(format!(
54+
"{} doesn't support the accelerometer",
55+
controller.name()
56+
));
57+
}
58+
if !controller.has_sensor(SensorType::Gyroscope) {
59+
return Err(format!(
60+
"{} doesn't support the gyroscope",
61+
controller.name()
62+
));
63+
}
64+
65+
controller
66+
.sensor_set_enabled(SensorType::Accelerometer, true)
67+
.map_err(|e| format!("error enabling accelerometer: {}", e))?;
68+
controller
69+
.sensor_set_enabled(SensorType::Gyroscope, true)
70+
.map_err(|e| format!("error enabling gyroscope: {}", e))?;
71+
let mut now = Instant::now();
72+
for event in sdl_context.event_pump()?.wait_iter() {
73+
if now.elapsed() > Duration::from_secs(1) {
74+
now = Instant::now();
75+
76+
let mut gyro_data = [0f32; 3];
77+
let mut accel_data = [0f32; 3];
78+
79+
controller
80+
.sensor_get_data(SensorType::Gyroscope, &mut gyro_data)
81+
.map_err(|e| format!("error getting gyro data: {}", e))?;
82+
controller
83+
.sensor_get_data(SensorType::Accelerometer, &mut accel_data)
84+
.map_err(|e| format!("error getting accel data: {}", e))?;
85+
86+
println!("gyro: {:?}, accel: {:?}", gyro_data, accel_data);
87+
}
88+
89+
if let Event::Quit { .. } = event {
90+
break;
91+
}
92+
}
93+
94+
Ok(())
95+
}

src/sdl2/controller.rs

+77
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
use crate::rwops::RWops;
2+
use crate::sensor::SensorType;
23
use libc::c_char;
4+
use std::convert::TryInto;
35
use std::error;
46
use std::ffi::{CStr, CString, NulError};
57
use std::fmt;
68
use std::io;
79
use std::path::Path;
10+
use sys::SDL_bool;
811

912
use crate::common::{validate_int, IntegerOrSdlError};
1013
use crate::get_error;
@@ -486,6 +489,80 @@ impl GameController {
486489
}
487490
}
488491

492+
impl GameController {
493+
#[doc(alias = "SDL_GameControllerHasSensor")]
494+
pub fn has_sensor(&self, sensor_type: SensorType) -> bool {
495+
let result = unsafe { sys::SDL_GameControllerHasSensor(self.raw, sensor_type.into()) };
496+
497+
match result {
498+
sys::SDL_bool::SDL_FALSE => false,
499+
sys::SDL_bool::SDL_TRUE => true,
500+
}
501+
}
502+
503+
#[doc(alias = "SDL_GameControllerIsSensorEnabled")]
504+
pub fn sensor_enabled(&self, sensor_type: SensorType) -> bool {
505+
let result =
506+
unsafe { sys::SDL_GameControllerIsSensorEnabled(self.raw, sensor_type.into()) };
507+
508+
match result {
509+
sys::SDL_bool::SDL_FALSE => false,
510+
sys::SDL_bool::SDL_TRUE => true,
511+
}
512+
}
513+
514+
#[doc(alias = "SDL_GameControllerHasSensor")]
515+
pub fn sensor_set_enabled(
516+
&self,
517+
sensor_type: SensorType,
518+
enabled: bool,
519+
) -> Result<(), IntegerOrSdlError> {
520+
let result = unsafe {
521+
sys::SDL_GameControllerSetSensorEnabled(
522+
self.raw,
523+
sensor_type.into(),
524+
if enabled {
525+
SDL_bool::SDL_TRUE
526+
} else {
527+
SDL_bool::SDL_FALSE
528+
},
529+
)
530+
};
531+
532+
if result != 0 {
533+
Err(IntegerOrSdlError::SdlError(get_error()))
534+
} else {
535+
Ok(())
536+
}
537+
}
538+
539+
/// Get data from a sensor.
540+
///
541+
/// The number of data points depends on the sensor. Both Gyroscope and
542+
/// Accelerometer return 3 values, one for each axis.
543+
#[doc(alias = "SDL_GameControllerGetSensorData")]
544+
pub fn sensor_get_data(
545+
&self,
546+
sensor_type: SensorType,
547+
data: &mut [f32],
548+
) -> Result<(), IntegerOrSdlError> {
549+
let result = unsafe {
550+
sys::SDL_GameControllerGetSensorData(
551+
self.raw,
552+
sensor_type.into(),
553+
data.as_mut_ptr(),
554+
data.len().try_into().unwrap(),
555+
)
556+
};
557+
558+
if result != 0 {
559+
Err(IntegerOrSdlError::SdlError(get_error()))
560+
} else {
561+
Ok(())
562+
}
563+
}
564+
}
565+
489566
impl Drop for GameController {
490567
#[doc(alias = "SDL_GameControllerClose")]
491568
fn drop(&mut self) {

src/sdl2/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ pub mod joystick;
7777
pub mod keyboard;
7878
pub mod log;
7979
pub mod messagebox;
80+
pub mod sensor;
8081
pub mod mouse;
8182
pub mod pixels;
8283
pub mod rect;

src/sdl2/sdl.rs

+7
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ impl Sdl {
121121
GameControllerSubsystem::new(self)
122122
}
123123

124+
/// Initializes the game controller subsystem.
125+
#[inline]
126+
pub fn sensor(&self) -> Result<SensorSubsystem, String> {
127+
SensorSubsystem::new(self)
128+
}
129+
124130
/// Initializes the timer subsystem.
125131
#[inline]
126132
pub fn timer(&self) -> Result<TimerSubsystem, String> {
@@ -278,6 +284,7 @@ subsystem!(VideoSubsystem, sys::SDL_INIT_VIDEO, nosync);
278284
subsystem!(TimerSubsystem, sys::SDL_INIT_TIMER, sync);
279285
// The event queue can be read from other threads.
280286
subsystem!(EventSubsystem, sys::SDL_INIT_EVENTS, sync);
287+
subsystem!(SensorSubsystem, sys::SDL_INIT_SENSOR, sync);
281288

282289
static mut IS_EVENT_PUMP_ALIVE: bool = false;
283290

src/sdl2/sensor.rs

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
use crate::sys;
2+
3+
use crate::common::{validate_int, IntegerOrSdlError};
4+
use crate::get_error;
5+
use crate::SensorSubsystem;
6+
use libc::c_char;
7+
use std::ffi::CStr;
8+
use sys::SDL_SensorGetData;
9+
use sys::SDL_SensorType;
10+
11+
impl SensorSubsystem {
12+
/// Retrieve the total number of attached joysticks *and* controllers identified by SDL.
13+
#[doc(alias = "SDL_NumSensors")]
14+
pub fn num_sensors(&self) -> Result<u32, String> {
15+
let result = unsafe { sys::SDL_NumSensors() };
16+
17+
if result >= 0 {
18+
Ok(result as u32)
19+
} else {
20+
Err(get_error())
21+
}
22+
}
23+
24+
/// Attempt to open the joystick at index `joystick_index` and return it.
25+
#[doc(alias = "SDL_SensorOpen")]
26+
pub fn open(&self, sensor_index: u32) -> Result<Sensor, IntegerOrSdlError> {
27+
use crate::common::IntegerOrSdlError::*;
28+
let sensor_index = validate_int(sensor_index, "sensor_index")?;
29+
30+
let sensor = unsafe { sys::SDL_SensorOpen(sensor_index) };
31+
32+
if sensor.is_null() {
33+
Err(SdlError(get_error()))
34+
} else {
35+
Ok(Sensor {
36+
subsystem: self.clone(),
37+
raw: sensor,
38+
})
39+
}
40+
}
41+
42+
/// Force joystick update when not using the event loop
43+
#[inline]
44+
#[doc(alias = "SDL_SensorUpdate")]
45+
pub fn update(&self) {
46+
unsafe { sys::SDL_SensorUpdate() };
47+
}
48+
}
49+
50+
#[derive(Debug, Clone, Copy)]
51+
pub enum SensorType {
52+
Unknown,
53+
Gyroscope,
54+
Accelerometer,
55+
}
56+
57+
impl Into<SDL_SensorType> for SensorType {
58+
fn into(self) -> SDL_SensorType {
59+
match self {
60+
SensorType::Unknown => SDL_SensorType::SDL_SENSOR_UNKNOWN,
61+
SensorType::Gyroscope => SDL_SensorType::SDL_SENSOR_GYRO,
62+
SensorType::Accelerometer => SDL_SensorType::SDL_SENSOR_ACCEL,
63+
}
64+
}
65+
}
66+
67+
/// Wrapper around the `SDL_Joystick` object
68+
pub struct Sensor {
69+
subsystem: SensorSubsystem,
70+
raw: *mut sys::SDL_Sensor,
71+
}
72+
73+
impl Sensor {
74+
#[inline]
75+
pub const fn subsystem(&self) -> &SensorSubsystem {
76+
&self.subsystem
77+
}
78+
79+
/// Return the name of the joystick or an empty string if no name
80+
/// is found.
81+
#[doc(alias = "SDL_SensorGetName")]
82+
pub fn name(&self) -> String {
83+
let name = unsafe { sys::SDL_SensorGetName(self.raw) };
84+
85+
c_str_to_string(name)
86+
}
87+
88+
#[doc(alias = "SDL_SensorGetInstanceID")]
89+
pub fn instance_id(&self) -> u32 {
90+
let result = unsafe { sys::SDL_SensorGetInstanceID(self.raw) };
91+
92+
if result < 0 {
93+
// Should only fail if the joystick is NULL.
94+
panic!("{}", get_error())
95+
} else {
96+
result as u32
97+
}
98+
}
99+
100+
#[doc(alias = "SDL_SensorGetType")]
101+
pub fn sensor_type(&self) -> SensorType {
102+
let result = unsafe { sys::SDL_SensorGetType(self.raw) };
103+
104+
match result {
105+
sys::SDL_SensorType::SDL_SENSOR_INVALID => {
106+
panic!("{}", get_error())
107+
}
108+
sys::SDL_SensorType::SDL_SENSOR_UNKNOWN => SensorType::Unknown,
109+
sys::SDL_SensorType::SDL_SENSOR_ACCEL => SensorType::Accelerometer,
110+
sys::SDL_SensorType::SDL_SENSOR_GYRO => SensorType::Gyroscope,
111+
}
112+
}
113+
114+
#[doc(alias = "SDL_SensorGetType")]
115+
pub fn get_data(&self) -> Result<SensorData, IntegerOrSdlError> {
116+
let mut data = [0f32; 16];
117+
let result = unsafe { SDL_SensorGetData(self.raw, data.as_mut_ptr(), data.len() as i32) };
118+
119+
if result != 0 {
120+
Err(IntegerOrSdlError::SdlError(get_error()))
121+
} else {
122+
Ok(match self.sensor_type() {
123+
SensorType::Gyroscope => SensorData::Accel([data[0], data[1], data[2]]),
124+
SensorType::Accelerometer => SensorData::Accel([data[0], data[1], data[2]]),
125+
SensorType::Unknown => SensorData::Unknown(data),
126+
})
127+
}
128+
}
129+
}
130+
131+
#[derive(Debug, Clone, Copy)]
132+
pub enum SensorData {
133+
Gyro([f32; 3]),
134+
Accel([f32; 3]),
135+
Unknown([f32; 16]),
136+
}
137+
138+
impl Drop for Sensor {
139+
#[doc(alias = "SDL_SensorClose")]
140+
fn drop(&mut self) {
141+
unsafe { sys::SDL_SensorClose(self.raw) }
142+
}
143+
}
144+
145+
/// Convert C string `c_str` to a String. Return an empty string if
146+
/// `c_str` is NULL.
147+
fn c_str_to_string(c_str: *const c_char) -> String {
148+
if c_str.is_null() {
149+
String::new()
150+
} else {
151+
unsafe {
152+
CStr::from_ptr(c_str as *const _)
153+
.to_str()
154+
.unwrap()
155+
.to_owned()
156+
}
157+
}
158+
}

0 commit comments

Comments
 (0)