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_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 #[doc(alias = "SDL_JoystickEventState")]
83 pub fn set_event_state(&self, state: bool) {
84 unsafe { sys::SDL_JoystickEventState(state as i32) };
85 }
86
87 #[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 #[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
137pub 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 #[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 #[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 panic!("{}", get_error())
172 } else {
173 result as u32
174 }
175 }
176
177 #[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 panic!("{}", get_error())
187 } else {
188 guid
189 }
190 }
191
192 #[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 #[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 panic!("{}", get_error())
223 } else {
224 result as u32
225 }
226 }
227
228 #[doc(alias = "SDL_JoystickGetAxis")]
232 pub fn axis(&self, axis: u32) -> Result<i16, IntegerOrSdlError> {
233 use crate::common::IntegerOrSdlError::*;
234 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 #[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 panic!("{}", get_error())
264 } else {
265 result as u32
266 }
267 }
268
269 #[doc(alias = "SDL_JoystickGetButton")]
273 pub fn button(&self, button: u32) -> Result<bool, IntegerOrSdlError> {
274 use crate::common::IntegerOrSdlError::*;
275 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 Ok(false)
290 } else {
291 Err(SdlError(err))
292 }
293 }
294 _ => unreachable!(),
296 }
297 }
298
299 #[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 panic!("{}", get_error())
307 } else {
308 result as u32
309 }
310 }
311
312 #[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 #[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 panic!("{}", get_error())
338 } else {
339 result as u32
340 }
341 }
342
343 #[doc(alias = "SDL_JoystickGetHat")]
345 pub fn hat(&self, hat: u32) -> Result<HatState, IntegerOrSdlError> {
346 use crate::common::IntegerOrSdlError::*;
347 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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#[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 #[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 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 #[doc(alias = "SDL_JoystickGetGUIDString")]
535 pub fn string(&self) -> String {
536 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 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 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#[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 _ => 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
627fn 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}