sdl2/
controller.rs

1use crate::rwops::RWops;
2use libc::c_char;
3use std::error;
4use std::ffi::{CStr, CString, NulError};
5use std::fmt;
6use std::io;
7use std::path::Path;
8
9#[cfg(feature = "hidapi")]
10use crate::sensor::SensorType;
11#[cfg(feature = "hidapi")]
12use std::convert::TryInto;
13
14use crate::common::{validate_int, IntegerOrSdlError};
15use crate::get_error;
16use crate::joystick;
17use crate::GameControllerSubsystem;
18use std::mem::transmute;
19
20use crate::sys;
21
22#[derive(Debug, Clone)]
23pub enum AddMappingError {
24    InvalidMapping(NulError),
25    InvalidFilePath(String),
26    ReadError(String),
27    SdlError(String),
28}
29
30impl fmt::Display for AddMappingError {
31    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32        use self::AddMappingError::*;
33
34        match *self {
35            InvalidMapping(ref e) => write!(f, "Null error: {}", e),
36            InvalidFilePath(ref value) => write!(f, "Invalid file path ({})", value),
37            ReadError(ref e) => write!(f, "Read error: {}", e),
38            SdlError(ref e) => write!(f, "SDL error: {}", e),
39        }
40    }
41}
42
43impl error::Error for AddMappingError {
44    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
45        match self {
46            Self::InvalidMapping(err) => Some(err),
47            Self::InvalidFilePath(_) | Self::ReadError(_) | Self::SdlError(_) => None,
48        }
49    }
50}
51
52impl GameControllerSubsystem {
53    /// Retrieve the total number of attached joysticks *and* controllers identified by SDL.
54    #[doc(alias = "SDL_NumJoysticks")]
55    pub fn num_joysticks(&self) -> Result<u32, String> {
56        let result = unsafe { sys::SDL_NumJoysticks() };
57
58        if result >= 0 {
59            Ok(result as u32)
60        } else {
61            Err(get_error())
62        }
63    }
64
65    /// Return true if the joystick at index `joystick_index` is a game controller.
66    #[inline]
67    #[doc(alias = "SDL_IsGameController")]
68    pub fn is_game_controller(&self, joystick_index: u32) -> bool {
69        match validate_int(joystick_index, "joystick_index") {
70            Ok(joystick_index) => unsafe {
71                sys::SDL_IsGameController(joystick_index) != sys::SDL_bool::SDL_FALSE
72            },
73            Err(_) => false,
74        }
75    }
76
77    /// Attempt to open the controller at index `joystick_index` and return it.
78    /// Controller IDs are the same as joystick IDs and the maximum number can
79    /// be retrieved using the `SDL_NumJoysticks` function.
80    #[doc(alias = "SDL_GameControllerOpen")]
81    pub fn open(&self, joystick_index: u32) -> Result<GameController, IntegerOrSdlError> {
82        use crate::common::IntegerOrSdlError::*;
83        let joystick_index = validate_int(joystick_index, "joystick_index")?;
84        let controller = unsafe { sys::SDL_GameControllerOpen(joystick_index) };
85
86        if controller.is_null() {
87            Err(SdlError(get_error()))
88        } else {
89            Ok(GameController {
90                subsystem: self.clone(),
91                raw: controller,
92            })
93        }
94    }
95
96    /// Return the name of the controller at index `joystick_index`.
97    #[doc(alias = "SDL_GameControllerNameForIndex")]
98    pub fn name_for_index(&self, joystick_index: u32) -> Result<String, IntegerOrSdlError> {
99        use crate::common::IntegerOrSdlError::*;
100        let joystick_index = validate_int(joystick_index, "joystick_index")?;
101        let c_str = unsafe { sys::SDL_GameControllerNameForIndex(joystick_index) };
102
103        if c_str.is_null() {
104            Err(SdlError(get_error()))
105        } else {
106            Ok(unsafe {
107                CStr::from_ptr(c_str as *const _)
108                    .to_str()
109                    .unwrap()
110                    .to_owned()
111            })
112        }
113    }
114
115    /// If state is `true` controller events are processed, otherwise
116    /// they're ignored.
117    #[doc(alias = "SDL_GameControllerEventState")]
118    pub fn set_event_state(&self, state: bool) {
119        unsafe { sys::SDL_GameControllerEventState(state as i32) };
120    }
121
122    /// Return `true` if controller events are processed.
123    #[doc(alias = "SDL_GameControllerEventState")]
124    pub fn event_state(&self) -> bool {
125        unsafe { sys::SDL_GameControllerEventState(sys::SDL_QUERY) == sys::SDL_ENABLE as i32 }
126    }
127
128    /// Add a new controller input mapping from a mapping string.
129    #[doc(alias = "SDL_GameControllerAddMapping")]
130    pub fn add_mapping(&self, mapping: &str) -> Result<MappingStatus, AddMappingError> {
131        use self::AddMappingError::*;
132        let mapping = match CString::new(mapping) {
133            Ok(s) => s,
134            Err(err) => return Err(InvalidMapping(err)),
135        };
136
137        let result =
138            unsafe { sys::SDL_GameControllerAddMapping(mapping.as_ptr() as *const c_char) };
139
140        match result {
141            1 => Ok(MappingStatus::Added),
142            0 => Ok(MappingStatus::Updated),
143            _ => Err(SdlError(get_error())),
144        }
145    }
146
147    /// Load controller input mappings from a file.
148    pub fn load_mappings<P: AsRef<Path>>(&self, path: P) -> Result<i32, AddMappingError> {
149        use self::AddMappingError::*;
150
151        let rw = RWops::from_file(path, "r").map_err(InvalidFilePath)?;
152        self.load_mappings_from_rw(rw)
153    }
154
155    /// Load controller input mappings from a [`Read`](std::io::Read) object.
156    pub fn load_mappings_from_read<R: io::Read>(
157        &self,
158        read: &mut R,
159    ) -> Result<i32, AddMappingError> {
160        use self::AddMappingError::*;
161
162        let mut buffer = Vec::with_capacity(1024);
163        let rw = RWops::from_read(read, &mut buffer).map_err(ReadError)?;
164        self.load_mappings_from_rw(rw)
165    }
166
167    /// Load controller input mappings from an SDL [`RWops`] object.
168    #[doc(alias = "SDL_GameControllerAddMappingsFromRW")]
169    pub fn load_mappings_from_rw(&self, rw: RWops<'_>) -> Result<i32, AddMappingError> {
170        use self::AddMappingError::*;
171
172        let result = unsafe { sys::SDL_GameControllerAddMappingsFromRW(rw.raw(), 0) };
173        match result {
174            -1 => Err(SdlError(get_error())),
175            _ => Ok(result),
176        }
177    }
178
179    #[doc(alias = "SDL_GameControllerMappingForGUID")]
180    pub fn mapping_for_guid(&self, guid: joystick::Guid) -> Result<String, String> {
181        let c_str = unsafe { sys::SDL_GameControllerMappingForGUID(guid.raw()) };
182
183        c_str_to_string_or_err(c_str)
184    }
185
186    #[inline]
187    /// Force controller update when not using the event loop
188    #[doc(alias = "SDL_GameControllerUpdate")]
189    pub fn update(&self) {
190        unsafe { sys::SDL_GameControllerUpdate() };
191    }
192}
193
194#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
195#[repr(i32)]
196pub enum Axis {
197    LeftX = sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTX as i32,
198    LeftY = sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTY as i32,
199    RightX = sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX as i32,
200    RightY = sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY as i32,
201    TriggerLeft = sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERLEFT as i32,
202    TriggerRight = sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERRIGHT as i32,
203}
204
205impl Axis {
206    /// Return the Axis from a string description in the same format
207    /// used by the game controller mapping strings.
208    #[doc(alias = "SDL_GameControllerGetAxisFromString")]
209    pub fn from_string(axis: &str) -> Option<Axis> {
210        let id = match CString::new(axis) {
211            Ok(axis) => unsafe {
212                sys::SDL_GameControllerGetAxisFromString(axis.as_ptr() as *const c_char)
213            },
214            // string contains a nul byte - it won't match anything.
215            Err(_) => sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_INVALID,
216        };
217
218        Axis::from_ll(id)
219    }
220
221    /// Return a string for a given axis in the same format using by
222    /// the game controller mapping strings
223    #[doc(alias = "SDL_GameControllerGetStringForAxis")]
224    pub fn string(self) -> String {
225        let axis: sys::SDL_GameControllerAxis;
226        unsafe {
227            axis = transmute(self);
228        }
229
230        let string = unsafe { sys::SDL_GameControllerGetStringForAxis(axis) };
231
232        c_str_to_string(string)
233    }
234
235    pub fn from_ll(bitflags: sys::SDL_GameControllerAxis) -> Option<Axis> {
236        Some(match bitflags {
237            sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_INVALID => return None,
238            sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTX => Axis::LeftX,
239            sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTY => Axis::LeftY,
240            sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX => Axis::RightX,
241            sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY => Axis::RightY,
242            sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERLEFT => Axis::TriggerLeft,
243            sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERRIGHT => Axis::TriggerRight,
244            _ => return None,
245        })
246    }
247
248    pub fn to_ll(self) -> sys::SDL_GameControllerAxis {
249        match self {
250            Axis::LeftX => sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTX,
251            Axis::LeftY => sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTY,
252            Axis::RightX => sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX,
253            Axis::RightY => sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY,
254            Axis::TriggerLeft => sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERLEFT,
255            Axis::TriggerRight => sys::SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
256        }
257    }
258}
259
260#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
261#[repr(i32)]
262pub enum Button {
263    A = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_A as i32,
264    B = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_B as i32,
265    X = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_X as i32,
266    Y = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_Y as i32,
267    Back = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_BACK as i32,
268    Guide = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_GUIDE as i32,
269    Start = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_START as i32,
270    LeftStick = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSTICK as i32,
271    RightStick = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSTICK as i32,
272    LeftShoulder = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSHOULDER as i32,
273    RightShoulder = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER as i32,
274    DPadUp = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_UP as i32,
275    DPadDown = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_DOWN as i32,
276    DPadLeft = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_LEFT as i32,
277    DPadRight = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_RIGHT as i32,
278    Misc1 = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_MISC1 as i32,
279    Paddle1 = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE1 as i32,
280    Paddle2 = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE2 as i32,
281    Paddle3 = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE3 as i32,
282    Paddle4 = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE4 as i32,
283    Touchpad = sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_TOUCHPAD as i32,
284}
285
286impl Button {
287    /// Return the Button from a string description in the same format
288    /// used by the game controller mapping strings.
289    #[doc(alias = "SDL_GameControllerGetButtonFromString")]
290    pub fn from_string(button: &str) -> Option<Button> {
291        let id = match CString::new(button) {
292            Ok(button) => unsafe {
293                sys::SDL_GameControllerGetButtonFromString(button.as_ptr() as *const c_char)
294            },
295            // string contains a nul byte - it won't match anything.
296            Err(_) => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_INVALID,
297        };
298
299        Button::from_ll(id)
300    }
301
302    /// Return a string for a given button in the same format using by
303    /// the game controller mapping strings
304    #[doc(alias = "SDL_GameControllerGetStringForButton")]
305    pub fn string(self) -> String {
306        let button: sys::SDL_GameControllerButton;
307        unsafe {
308            button = transmute(self);
309        }
310
311        let string = unsafe { sys::SDL_GameControllerGetStringForButton(button) };
312
313        c_str_to_string(string)
314    }
315
316    pub fn from_ll(bitflags: sys::SDL_GameControllerButton) -> Option<Button> {
317        Some(match bitflags {
318            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_INVALID => return None,
319            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_A => Button::A,
320            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_B => Button::B,
321            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_X => Button::X,
322            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_Y => Button::Y,
323            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_BACK => Button::Back,
324            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_GUIDE => Button::Guide,
325            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_START => Button::Start,
326            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSTICK => Button::LeftStick,
327            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSTICK => Button::RightStick,
328            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSHOULDER => {
329                Button::LeftShoulder
330            }
331            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER => {
332                Button::RightShoulder
333            }
334            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_UP => Button::DPadUp,
335            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_DOWN => Button::DPadDown,
336            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_LEFT => Button::DPadLeft,
337            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_RIGHT => Button::DPadRight,
338            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_MISC1 => Button::Misc1,
339            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE1 => Button::Paddle1,
340            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE2 => Button::Paddle2,
341            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE3 => Button::Paddle3,
342            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE4 => Button::Paddle4,
343            sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_TOUCHPAD => Button::Touchpad,
344            _ => return None,
345        })
346    }
347
348    pub fn to_ll(self) -> sys::SDL_GameControllerButton {
349        match self {
350            Button::A => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_A,
351            Button::B => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_B,
352            Button::X => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_X,
353            Button::Y => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_Y,
354            Button::Back => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_BACK,
355            Button::Guide => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_GUIDE,
356            Button::Start => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_START,
357            Button::LeftStick => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSTICK,
358            Button::RightStick => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSTICK,
359            Button::LeftShoulder => {
360                sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_LEFTSHOULDER
361            }
362            Button::RightShoulder => {
363                sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
364            }
365            Button::DPadUp => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_UP,
366            Button::DPadDown => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_DOWN,
367            Button::DPadLeft => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_LEFT,
368            Button::DPadRight => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
369            Button::Misc1 => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_MISC1,
370            Button::Paddle1 => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE1,
371            Button::Paddle2 => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE2,
372            Button::Paddle3 => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE3,
373            Button::Paddle4 => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_PADDLE4,
374            Button::Touchpad => sys::SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_TOUCHPAD,
375        }
376    }
377}
378
379/// Possible return values for `add_mapping`
380#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
381pub enum MappingStatus {
382    Added = 1,
383    Updated = 0,
384}
385
386/// Wrapper around the `SDL_GameController` object
387pub struct GameController {
388    subsystem: GameControllerSubsystem,
389    raw: *mut sys::SDL_GameController,
390}
391
392impl GameController {
393    #[inline]
394    pub fn subsystem(&self) -> &GameControllerSubsystem {
395        &self.subsystem
396    }
397
398    /// Return the name of the controller or an empty string if no
399    /// name is found.
400    #[doc(alias = "SDL_GameControllerName")]
401    pub fn name(&self) -> String {
402        let name = unsafe { sys::SDL_GameControllerName(self.raw) };
403
404        c_str_to_string(name)
405    }
406
407    /// Return a String describing the controller's button and axis
408    /// mappings
409    #[doc(alias = "SDL_GameControllerMapping")]
410    pub fn mapping(&self) -> String {
411        let mapping = unsafe { sys::SDL_GameControllerMapping(self.raw) };
412
413        c_str_to_string(mapping)
414    }
415
416    /// Return true if the controller has been opened and currently
417    /// connected.
418    #[doc(alias = "SDL_GameControllerGetAttached")]
419    pub fn attached(&self) -> bool {
420        unsafe { sys::SDL_GameControllerGetAttached(self.raw) != sys::SDL_bool::SDL_FALSE }
421    }
422
423    /// Return the joystick instance id of this controller
424    #[doc(alias = "SDL_GameControllerGetJoystick")]
425    pub fn instance_id(&self) -> u32 {
426        let result = unsafe {
427            let joystick = sys::SDL_GameControllerGetJoystick(self.raw);
428            sys::SDL_JoystickInstanceID(joystick)
429        };
430
431        if result < 0 {
432            // Should only fail if the joystick is NULL.
433            panic!("{}", get_error())
434        } else {
435            result as u32
436        }
437    }
438
439    /// Return the USB vendor ID of an opened controller, if available.
440    #[doc(alias = "SDL_GameControllerGetVendor")]
441    pub fn vendor_id(&self) -> Option<u16> {
442        let result = unsafe { sys::SDL_GameControllerGetVendor(self.raw) };
443
444        if result == 0 {
445            None
446        } else {
447            Some(result)
448        }
449    }
450
451    /// Return the USB product ID of an opened controller, if available.
452    #[doc(alias = "SDL_GameControllerGetProduct")]
453    pub fn product_id(&self) -> Option<u16> {
454        let result = unsafe { sys::SDL_GameControllerGetProduct(self.raw) };
455
456        if result == 0 {
457            None
458        } else {
459            Some(result)
460        }
461    }
462
463    /// Get the position of the given `axis`
464    #[doc(alias = "SDL_GameControllerGetAxis")]
465    pub fn axis(&self, axis: Axis) -> i16 {
466        // This interface is a bit messed up: 0 is a valid position
467        // but can also mean that an error occured.
468        // Fortunately, an error can only occur if the controller pointer is NULL.
469        // There should be no apparent reason for this to change in the future.
470
471        let raw_axis: sys::SDL_GameControllerAxis;
472        unsafe {
473            raw_axis = transmute(axis);
474        }
475
476        unsafe { sys::SDL_GameControllerGetAxis(self.raw, raw_axis) }
477    }
478
479    /// Returns `true` if `button` is pressed.
480    #[doc(alias = "SDL_GameControllerGetButton")]
481    pub fn button(&self, button: Button) -> bool {
482        // This interface is a bit messed up: 0 is a valid position
483        // but can also mean that an error occured.
484        // Fortunately, an error can only occur if the controller pointer is NULL.
485        // There should be no apparent reason for this to change in the future.
486
487        let raw_button: sys::SDL_GameControllerButton;
488        unsafe {
489            raw_button = transmute(button);
490        }
491
492        unsafe { sys::SDL_GameControllerGetButton(self.raw, raw_button) != 0 }
493    }
494
495    /// Set the rumble motors to their specified intensities, if supported.
496    /// Automatically resets back to zero after `duration_ms` milliseconds have passed.
497    ///
498    /// # Notes
499    ///
500    /// The value range for the intensities is 0 to 0xFFFF.
501    ///
502    /// Do *not* use `std::u32::MAX` or similar for `duration_ms` if you want
503    /// the rumble effect to keep playing for a long time, as this results in
504    /// the effect ending immediately after starting due to an overflow.
505    /// Use some smaller, "huge enough" number instead.
506    #[doc(alias = "SDL_GameControllerRumble")]
507    pub fn set_rumble(
508        &mut self,
509        low_frequency_rumble: u16,
510        high_frequency_rumble: u16,
511        duration_ms: u32,
512    ) -> Result<(), IntegerOrSdlError> {
513        let result = unsafe {
514            sys::SDL_GameControllerRumble(
515                self.raw,
516                low_frequency_rumble,
517                high_frequency_rumble,
518                duration_ms,
519            )
520        };
521
522        if result != 0 {
523            Err(IntegerOrSdlError::SdlError(get_error()))
524        } else {
525            Ok(())
526        }
527    }
528
529    /// Start a rumble effect in the game controller's triggers.
530    #[doc(alias = "SDL_GameControllerRumbleTriggers")]
531    pub fn set_rumble_triggers(
532        &mut self,
533        left_rumble: u16,
534        right_rumble: u16,
535        duration_ms: u32,
536    ) -> Result<(), IntegerOrSdlError> {
537        let result = unsafe {
538            sys::SDL_GameControllerRumbleTriggers(self.raw, left_rumble, right_rumble, duration_ms)
539        };
540
541        if result != 0 {
542            Err(IntegerOrSdlError::SdlError(get_error()))
543        } else {
544            Ok(())
545        }
546    }
547
548    /// Query whether a game controller has an LED.
549    #[doc(alias = "SDL_GameControllerHasLED")]
550    pub fn has_led(&self) -> bool {
551        let result = unsafe { sys::SDL_GameControllerHasLED(self.raw) };
552
553        match result {
554            sys::SDL_bool::SDL_FALSE => false,
555            sys::SDL_bool::SDL_TRUE => true,
556        }
557    }
558
559    /// Query whether a game controller has rumble support.
560    #[doc(alias = "SDL_GameControllerHasRumble")]
561    pub fn has_rumble(&self) -> bool {
562        let result = unsafe { sys::SDL_GameControllerHasRumble(self.raw) };
563
564        match result {
565            sys::SDL_bool::SDL_FALSE => false,
566            sys::SDL_bool::SDL_TRUE => true,
567        }
568    }
569
570    /// Query whether a game controller has rumble support on triggers.
571    #[doc(alias = "SDL_GameControllerHasRumbleTriggers")]
572    pub fn has_rumble_triggers(&self) -> bool {
573        let result = unsafe { sys::SDL_GameControllerHasRumbleTriggers(self.raw) };
574
575        match result {
576            sys::SDL_bool::SDL_FALSE => false,
577            sys::SDL_bool::SDL_TRUE => true,
578        }
579    }
580
581    /// Update a game controller's LED color.
582    #[doc(alias = "SDL_GameControllerSetLED")]
583    pub fn set_led(&mut self, red: u8, green: u8, blue: u8) -> Result<(), IntegerOrSdlError> {
584        let result = unsafe { sys::SDL_GameControllerSetLED(self.raw, red, green, blue) };
585
586        if result != 0 {
587            Err(IntegerOrSdlError::SdlError(get_error()))
588        } else {
589            Ok(())
590        }
591    }
592
593    /// Send a controller specific effect packet.
594    #[doc(alias = "SDL_GameControllerSendEffect")]
595    pub fn send_effect(&mut self, data: &[u8]) -> Result<(), String> {
596        let result = unsafe {
597            sys::SDL_GameControllerSendEffect(
598                self.raw,
599                data.as_ptr() as *const libc::c_void,
600                data.len() as i32,
601            )
602        };
603
604        if result != 0 {
605            Err(get_error())
606        } else {
607            Ok(())
608        }
609    }
610}
611
612#[cfg(feature = "hidapi")]
613impl GameController {
614    #[doc(alias = "SDL_GameControllerHasSensor")]
615    pub fn has_sensor(&self, sensor_type: crate::sensor::SensorType) -> bool {
616        let result = unsafe { sys::SDL_GameControllerHasSensor(self.raw, sensor_type.into()) };
617
618        match result {
619            sys::SDL_bool::SDL_FALSE => false,
620            sys::SDL_bool::SDL_TRUE => true,
621        }
622    }
623
624    #[doc(alias = "SDL_GameControllerIsSensorEnabled")]
625    pub fn sensor_enabled(&self, sensor_type: crate::sensor::SensorType) -> bool {
626        let result =
627            unsafe { sys::SDL_GameControllerIsSensorEnabled(self.raw, sensor_type.into()) };
628
629        match result {
630            sys::SDL_bool::SDL_FALSE => false,
631            sys::SDL_bool::SDL_TRUE => true,
632        }
633    }
634
635    #[doc(alias = "SDL_GameControllerSetSensorEnabled")]
636    pub fn sensor_set_enabled(
637        &self,
638        sensor_type: crate::sensor::SensorType,
639        enabled: bool,
640    ) -> Result<(), IntegerOrSdlError> {
641        let result = unsafe {
642            sys::SDL_GameControllerSetSensorEnabled(
643                self.raw,
644                sensor_type.into(),
645                if enabled {
646                    sys::SDL_bool::SDL_TRUE
647                } else {
648                    sys::SDL_bool::SDL_FALSE
649                },
650            )
651        };
652
653        if result != 0 {
654            Err(IntegerOrSdlError::SdlError(get_error()))
655        } else {
656            Ok(())
657        }
658    }
659
660    /// Get the data rate (number of events per second) of a game controller sensor.
661    #[doc(alias = "SDL_GameControllerGetSensorDataRate")]
662    pub fn sensor_get_data_rate(&self, sensor_type: SensorType) -> f32 {
663        unsafe { sys::SDL_GameControllerGetSensorDataRate(self.raw, sensor_type.into()) }
664    }
665
666    /// Get data from a sensor.
667    ///
668    /// The number of data points depends on the sensor. Both Gyroscope and
669    /// Accelerometer return 3 values, one for each axis.
670    #[doc(alias = "SDL_GameControllerGetSensorData")]
671    pub fn sensor_get_data(
672        &self,
673        sensor_type: SensorType,
674        data: &mut [f32],
675    ) -> Result<(), IntegerOrSdlError> {
676        let result = unsafe {
677            sys::SDL_GameControllerGetSensorData(
678                self.raw,
679                sensor_type.into(),
680                data.as_mut_ptr(),
681                data.len().try_into().unwrap(),
682            )
683        };
684
685        if result != 0 {
686            Err(IntegerOrSdlError::SdlError(get_error()))
687        } else {
688            Ok(())
689        }
690    }
691}
692
693impl Drop for GameController {
694    #[doc(alias = "SDL_GameControllerClose")]
695    fn drop(&mut self) {
696        unsafe { sys::SDL_GameControllerClose(self.raw) }
697    }
698}
699
700/// Convert C string `c_str` to a String. Return an empty string if
701/// `c_str` is NULL.
702fn c_str_to_string(c_str: *const c_char) -> String {
703    if c_str.is_null() {
704        String::new()
705    } else {
706        unsafe {
707            CStr::from_ptr(c_str as *const _)
708                .to_str()
709                .unwrap()
710                .to_owned()
711        }
712    }
713}
714
715/// Convert C string `c_str` to a String. Return an SDL error if
716/// `c_str` is NULL.
717fn c_str_to_string_or_err(c_str: *const c_char) -> Result<String, String> {
718    if c_str.is_null() {
719        Err(get_error())
720    } else {
721        Ok(unsafe {
722            CStr::from_ptr(c_str as *const _)
723                .to_str()
724                .unwrap()
725                .to_owned()
726        })
727    }
728}