1use crate::sys;
2use crate::clear_error;
5use crate::common::{validate_int, IntegerOrSdlError};
6use crate::get_error;
7use crate::guid::Guid;
8use crate::Error;
9use crate::JoystickSubsystem;
10use libc::{c_char, c_void};
11use std::ffi::CStr;
12use std::fmt;
13use sys::power::{SDL_PowerState, SDL_POWERSTATE_UNKNOWN};
14use sys::stdinc::SDL_free;
15
16pub type JoystickId = sys::joystick::SDL_JoystickID;
17
18impl JoystickSubsystem {
19 #[doc(alias = "SDL_GetJoysticks")]
21 pub fn joysticks(&self) -> Result<Vec<JoystickId>, Error> {
22 let mut num_joysticks: i32 = 0;
23 unsafe {
24 let joystick_ids = sys::joystick::SDL_GetJoysticks(&mut num_joysticks);
25 if joystick_ids.is_null() {
26 Err(get_error())
27 } else {
28 let mut instances = Vec::new();
29 for i in 0..num_joysticks {
30 let id = *joystick_ids.offset(i as isize);
31 instances.push(id);
32 }
33 SDL_free(joystick_ids as *mut c_void);
34 Ok(instances)
35 }
36 }
37 }
38
39 #[doc(alias = "SDL_OpenJoystick")]
41 pub fn open(&self, joystick_id: JoystickId) -> Result<Joystick, IntegerOrSdlError> {
42 use crate::common::IntegerOrSdlError::*;
43 let joystick = unsafe { sys::joystick::SDL_OpenJoystick(joystick_id) };
44
45 if joystick.is_null() {
46 Err(SdlError(get_error()))
47 } else {
48 Ok(Joystick {
49 subsystem: self.clone(),
50 raw: joystick,
51 })
52 }
53 }
54
55 #[doc(alias = "SDL_SetJoystickEventsEnabled")]
58 pub fn set_joystick_events_enabled(&self, state: bool) {
59 unsafe { sys::joystick::SDL_SetJoystickEventsEnabled(state) };
60 }
61
62 #[doc(alias = "SDL_JoystickEventsEnabled")]
64 pub fn event_state(&self) -> bool {
65 unsafe { sys::joystick::SDL_JoystickEventsEnabled() }
66 }
67
68 #[inline]
70 #[doc(alias = "SDL_UpdateJoysticks")]
71 pub fn update(&self) {
72 unsafe { sys::joystick::SDL_UpdateJoysticks() };
73 }
74}
75
76pub struct PowerInfo {
78 pub state: PowerLevel,
79 pub percentage: i32,
80}
81
82impl fmt::Debug for PowerInfo {
83 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84 write!(
85 f,
86 "PowerInfo {{ state: {:?}, percentage: {} }}",
87 self.state, self.percentage
88 )
89 }
90}
91
92#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
93#[repr(i32)]
94pub enum PowerLevel {
95 Unknown = SDL_PowerState::UNKNOWN.0,
96 Error = SDL_PowerState::ERROR.0,
97 OnBattery = SDL_PowerState::ON_BATTERY.0,
98 NoBattery = SDL_PowerState::NO_BATTERY.0,
99 Charging = SDL_PowerState::CHARGING.0,
100 Charged = SDL_PowerState::CHARGED.0,
101}
102
103impl PowerLevel {
104 pub fn from_ll(raw: SDL_PowerState) -> PowerLevel {
105 match raw {
106 SDL_PowerState::UNKNOWN => PowerLevel::Unknown,
107 SDL_PowerState::ERROR => PowerLevel::Error,
108 SDL_PowerState::ON_BATTERY => PowerLevel::OnBattery,
109 SDL_PowerState::NO_BATTERY => PowerLevel::NoBattery,
110 SDL_PowerState::CHARGING => PowerLevel::Charging,
111 SDL_PowerState::CHARGED => PowerLevel::Charged,
112 _ => panic!("Unexpected power level"),
113 }
114 }
115
116 pub fn to_ll(self) -> SDL_PowerState {
117 match self {
118 PowerLevel::Unknown => SDL_PowerState::UNKNOWN,
119 PowerLevel::Error => SDL_PowerState::ERROR,
120 PowerLevel::OnBattery => SDL_PowerState::ON_BATTERY,
121 PowerLevel::NoBattery => SDL_PowerState::NO_BATTERY,
122 PowerLevel::Charging => SDL_PowerState::CHARGING,
123 PowerLevel::Charged => SDL_PowerState::CHARGED,
124 }
125 }
126}
127
128pub struct Joystick {
130 subsystem: JoystickSubsystem,
131 raw: *mut sys::joystick::SDL_Joystick,
132}
133
134impl Joystick {
135 #[inline]
136 pub const fn subsystem(&self) -> &JoystickSubsystem {
137 &self.subsystem
138 }
139
140 #[doc(alias = "SDL_GetJoystickName")]
143 pub fn name(&self) -> String {
144 let name = unsafe { sys::joystick::SDL_GetJoystickName(self.raw) };
145
146 c_str_to_string(name)
147 }
148
149 #[doc(alias = "SDL_JoystickConnected")]
152 pub fn connected(&self) -> bool {
153 unsafe { sys::joystick::SDL_JoystickConnected(self.raw) }
154 }
155
156 #[doc(alias = "SDL_GetJoystickID")]
157 pub fn id(&self) -> u32 {
158 let result = unsafe { sys::joystick::SDL_GetJoystickID(self.raw) };
159
160 if result == 0 {
161 panic!("{}", get_error())
163 } else {
164 result as u32
165 }
166 }
167
168 #[doc(alias = "SDL_GetJoystickGUID")]
170 pub fn guid(&self) -> Guid {
171 let raw = unsafe { sys::joystick::SDL_GetJoystickGUID(self.raw) };
172
173 let guid = Guid { raw };
174
175 if guid.is_zero() {
176 panic!("{}", get_error())
178 } else {
179 guid
180 }
181 }
182
183 #[doc(alias = "SDL_GetJoystickPowerLevel")]
187 pub fn power_info(&self) -> Result<PowerInfo, IntegerOrSdlError> {
188 use crate::common::IntegerOrSdlError::*;
189 clear_error();
190
191 let mut power_pct: core::ffi::c_int = 0;
192 let result = unsafe { sys::joystick::SDL_GetJoystickPowerInfo(self.raw, &mut power_pct) };
193
194 let state = PowerLevel::from_ll(result);
195
196 if result != SDL_POWERSTATE_UNKNOWN {
197 Ok(PowerInfo {
198 state,
199 percentage: power_pct,
200 })
201 } else {
202 let err = get_error();
203
204 if err.is_empty() {
205 Ok(PowerInfo {
206 state,
207 percentage: power_pct,
208 })
209 } else {
210 Err(SdlError(err))
211 }
212 }
213 }
214
215 #[doc(alias = "SDL_GetNumJoystickAxes")]
217 pub fn num_axes(&self) -> u32 {
218 let result = unsafe { sys::joystick::SDL_GetNumJoystickAxes(self.raw) };
219
220 if result < 0 {
221 panic!("{}", get_error())
223 } else {
224 result as u32
225 }
226 }
227
228 #[doc(alias = "SDL_GetJoystickAxis")]
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::joystick::SDL_GetJoystickAxis(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_GetNumJoystickButtons")]
258 pub fn num_buttons(&self) -> u32 {
259 let result = unsafe { sys::joystick::SDL_GetNumJoystickButtons(self.raw) };
260
261 if result < 0 {
262 panic!("{}", get_error())
264 } else {
265 result as u32
266 }
267 }
268
269 #[doc(alias = "SDL_GetJoystickButton")]
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::joystick::SDL_GetJoystickButton(self.raw, button) };
281
282 match pressed {
283 true => Ok(true),
284 false => {
285 let err = get_error();
286
287 if err.is_empty() {
288 Ok(false)
290 } else {
291 Err(SdlError(err))
292 }
293 }
294 }
295 }
296
297 #[doc(alias = "SDL_GetNumJoystickHats")]
299 pub fn num_hats(&self) -> u32 {
300 let result = unsafe { sys::joystick::SDL_GetNumJoystickHats(self.raw) };
301
302 if result < 0 {
303 panic!("{}", get_error())
305 } else {
306 result as u32
307 }
308 }
309
310 #[doc(alias = "SDL_GetJoystickHat")]
312 pub fn hat(&self, hat: u32) -> Result<HatState, IntegerOrSdlError> {
313 use crate::common::IntegerOrSdlError::*;
314 clear_error();
318
319 let hat = validate_int(hat, "hat")?;
320 let result = unsafe { sys::joystick::SDL_GetJoystickHat(self.raw, hat) };
321
322 let state = HatState::from_raw(result as u8);
323
324 if result != 0 {
325 Ok(state)
326 } else {
327 let err = get_error();
328
329 if err.is_empty() {
330 Ok(state)
331 } else {
332 Err(SdlError(err))
333 }
334 }
335 }
336
337 #[doc(alias = "SDL_RumbleJoystick")]
351 pub fn set_rumble(
352 &mut self,
353 low_frequency_rumble: u16,
354 high_frequency_rumble: u16,
355 duration_ms: u32,
356 ) -> bool {
357 unsafe {
358 sys::joystick::SDL_RumbleJoystick(
359 self.raw,
360 low_frequency_rumble,
361 high_frequency_rumble,
362 duration_ms,
363 )
364 }
365 }
366
367 #[doc(alias = "SDL_RumbleJoystickTriggers")]
369 pub fn set_rumble_triggers(
370 &mut self,
371 left_rumble: u16,
372 right_rumble: u16,
373 duration_ms: u32,
374 ) -> Result<(), IntegerOrSdlError> {
375 let result = unsafe {
376 sys::joystick::SDL_RumbleJoystickTriggers(
377 self.raw,
378 left_rumble,
379 right_rumble,
380 duration_ms,
381 )
382 };
383
384 if !result {
385 Err(IntegerOrSdlError::SdlError(get_error()))
386 } else {
387 Ok(())
388 }
389 }
390
391 #[doc(alias = "SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN")]
393 pub unsafe fn has_led(&self) -> bool {
394 let props = unsafe { sys::joystick::SDL_GetJoystickProperties(self.raw) };
395 sys::properties::SDL_GetBooleanProperty(
396 props,
397 sys::joystick::SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN,
398 false,
399 )
400 }
401
402 #[doc(alias = "SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN")]
404 pub unsafe fn has_rumble(&self) -> bool {
405 let props = unsafe { sys::joystick::SDL_GetJoystickProperties(self.raw) };
406 sys::properties::SDL_GetBooleanProperty(
407 props,
408 sys::joystick::SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN,
409 false,
410 )
411 }
412
413 #[doc(alias = "SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN")]
415 pub unsafe fn has_rumble_triggers(&self) -> bool {
416 let props = unsafe { sys::joystick::SDL_GetJoystickProperties(self.raw) };
417 sys::properties::SDL_GetBooleanProperty(
418 props,
419 sys::joystick::SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN,
420 false,
421 )
422 }
423
424 #[doc(alias = "SDL_SetJoystickLED")]
426 pub fn set_led(&mut self, red: u8, green: u8, blue: u8) -> Result<(), IntegerOrSdlError> {
427 let result = unsafe { sys::joystick::SDL_SetJoystickLED(self.raw, red, green, blue) };
428
429 if !result {
430 Err(IntegerOrSdlError::SdlError(get_error()))
431 } else {
432 Ok(())
433 }
434 }
435
436 #[doc(alias = "SDL_SendJoystickEffect")]
438 pub fn send_effect(&mut self, data: &[u8]) -> Result<(), IntegerOrSdlError> {
439 let result = unsafe {
440 sys::joystick::SDL_SendJoystickEffect(
441 self.raw,
442 data.as_ptr() as *const libc::c_void,
443 data.len() as i32,
444 )
445 };
446
447 if !result {
448 Err(IntegerOrSdlError::SdlError(get_error()))
449 } else {
450 Ok(())
451 }
452 }
453}
454
455impl Drop for Joystick {
456 #[doc(alias = "SDL_CloseJoystick")]
457 fn drop(&mut self) {
458 if self.connected() {
459 unsafe { sys::joystick::SDL_CloseJoystick(self.raw) }
460 }
461 }
462}
463
464#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
469pub enum HatState {
470 Centered = 0,
471 Up = 0x01,
472 Right = 0x02,
473 Down = 0x04,
474 Left = 0x08,
475 RightUp = 0x02 | 0x01,
476 RightDown = 0x02 | 0x04,
477 LeftUp = 0x08 | 0x01,
478 LeftDown = 0x08 | 0x04,
479}
480
481impl HatState {
482 pub fn from_raw(raw: u8) -> HatState {
483 match raw {
484 0 => HatState::Centered,
485 1 => HatState::Up,
486 2 => HatState::Right,
487 4 => HatState::Down,
488 8 => HatState::Left,
489 3 => HatState::RightUp,
490 6 => HatState::RightDown,
491 9 => HatState::LeftUp,
492 12 => HatState::LeftDown,
493
494 _ => HatState::Centered,
498 }
499 }
500
501 pub fn to_raw(self) -> u8 {
502 match self {
503 HatState::Centered => 0,
504 HatState::Up => 1,
505 HatState::Right => 2,
506 HatState::Down => 4,
507 HatState::Left => 8,
508 HatState::RightUp => 3,
509 HatState::RightDown => 6,
510 HatState::LeftUp => 9,
511 HatState::LeftDown => 12,
512 }
513 }
514}
515
516#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
517#[repr(i32)]
518pub enum ConnectionState {
519 Invalid = sys::joystick::SDL_JoystickConnectionState::INVALID.0,
520 Unknown = sys::joystick::SDL_JoystickConnectionState::UNKNOWN.0,
521 Wired = sys::joystick::SDL_JoystickConnectionState::WIRED.0,
522 Wireless = sys::joystick::SDL_JoystickConnectionState::WIRELESS.0,
523}
524
525impl ConnectionState {
526 pub fn from_ll(bitflags: sys::joystick::SDL_JoystickConnectionState) -> ConnectionState {
527 match bitflags {
528 sys::joystick::SDL_JoystickConnectionState::UNKNOWN => ConnectionState::Unknown,
529 sys::joystick::SDL_JoystickConnectionState::WIRED => ConnectionState::Wired,
530 sys::joystick::SDL_JoystickConnectionState::WIRELESS => ConnectionState::Wireless,
531 _ => ConnectionState::Invalid,
532 }
533 }
534
535 pub fn to_ll(self) -> sys::joystick::SDL_JoystickConnectionState {
536 match self {
537 ConnectionState::Invalid => sys::joystick::SDL_JoystickConnectionState::INVALID,
538 ConnectionState::Unknown => sys::joystick::SDL_JoystickConnectionState::UNKNOWN,
539 ConnectionState::Wired => sys::joystick::SDL_JoystickConnectionState::WIRED,
540 ConnectionState::Wireless => sys::joystick::SDL_JoystickConnectionState::WIRELESS,
541 }
542 }
543}
544
545fn c_str_to_string(c_str: *const c_char) -> String {
548 if c_str.is_null() {
549 String::new()
550 } else {
551 let bytes = unsafe { CStr::from_ptr(c_str as *const _).to_bytes() };
552
553 String::from_utf8_lossy(bytes).to_string()
554 }
555}