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 #[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 #[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 #[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 #[doc(alias = "SDL_JoystickGetDevicePlayerIndex")]
65 pub fn player_index_for_device_id(
66 &self,
67 device_index: u32,
68 ) -> Result<Option<u32>, IntegerOrSdlError> {
69 let device_index = validate_int(device_index, "device_index")?;
70
71 let player_index = unsafe { sys::SDL_JoystickGetDevicePlayerIndex(device_index) };
72
73 if player_index < 0 {
75 Ok(None)
76 } else {
77 Ok(Some(player_index as u32))
78 }
79 }
80
81 #[doc(alias = "SDL_JoystickFromPlayerIndex")]
83 pub fn instance_id_for_player_index(
84 &self,
85 player_index: u32,
86 ) -> Result<Option<u32>, IntegerOrSdlError> {
87 let player_index = validate_int(player_index, "player_index")?;
88
89 let joystick = unsafe { sys::SDL_JoystickFromPlayerIndex(player_index) };
90
91 if joystick.is_null() {
92 Ok(None)
93 } else {
94 let result = unsafe { sys::SDL_JoystickInstanceID(joystick) };
95
96 if result < 0 {
97 panic!("{}", get_error())
99 } else {
100 Ok(Some(result as u32))
101 }
102 }
103 }
104
105 #[doc(alias = "SDL_JoystickGetDeviceGUID")]
107 pub fn device_guid(&self, joystick_index: u32) -> Result<Guid, IntegerOrSdlError> {
108 use crate::common::IntegerOrSdlError::*;
109 let joystick_index = validate_int(joystick_index, "joystick_index")?;
110
111 let raw = unsafe { sys::SDL_JoystickGetDeviceGUID(joystick_index) };
112
113 let guid = Guid { raw };
114
115 if guid.is_zero() {
116 Err(SdlError(get_error()))
117 } else {
118 Ok(guid)
119 }
120 }
121
122 #[doc(alias = "SDL_JoystickEventState")]
125 pub fn set_event_state(&self, state: bool) {
126 unsafe { sys::SDL_JoystickEventState(state as i32) };
127 }
128
129 #[doc(alias = "SDL_JoystickEventState")]
131 pub fn event_state(&self) -> bool {
132 unsafe { sys::SDL_JoystickEventState(sys::SDL_QUERY) == sys::SDL_ENABLE as i32 }
133 }
134
135 #[inline]
137 #[doc(alias = "SDL_JoystickUpdate")]
138 pub fn update(&self) {
139 unsafe { sys::SDL_JoystickUpdate() };
140 }
141}
142
143#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
144#[repr(i32)]
145pub enum PowerLevel {
146 Unknown = SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_UNKNOWN as i32,
147 Empty = SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_EMPTY as i32,
148 Low = SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_LOW as i32,
149 Medium = SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_MEDIUM as i32,
150 Full = SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_FULL as i32,
151 Wired = SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_WIRED as i32,
152}
153
154impl PowerLevel {
155 pub fn from_ll(raw: SDL_JoystickPowerLevel) -> PowerLevel {
156 match raw {
157 SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_UNKNOWN => PowerLevel::Unknown,
158 SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_EMPTY => PowerLevel::Empty,
159 SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_LOW => PowerLevel::Low,
160 SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_MEDIUM => PowerLevel::Medium,
161 SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_FULL => PowerLevel::Full,
162 SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_WIRED => PowerLevel::Wired,
163 _ => panic!("Unexpected power level: {}", raw as i32),
164 }
165 }
166
167 pub fn to_ll(self) -> SDL_JoystickPowerLevel {
168 match self {
169 PowerLevel::Unknown => SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_UNKNOWN,
170 PowerLevel::Empty => SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_EMPTY,
171 PowerLevel::Low => SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_LOW,
172 PowerLevel::Medium => SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_MEDIUM,
173 PowerLevel::Full => SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_FULL,
174 PowerLevel::Wired => SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_WIRED,
175 }
176 }
177}
178
179pub struct Joystick {
181 subsystem: JoystickSubsystem,
182 raw: *mut sys::SDL_Joystick,
183}
184
185impl Joystick {
186 #[inline]
187 pub const fn subsystem(&self) -> &JoystickSubsystem {
188 &self.subsystem
189 }
190
191 #[doc(alias = "SDL_JoystickName")]
194 pub fn name(&self) -> String {
195 let name = unsafe { sys::SDL_JoystickName(self.raw) };
196
197 c_str_to_string(name)
198 }
199
200 #[doc(alias = "SDL_JoystickGetAttached")]
203 pub fn attached(&self) -> bool {
204 unsafe { sys::SDL_JoystickGetAttached(self.raw) != sys::SDL_bool::SDL_FALSE }
205 }
206
207 #[doc(alias = "SDL_JoystickInstanceID")]
208 pub fn instance_id(&self) -> u32 {
209 let result = unsafe { sys::SDL_JoystickInstanceID(self.raw) };
210
211 if result < 0 {
212 panic!("{}", get_error())
214 } else {
215 result as u32
216 }
217 }
218
219 #[doc(alias = "SDL_JoystickGetGUID")]
221 pub fn guid(&self) -> Guid {
222 let raw = unsafe { sys::SDL_JoystickGetGUID(self.raw) };
223
224 let guid = Guid { raw };
225
226 if guid.is_zero() {
227 panic!("{}", get_error())
229 } else {
230 guid
231 }
232 }
233
234 #[doc(alias = "SDL_JoystickCurrentPowerLevel")]
236 pub fn power_level(&self) -> Result<PowerLevel, IntegerOrSdlError> {
237 use crate::common::IntegerOrSdlError::*;
238 clear_error();
239
240 let result = unsafe { sys::SDL_JoystickCurrentPowerLevel(self.raw) };
241
242 let state = PowerLevel::from_ll(result);
243
244 if result != SDL_JoystickPowerLevel::SDL_JOYSTICK_POWER_UNKNOWN {
245 Ok(state)
246 } else {
247 let err = get_error();
248
249 if err.is_empty() {
250 Ok(state)
251 } else {
252 Err(SdlError(err))
253 }
254 }
255 }
256
257 #[doc(alias = "SDL_JoystickNumAxes")]
259 pub fn num_axes(&self) -> u32 {
260 let result = unsafe { sys::SDL_JoystickNumAxes(self.raw) };
261
262 if result < 0 {
263 panic!("{}", get_error())
265 } else {
266 result as u32
267 }
268 }
269
270 #[doc(alias = "SDL_JoystickGetAxis")]
274 pub fn axis(&self, axis: u32) -> Result<i16, IntegerOrSdlError> {
275 use crate::common::IntegerOrSdlError::*;
276 clear_error();
281
282 let axis = validate_int(axis, "axis")?;
283 let pos = unsafe { sys::SDL_JoystickGetAxis(self.raw, axis) };
284
285 if pos != 0 {
286 Ok(pos)
287 } else {
288 let err = get_error();
289
290 if err.is_empty() {
291 Ok(pos)
292 } else {
293 Err(SdlError(err))
294 }
295 }
296 }
297
298 #[doc(alias = "SDL_JoystickNumButtons")]
300 pub fn num_buttons(&self) -> u32 {
301 let result = unsafe { sys::SDL_JoystickNumButtons(self.raw) };
302
303 if result < 0 {
304 panic!("{}", get_error())
306 } else {
307 result as u32
308 }
309 }
310
311 #[doc(alias = "SDL_JoystickGetButton")]
315 pub fn button(&self, button: u32) -> Result<bool, IntegerOrSdlError> {
316 use crate::common::IntegerOrSdlError::*;
317 clear_error();
320
321 let button = validate_int(button, "button")?;
322 let pressed = unsafe { sys::SDL_JoystickGetButton(self.raw, button) };
323
324 match pressed {
325 1 => Ok(true),
326 0 => {
327 let err = get_error();
328
329 if err.is_empty() {
330 Ok(false)
332 } else {
333 Err(SdlError(err))
334 }
335 }
336 _ => unreachable!(),
338 }
339 }
340
341 #[doc(alias = "SDL_JoystickNumBalls")]
343 pub fn num_balls(&self) -> u32 {
344 let result = unsafe { sys::SDL_JoystickNumBalls(self.raw) };
345
346 if result < 0 {
347 panic!("{}", get_error())
349 } else {
350 result as u32
351 }
352 }
353
354 #[doc(alias = "SDL_JoystickGetBall")]
357 pub fn ball(&self, ball: u32) -> Result<(i32, i32), IntegerOrSdlError> {
358 use crate::common::IntegerOrSdlError::*;
359 let mut dx = 0;
360 let mut dy = 0;
361
362 let ball = validate_int(ball, "ball")?;
363 let result = unsafe { sys::SDL_JoystickGetBall(self.raw, ball, &mut dx, &mut dy) };
364
365 if result == 0 {
366 Ok((dx, dy))
367 } else {
368 Err(SdlError(get_error()))
369 }
370 }
371
372 #[doc(alias = "SDL_JoystickNumHats")]
374 pub fn num_hats(&self) -> u32 {
375 let result = unsafe { sys::SDL_JoystickNumHats(self.raw) };
376
377 if result < 0 {
378 panic!("{}", get_error())
380 } else {
381 result as u32
382 }
383 }
384
385 #[doc(alias = "SDL_JoystickGetHat")]
387 pub fn hat(&self, hat: u32) -> Result<HatState, IntegerOrSdlError> {
388 use crate::common::IntegerOrSdlError::*;
389 clear_error();
393
394 let hat = validate_int(hat, "hat")?;
395 let result = unsafe { sys::SDL_JoystickGetHat(self.raw, hat) };
396
397 let state = HatState::from_raw(result as u8);
398
399 if result != 0 {
400 Ok(state)
401 } else {
402 let err = get_error();
403
404 if err.is_empty() {
405 Ok(state)
406 } else {
407 Err(SdlError(err))
408 }
409 }
410 }
411
412 #[doc(alias = "SDL_JoystickRumble")]
424 pub fn set_rumble(
425 &mut self,
426 low_frequency_rumble: u16,
427 high_frequency_rumble: u16,
428 duration_ms: u32,
429 ) -> Result<(), IntegerOrSdlError> {
430 let result = unsafe {
431 sys::SDL_JoystickRumble(
432 self.raw,
433 low_frequency_rumble,
434 high_frequency_rumble,
435 duration_ms,
436 )
437 };
438
439 if result != 0 {
440 Err(IntegerOrSdlError::SdlError(get_error()))
441 } else {
442 Ok(())
443 }
444 }
445
446 #[doc(alias = "SDL_JoystickRumbleTriggers")]
448 pub fn set_rumble_triggers(
449 &mut self,
450 left_rumble: u16,
451 right_rumble: u16,
452 duration_ms: u32,
453 ) -> Result<(), IntegerOrSdlError> {
454 let result = unsafe {
455 sys::SDL_JoystickRumbleTriggers(self.raw, left_rumble, right_rumble, duration_ms)
456 };
457
458 if result != 0 {
459 Err(IntegerOrSdlError::SdlError(get_error()))
460 } else {
461 Ok(())
462 }
463 }
464
465 #[doc(alias = "SDL_JoystickHasLED")]
467 pub fn has_led(&self) -> bool {
468 let result = unsafe { sys::SDL_JoystickHasLED(self.raw) };
469
470 match result {
471 sys::SDL_bool::SDL_FALSE => false,
472 sys::SDL_bool::SDL_TRUE => true,
473 }
474 }
475
476 #[doc(alias = "SDL_JoystickHasRumble")]
478 pub fn has_rumble(&self) -> bool {
479 let result = unsafe { sys::SDL_JoystickHasRumble(self.raw) };
480
481 match result {
482 sys::SDL_bool::SDL_FALSE => false,
483 sys::SDL_bool::SDL_TRUE => true,
484 }
485 }
486
487 #[doc(alias = "SDL_JoystickHasRumbleTriggers")]
489 pub fn has_rumble_triggers(&self) -> bool {
490 let result = unsafe { sys::SDL_JoystickHasRumbleTriggers(self.raw) };
491
492 match result {
493 sys::SDL_bool::SDL_FALSE => false,
494 sys::SDL_bool::SDL_TRUE => true,
495 }
496 }
497
498 #[doc(alias = "SDL_JoystickSetLED")]
500 pub fn set_led(&mut self, red: u8, green: u8, blue: u8) -> Result<(), IntegerOrSdlError> {
501 let result = unsafe { sys::SDL_JoystickSetLED(self.raw, red, green, blue) };
502
503 if result != 0 {
504 Err(IntegerOrSdlError::SdlError(get_error()))
505 } else {
506 Ok(())
507 }
508 }
509
510 #[doc(alias = "SDL_JoystickSendEffect")]
512 pub fn send_effect(&mut self, data: &[u8]) -> Result<(), IntegerOrSdlError> {
513 let result = unsafe {
514 sys::SDL_JoystickSendEffect(
515 self.raw,
516 data.as_ptr() as *const libc::c_void,
517 data.len() as i32,
518 )
519 };
520
521 if result != 0 {
522 Err(IntegerOrSdlError::SdlError(get_error()))
523 } else {
524 Ok(())
525 }
526 }
527
528 #[doc(alias = "SDL_JoystickSetPlayerIndex")]
530 pub fn set_player_index(&mut self, player_index: Option<u32>) -> Result<(), IntegerOrSdlError> {
531 let player_index = match player_index {
532 None => -1,
533 Some(player_index) => validate_int(player_index, "player_index")?,
534 };
535
536 unsafe { sys::SDL_JoystickSetPlayerIndex(self.raw, player_index) };
537
538 Ok(())
539 }
540
541 #[doc(alias = "SDL_JoystickGetPlayerIndex")]
543 pub fn get_player_index(&self) -> Option<u32> {
544 let player_index = unsafe { sys::SDL_JoystickGetPlayerIndex(self.raw) };
545
546 if player_index < 0 {
548 None
549 } else {
550 Some(player_index as u32)
551 }
552 }
553}
554
555impl Drop for Joystick {
556 #[doc(alias = "SDL_JoystickClose")]
557 fn drop(&mut self) {
558 if self.attached() {
559 unsafe { sys::SDL_JoystickClose(self.raw) }
560 }
561 }
562}
563
564#[derive(Copy, Clone)]
567pub struct Guid {
568 raw: sys::SDL_JoystickGUID,
569}
570
571impl PartialEq for Guid {
572 fn eq(&self, other: &Guid) -> bool {
573 self.raw.data == other.raw.data
574 }
575}
576
577impl Eq for Guid {}
578
579impl Guid {
580 #[doc(alias = "SDL_JoystickGetGUIDFromString")]
582 pub fn from_string(guid: &str) -> Result<Guid, NulError> {
583 let guid = CString::new(guid)?;
584
585 let raw = unsafe { sys::SDL_JoystickGetGUIDFromString(guid.as_ptr() as *const c_char) };
586
587 Ok(Guid { raw })
588 }
589
590 pub fn is_zero(&self) -> bool {
592 for &i in &self.raw.data {
593 if i != 0 {
594 return false;
595 }
596 }
597
598 true
599 }
600
601 #[doc(alias = "SDL_JoystickGetGUIDString")]
603 pub fn string(&self) -> String {
604 let mut buf = [0; 33];
608
609 let len = buf.len() as i32;
610 let c_str = buf.as_mut_ptr();
611
612 unsafe {
613 sys::SDL_JoystickGetGUIDString(self.raw, c_str, len);
614 }
615
616 if c_str.is_null() {
620 String::new()
621 } else {
622 unsafe {
623 CStr::from_ptr(c_str as *const _)
624 .to_str()
625 .unwrap()
626 .to_string()
627 }
628 }
629 }
630
631 pub fn raw(self) -> sys::SDL_JoystickGUID {
633 self.raw
634 }
635}
636
637impl Display for Guid {
638 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
639 write!(f, "{}", self.string())
640 }
641}
642
643#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
648pub enum HatState {
649 Centered = 0,
650 Up = 0x01,
651 Right = 0x02,
652 Down = 0x04,
653 Left = 0x08,
654 RightUp = 0x02 | 0x01,
655 RightDown = 0x02 | 0x04,
656 LeftUp = 0x08 | 0x01,
657 LeftDown = 0x08 | 0x04,
658}
659
660impl HatState {
661 pub fn from_raw(raw: u8) -> HatState {
662 match raw {
663 0 => HatState::Centered,
664 1 => HatState::Up,
665 2 => HatState::Right,
666 4 => HatState::Down,
667 8 => HatState::Left,
668 3 => HatState::RightUp,
669 6 => HatState::RightDown,
670 9 => HatState::LeftUp,
671 12 => HatState::LeftDown,
672
673 _ => HatState::Centered,
677 }
678 }
679
680 pub fn to_raw(self) -> u8 {
681 match self {
682 HatState::Centered => 0,
683 HatState::Up => 1,
684 HatState::Right => 2,
685 HatState::Down => 4,
686 HatState::Left => 8,
687 HatState::RightUp => 3,
688 HatState::RightDown => 6,
689 HatState::LeftUp => 9,
690 HatState::LeftDown => 12,
691 }
692 }
693}
694
695fn c_str_to_string(c_str: *const c_char) -> String {
698 if c_str.is_null() {
699 String::new()
700 } else {
701 let bytes = unsafe { CStr::from_ptr(c_str as *const _).to_bytes() };
702
703 String::from_utf8_lossy(bytes).to_string()
704 }
705}