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