[go: up one dir, main page]

sdl2/
joystick.rs

1use crate::sys;
2use crate::sys::SDL_JoystickPowerLevel;
3
4use crate::clear_error;
5use crate::common::{validate_int, IntegerOrSdlError};
6use crate::get_error;
7use crate::JoystickSubsystem;
8use libc::c_char;
9use std::ffi::{CStr, CString, NulError};
10use std::fmt::{Display, Error, Formatter};
11
12impl JoystickSubsystem {
13    /// Retrieve the total number of attached joysticks *and* controllers identified by SDL.
14    #[doc(alias = "SDL_NumJoysticks")]
15    pub fn num_joysticks(&self) -> Result<u32, String> {
16        let result = unsafe { sys::SDL_NumJoysticks() };
17
18        if result >= 0 {
19            Ok(result as u32)
20        } else {
21            Err(get_error())
22        }
23    }
24
25    /// Attempt to open the joystick at index `joystick_index` and return it.
26    #[doc(alias = "SDL_JoystickOpen")]
27    pub fn open(&self, joystick_index: u32) -> Result<Joystick, IntegerOrSdlError> {
28        use crate::common::IntegerOrSdlError::*;
29        let joystick_index = validate_int(joystick_index, "joystick_index")?;
30
31        let joystick = unsafe { sys::SDL_JoystickOpen(joystick_index) };
32
33        if joystick.is_null() {
34            Err(SdlError(get_error()))
35        } else {
36            Ok(Joystick {
37                subsystem: self.clone(),
38                raw: joystick,
39            })
40        }
41    }
42
43    /// Return the name of the joystick at index `joystick_index`.
44    #[doc(alias = "SDL_JoystickNameForIndex")]
45    pub fn name_for_index(&self, joystick_index: u32) -> Result<String, IntegerOrSdlError> {
46        use crate::common::IntegerOrSdlError::*;
47        let joystick_index = validate_int(joystick_index, "joystick_index")?;
48
49        let c_str = unsafe { sys::SDL_JoystickNameForIndex(joystick_index) };
50
51        if c_str.is_null() {
52            Err(SdlError(get_error()))
53        } else {
54            Ok(unsafe {
55                CStr::from_ptr(c_str as *const _)
56                    .to_str()
57                    .unwrap()
58                    .to_string()
59            })
60        }
61    }
62
63    /// Get the GUID for the joystick at index `joystick_index`
64    #[doc(alias = "SDL_JoystickGetDeviceGUID")]
65    pub fn device_guid(&self, joystick_index: u32) -> Result<Guid, IntegerOrSdlError> {
66        use crate::common::IntegerOrSdlError::*;
67        let joystick_index = validate_int(joystick_index, "joystick_index")?;
68
69        let raw = unsafe { sys::SDL_JoystickGetDeviceGUID(joystick_index) };
70
71        let guid = Guid { raw };
72
73        if guid.is_zero() {
74            Err(SdlError(get_error()))
75        } else {
76            Ok(guid)
77        }
78    }
79
80    /// If state is `true` joystick events are processed, otherwise
81    /// they're ignored.
82    #[doc(alias = "SDL_JoystickEventState")]
83    pub fn set_event_state(&self, state: bool) {
84        unsafe { sys::SDL_JoystickEventState(state as i32) };
85    }
86
87    /// Return `true` if joystick events are processed.
88    #[doc(alias = "SDL_JoystickEventState")]
89    pub fn event_state(&self) -> bool {
90        unsafe { sys::SDL_JoystickEventState(sys::SDL_QUERY) == sys::SDL_ENABLE as i32 }
91    }
92
93    /// Force joystick update when not using the event loop
94    #[inline]
95    #[doc(alias = "SDL_JoystickUpdate")]
96    pub fn update(&self) {
97        unsafe { sys::SDL_JoystickUpdate() };
98    }
99}
100
101#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
102#[repr(i32)]
103pub enum PowerLevel {
104    Unknown = SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_UNKNOWN as i32,
105    Empty = SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_EMPTY as i32,
106    Low = SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_LOW as i32,
107    Medium = SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_MEDIUM as i32,
108    Full = SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_FULL as i32,
109    Wired = SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_WIRED as i32,
110}
111
112impl PowerLevel {
113    pub fn from_ll(raw: SDL_JoystickPowerLevel) -> PowerLevel {
114        match raw {
115            SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_UNKNOWN => PowerLevel::Unknown,
116            SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_EMPTY => PowerLevel::Empty,
117            SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_LOW => PowerLevel::Low,
118            SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_MEDIUM => PowerLevel::Medium,
119            SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_FULL => PowerLevel::Full,
120            SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_WIRED => PowerLevel::Wired,
121            _ => panic!("Unexpected power level: {}", raw as i32),
122        }
123    }
124
125    pub fn to_ll(self) -> SDL_JoystickPowerLevel {
126        match self {
127            PowerLevel::Unknown => SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_UNKNOWN,
128            PowerLevel::Empty => SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_EMPTY,
129            PowerLevel::Low => SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_LOW,
130            PowerLevel::Medium => SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_MEDIUM,
131            PowerLevel::Full => SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_FULL,
132            PowerLevel::Wired => SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_WIRED,
133        }
134    }
135}
136
137/// Wrapper around the `SDL_Joystick` object
138pub struct Joystick {
139    subsystem: JoystickSubsystem,
140    raw: *mut sys::SDL_Joystick,
141}
142
143impl Joystick {
144    #[inline]
145    pub const fn subsystem(&self) -> &JoystickSubsystem {
146        &self.subsystem
147    }
148
149    /// Return the name of the joystick or an empty string if no name
150    /// is found.
151    #[doc(alias = "SDL_JoystickName")]
152    pub fn name(&self) -> String {
153        let name = unsafe { sys::SDL_JoystickName(self.raw) };
154
155        c_str_to_string(name)
156    }
157
158    /// Return true if the joystick has been opened and currently
159    /// connected.
160    #[doc(alias = "SDL_JoystickGetAttached")]
161    pub fn attached(&self) -> bool {
162        unsafe { sys::SDL_JoystickGetAttached(self.raw) != sys::SDL_bool::SDL_FALSE }
163    }
164
165    #[doc(alias = "SDL_JoystickInstanceID")]
166    pub fn instance_id(&self) -> u32 {
167        let result = unsafe { sys::SDL_JoystickInstanceID(self.raw) };
168
169        if result < 0 {
170            // Should only fail if the joystick is NULL.
171            panic!("{}", get_error())
172        } else {
173            result as u32
174        }
175    }
176
177    /// Retrieve the joystick's GUID
178    #[doc(alias = "SDL_JoystickGetGUID")]
179    pub fn guid(&self) -> Guid {
180        let raw = unsafe { sys::SDL_JoystickGetGUID(self.raw) };
181
182        let guid = Guid { raw };
183
184        if guid.is_zero() {
185            // Should only fail if the joystick is NULL.
186            panic!("{}", get_error())
187        } else {
188            guid
189        }
190    }
191
192    /// Retrieve the battery level of this joystick
193    #[doc(alias = "SDL_JoystickCurrentPowerLevel")]
194    pub fn power_level(&self) -> Result<PowerLevel, IntegerOrSdlError> {
195        use crate::common::IntegerOrSdlError::*;
196        clear_error();
197
198        let result = unsafe { sys::SDL_JoystickCurrentPowerLevel(self.raw) };
199
200        let state = PowerLevel::from_ll(result);
201
202        if result != SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_UNKNOWN {
203            Ok(state)
204        } else {
205            let err = get_error();
206
207            if err.is_empty() {
208                Ok(state)
209            } else {
210                Err(SdlError(err))
211            }
212        }
213    }
214
215    /// Retrieve the number of axes for this joystick
216    #[doc(alias = "SDL_JoystickNumAxes")]
217    pub fn num_axes(&self) -> u32 {
218        let result = unsafe { sys::SDL_JoystickNumAxes(self.raw) };
219
220        if result < 0 {
221            // Should only fail if the joystick is NULL.
222            panic!("{}", get_error())
223        } else {
224            result as u32
225        }
226    }
227
228    /// Gets the position of the given `axis`.
229    ///
230    /// The function will fail if the joystick doesn't have the provided axis.
231    #[doc(alias = "SDL_JoystickGetAxis")]
232    pub fn axis(&self, axis: u32) -> Result<i16, IntegerOrSdlError> {
233        use crate::common::IntegerOrSdlError::*;
234        // This interface is a bit messed up: 0 is a valid position
235        // but can also mean that an error occured. As far as I can
236        // tell the only way to know if an error happened is to see if
237        // get_error() returns a non-empty string.
238        clear_error();
239
240        let axis = validate_int(axis, "axis")?;
241        let pos = unsafe { sys::SDL_JoystickGetAxis(self.raw, axis) };
242
243        if pos != 0 {
244            Ok(pos)
245        } else {
246            let err = get_error();
247
248            if err.is_empty() {
249                Ok(pos)
250            } else {
251                Err(SdlError(err))
252            }
253        }
254    }
255
256    /// Retrieve the number of buttons for this joystick
257    #[doc(alias = "SDL_JoystickNumButtons")]
258    pub fn num_buttons(&self) -> u32 {
259        let result = unsafe { sys::SDL_JoystickNumButtons(self.raw) };
260
261        if result < 0 {
262            // Should only fail if the joystick is NULL.
263            panic!("{}", get_error())
264        } else {
265            result as u32
266        }
267    }
268
269    /// Return `Ok(true)` if `button` is pressed.
270    ///
271    /// The function will fail if the joystick doesn't have the provided button.
272    #[doc(alias = "SDL_JoystickGetButton")]
273    pub fn button(&self, button: u32) -> Result<bool, IntegerOrSdlError> {
274        use crate::common::IntegerOrSdlError::*;
275        // Same deal as axis, 0 can mean both unpressed or
276        // error...
277        clear_error();
278
279        let button = validate_int(button, "button")?;
280        let pressed = unsafe { sys::SDL_JoystickGetButton(self.raw, button) };
281
282        match pressed {
283            1 => Ok(true),
284            0 => {
285                let err = get_error();
286
287                if err.is_empty() {
288                    // Button is not pressed
289                    Ok(false)
290                } else {
291                    Err(SdlError(err))
292                }
293            }
294            // Should be unreachable
295            _ => unreachable!(),
296        }
297    }
298
299    /// Retrieve the number of balls for this joystick
300    #[doc(alias = "SDL_JoystickNumBalls")]
301    pub fn num_balls(&self) -> u32 {
302        let result = unsafe { sys::SDL_JoystickNumBalls(self.raw) };
303
304        if result < 0 {
305            // Should only fail if the joystick is NULL.
306            panic!("{}", get_error())
307        } else {
308            result as u32
309        }
310    }
311
312    /// Return a pair `(dx, dy)` containing the difference in axis
313    /// position since the last poll
314    #[doc(alias = "SDL_JoystickGetBall")]
315    pub fn ball(&self, ball: u32) -> Result<(i32, i32), IntegerOrSdlError> {
316        use crate::common::IntegerOrSdlError::*;
317        let mut dx = 0;
318        let mut dy = 0;
319
320        let ball = validate_int(ball, "ball")?;
321        let result = unsafe { sys::SDL_JoystickGetBall(self.raw, ball, &mut dx, &mut dy) };
322
323        if result == 0 {
324            Ok((dx, dy))
325        } else {
326            Err(SdlError(get_error()))
327        }
328    }
329
330    /// Retrieve the number of balls for this joystick
331    #[doc(alias = "SDL_JoystickNumHats")]
332    pub fn num_hats(&self) -> u32 {
333        let result = unsafe { sys::SDL_JoystickNumHats(self.raw) };
334
335        if result < 0 {
336            // Should only fail if the joystick is NULL.
337            panic!("{}", get_error())
338        } else {
339            result as u32
340        }
341    }
342
343    /// Return the position of `hat` for this joystick
344    #[doc(alias = "SDL_JoystickGetHat")]
345    pub fn hat(&self, hat: u32) -> Result<HatState, IntegerOrSdlError> {
346        use crate::common::IntegerOrSdlError::*;
347        // Guess what? This function as well uses 0 to report an error
348        // but 0 is also a valid value (HatState::Centered). So we
349        // have to use the same hack as `axis`...
350        clear_error();
351
352        let hat = validate_int(hat, "hat")?;
353        let result = unsafe { sys::SDL_JoystickGetHat(self.raw, hat) };
354
355        let state = HatState::from_raw(result as u8);
356
357        if result != 0 {
358            Ok(state)
359        } else {
360            let err = get_error();
361
362            if err.is_empty() {
363                Ok(state)
364            } else {
365                Err(SdlError(err))
366            }
367        }
368    }
369
370    /// Set the rumble motors to their specified intensities, if supported.
371    /// Automatically resets back to zero after `duration_ms` milliseconds have passed.
372    ///
373    /// # Notes
374    ///
375    /// The value range for the intensities is 0 to 0xFFFF.
376    ///
377    /// Do *not* use `std::u32::MAX` or similar for `duration_ms` if you want
378    /// the rumble effect to keep playing for a long time, as this results in
379    /// the effect ending immediately after starting due to an overflow.
380    /// Use some smaller, "huge enough" number instead.
381    #[doc(alias = "SDL_JoystickRumble")]
382    pub fn set_rumble(
383        &mut self,
384        low_frequency_rumble: u16,
385        high_frequency_rumble: u16,
386        duration_ms: u32,
387    ) -> Result<(), IntegerOrSdlError> {
388        let result = unsafe {
389            sys::SDL_JoystickRumble(
390                self.raw,
391                low_frequency_rumble,
392                high_frequency_rumble,
393                duration_ms,
394            )
395        };
396
397        if result != 0 {
398            Err(IntegerOrSdlError::SdlError(get_error()))
399        } else {
400            Ok(())
401        }
402    }
403
404    /// Start a rumble effect in the joystick's triggers.
405    #[doc(alias = "SDL_JoystickRumbleTriggers")]
406    pub fn set_rumble_triggers(
407        &mut self,
408        left_rumble: u16,
409        right_rumble: u16,
410        duration_ms: u32,
411    ) -> Result<(), IntegerOrSdlError> {
412        let result = unsafe {
413            sys::SDL_JoystickRumbleTriggers(self.raw, left_rumble, right_rumble, duration_ms)
414        };
415
416        if result != 0 {
417            Err(IntegerOrSdlError::SdlError(get_error()))
418        } else {
419            Ok(())
420        }
421    }
422
423    /// Query whether a joystick has an LED.
424    #[doc(alias = "SDL_JoystickHasLED")]
425    pub fn has_led(&self) -> bool {
426        let result = unsafe { sys::SDL_JoystickHasLED(self.raw) };
427
428        match result {
429            sys::SDL_bool::SDL_FALSE => false,
430            sys::SDL_bool::SDL_TRUE => true,
431        }
432    }
433
434    /// Query whether a joystick has rumble support.
435    #[doc(alias = "SDL_JoystickHasRumble")]
436    pub fn has_rumble(&self) -> bool {
437        let result = unsafe { sys::SDL_JoystickHasRumble(self.raw) };
438
439        match result {
440            sys::SDL_bool::SDL_FALSE => false,
441            sys::SDL_bool::SDL_TRUE => true,
442        }
443    }
444
445    /// Query whether a joystick has rumble support on triggers.
446    #[doc(alias = "SDL_JoystickHasRumbleTriggers")]
447    pub fn has_rumble_triggers(&self) -> bool {
448        let result = unsafe { sys::SDL_JoystickHasRumbleTriggers(self.raw) };
449
450        match result {
451            sys::SDL_bool::SDL_FALSE => false,
452            sys::SDL_bool::SDL_TRUE => true,
453        }
454    }
455
456    /// Update a joystick's LED color.
457    #[doc(alias = "SDL_JoystickSetLED")]
458    pub fn set_led(&mut self, red: u8, green: u8, blue: u8) -> Result<(), IntegerOrSdlError> {
459        let result = unsafe { sys::SDL_JoystickSetLED(self.raw, red, green, blue) };
460
461        if result != 0 {
462            Err(IntegerOrSdlError::SdlError(get_error()))
463        } else {
464            Ok(())
465        }
466    }
467
468    /// Send a joystick specific effect packet.
469    #[doc(alias = "SDL_JoystickSendEffect")]
470    pub fn send_effect(&mut self, data: &[u8]) -> Result<(), IntegerOrSdlError> {
471        let result = unsafe {
472            sys::SDL_JoystickSendEffect(
473                self.raw,
474                data.as_ptr() as *const libc::c_void,
475                data.len() as i32,
476            )
477        };
478
479        if result != 0 {
480            Err(IntegerOrSdlError::SdlError(get_error()))
481        } else {
482            Ok(())
483        }
484    }
485}
486
487impl Drop for Joystick {
488    #[doc(alias = "SDL_JoystickClose")]
489    fn drop(&mut self) {
490        if self.attached() {
491            unsafe { sys::SDL_JoystickClose(self.raw) }
492        }
493    }
494}
495
496/// Wrapper around a `SDL_JoystickGUID`, a globally unique identifier
497/// for a joystick.
498#[derive(Copy, Clone)]
499pub struct Guid {
500    raw: sys::SDL_JoystickGUID,
501}
502
503impl PartialEq for Guid {
504    fn eq(&self, other: &Guid) -> bool {
505        self.raw.data == other.raw.data
506    }
507}
508
509impl Eq for Guid {}
510
511impl Guid {
512    /// Create a GUID from a string representation.
513    #[doc(alias = "SDL_JoystickGetGUIDFromString")]
514    pub fn from_string(guid: &str) -> Result<Guid, NulError> {
515        let guid = CString::new(guid)?;
516
517        let raw = unsafe { sys::SDL_JoystickGetGUIDFromString(guid.as_ptr() as *const c_char) };
518
519        Ok(Guid { raw })
520    }
521
522    /// Return `true` if GUID is full 0s
523    pub fn is_zero(&self) -> bool {
524        for &i in &self.raw.data {
525            if i != 0 {
526                return false;
527            }
528        }
529
530        true
531    }
532
533    /// Return a String representation of GUID
534    #[doc(alias = "SDL_JoystickGetGUIDString")]
535    pub fn string(&self) -> String {
536        // Doc says "buf should supply at least 33bytes". I took that
537        // to mean that 33bytes should be enough in all cases, but
538        // maybe I'm wrong?
539        let mut buf = [0; 33];
540
541        let len = buf.len() as i32;
542        let c_str = buf.as_mut_ptr();
543
544        unsafe {
545            sys::SDL_JoystickGetGUIDString(self.raw, c_str, len);
546        }
547
548        // The buffer should always be NUL terminated (the
549        // documentation doesn't explicitly say it but I checked the
550        // code)
551        if c_str.is_null() {
552            String::new()
553        } else {
554            unsafe {
555                CStr::from_ptr(c_str as *const _)
556                    .to_str()
557                    .unwrap()
558                    .to_string()
559            }
560        }
561    }
562
563    /// Return a copy of the internal SDL_JoystickGUID
564    pub fn raw(self) -> sys::SDL_JoystickGUID {
565        self.raw
566    }
567}
568
569impl Display for Guid {
570    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
571        write!(f, "{}", self.string())
572    }
573}
574
575/// This is represented in SDL2 as a bitfield but obviously not all
576/// combinations make sense: 5 for instance would mean up and down at
577/// the same time... To simplify things I turn it into an enum which
578/// is how the SDL2 docs present it anyway (using macros).
579#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
580pub enum HatState {
581    Centered = 0,
582    Up = 0x01,
583    Right = 0x02,
584    Down = 0x04,
585    Left = 0x08,
586    RightUp = 0x02 | 0x01,
587    RightDown = 0x02 | 0x04,
588    LeftUp = 0x08 | 0x01,
589    LeftDown = 0x08 | 0x04,
590}
591
592impl HatState {
593    pub fn from_raw(raw: u8) -> HatState {
594        match raw {
595            0 => HatState::Centered,
596            1 => HatState::Up,
597            2 => HatState::Right,
598            4 => HatState::Down,
599            8 => HatState::Left,
600            3 => HatState::RightUp,
601            6 => HatState::RightDown,
602            9 => HatState::LeftUp,
603            12 => HatState::LeftDown,
604
605            // The Xinput driver on Windows can report hat states on certain hardware that don't
606            // make any sense from a gameplay perspective, and so aren't worth putting in the
607            // HatState enumeration.
608            _ => HatState::Centered,
609        }
610    }
611
612    pub fn to_raw(self) -> u8 {
613        match self {
614            HatState::Centered => 0,
615            HatState::Up => 1,
616            HatState::Right => 2,
617            HatState::Down => 4,
618            HatState::Left => 8,
619            HatState::RightUp => 3,
620            HatState::RightDown => 6,
621            HatState::LeftUp => 9,
622            HatState::LeftDown => 12,
623        }
624    }
625}
626
627/// Convert C string `c_str` to a String. Return an empty string if
628/// `c_str` is NULL.
629fn c_str_to_string(c_str: *const c_char) -> String {
630    if c_str.is_null() {
631        String::new()
632    } else {
633        let bytes = unsafe { CStr::from_ptr(c_str as *const _).to_bytes() };
634
635        String::from_utf8_lossy(bytes).to_string()
636    }
637}