1#![allow(non_upper_case_globals)]
26#![warn(missing_docs)]
27#![forbid(missing_debug_implementations)]
28#![cfg(windows)]
29
30#[macro_use]
31extern crate log;
32
33#[macro_use]
34extern crate lazy_static;
35
36extern crate winapi;
37
38use winapi::shared::guiddef::GUID;
39use winapi::shared::minwindef::{BOOL, BYTE, DWORD, HMODULE, UINT, WORD};
40use winapi::shared::ntdef::LPWSTR;
41use winapi::shared::winerror::{ERROR_DEVICE_NOT_CONNECTED, ERROR_EMPTY, ERROR_SUCCESS};
42use winapi::um::libloaderapi::{GetProcAddress, LoadLibraryW};
43use winapi::um::xinput::*;
44
45pub const XINPUT_GAMEPAD_GUIDE: winapi::shared::minwindef::WORD = 0x0400;
47
48#[repr(C)]
50#[derive(Clone, Copy)]
51#[allow(non_camel_case_types)]
52pub struct XINPUT_CAPABILITIES_EX {
53 pub capabilities: XINPUT_CAPABILITIES,
55 pub vendor_id: WORD,
57 pub product_id: WORD,
59 pub revision_id: WORD,
61 pub a4: DWORD,
63}
64impl ::std::fmt::Debug for XINPUT_CAPABILITIES_EX {
65 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
66 write!(f, "XINPUT_CAPABILITIES_EX (_)")
67 }
68}
69
70use std::fmt::{self, Debug, Formatter};
71
72type XInputEnableFunc = unsafe extern "system" fn(BOOL);
73type XInputGetStateFunc = unsafe extern "system" fn(DWORD, *mut XINPUT_STATE) -> DWORD;
74type XInputSetStateFunc = unsafe extern "system" fn(DWORD, *mut XINPUT_VIBRATION) -> DWORD;
75type XInputGetCapabilitiesFunc =
76 unsafe extern "system" fn(DWORD, DWORD, *mut XINPUT_CAPABILITIES) -> DWORD;
77
78type XInputGetStateExFunc = unsafe extern "system" fn(DWORD, *mut XINPUT_STATE) -> DWORD;
80
81type XInputGetCapabilitiesEx =
83 unsafe extern "system" fn(DWORD, DWORD, DWORD, *mut XINPUT_CAPABILITIES_EX) -> DWORD;
84
85type XInputGetDSoundAudioDeviceGuidsFunc =
87 unsafe extern "system" fn(DWORD, *mut GUID, *mut GUID) -> DWORD;
88
89type XInputGetKeystrokeFunc = unsafe extern "system" fn(DWORD, DWORD, PXINPUT_KEYSTROKE) -> DWORD;
91type XInputGetBatteryInformationFunc =
92 unsafe extern "system" fn(DWORD, BYTE, *mut XINPUT_BATTERY_INFORMATION) -> DWORD;
93
94type XInputGetAudioDeviceIdsFunc =
96 unsafe extern "system" fn(DWORD, LPWSTR, *mut UINT, LPWSTR, *mut UINT) -> DWORD;
97
98#[derive(Clone)]
100pub struct XInputHandle {
101 handle: HMODULE,
102 xinput_enable: XInputEnableFunc,
103 xinput_get_state: XInputGetStateFunc,
104 xinput_set_state: XInputSetStateFunc,
105 xinput_get_capabilities: XInputGetCapabilitiesFunc,
106 opt_xinput_get_state_ex: Option<XInputGetStateExFunc>,
107 opt_xinput_get_capabilities_ex: Option<XInputGetCapabilitiesEx>,
108 opt_xinput_get_keystroke: Option<XInputGetKeystrokeFunc>,
109 opt_xinput_get_battery_information: Option<XInputGetBatteryInformationFunc>,
110 _opt_xinput_get_audio_device_ids: Option<XInputGetAudioDeviceIdsFunc>,
112 _opt_xinput_get_dsound_audio_device_guids: Option<XInputGetDSoundAudioDeviceGuidsFunc>,
113}
114
115impl Debug for XInputHandle {
116 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
117 write!(f, "XInputHandle(handle = {:?})", self.handle)
118 }
119}
120
121unsafe impl Send for XInputHandle {}
122unsafe impl Sync for XInputHandle {}
123
124lazy_static! {
125 static ref GLOBAL_XINPUT_HANDLE: Result<XInputHandle, XInputLoadingFailure> =
126 XInputHandle::load_default();
127}
128
129pub(crate) struct WideNullU16<'a>(&'a [u16; ::winapi::shared::minwindef::MAX_PATH]);
131impl<'a> ::std::fmt::Debug for WideNullU16<'a> {
132 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
133 for &u in self.0.iter() {
134 if u == 0 {
135 break;
136 } else {
137 write!(f, "{}", u as u8 as char)?
138 }
139 }
140 Ok(())
141 }
142}
143
144pub(crate) fn wide_null<S: AsRef<str>>(s: S) -> [u16; ::winapi::shared::minwindef::MAX_PATH] {
146 let mut output: [u16; ::winapi::shared::minwindef::MAX_PATH] =
147 [0; ::winapi::shared::minwindef::MAX_PATH];
148 let mut i = 0;
149 for u in s.as_ref().encode_utf16() {
150 if i == output.len() - 1 {
151 break;
152 } else {
153 output[i] = u;
154 }
155 i += 1;
156 }
157 output[i] = 0;
158 output
159}
160
161#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
163pub enum XInputLoadingFailure {
164 #[deprecated]
168 AlreadyLoading,
169 AlreadyActive,
172 UnknownState,
175 NoDLL,
179 NoPointers,
184}
185
186impl XInputHandle {
187 pub fn load_default() -> Result<XInputHandle, XInputLoadingFailure> {
206 let xinput14 = "xinput1_4.dll";
207 let xinput13 = "xinput1_3.dll";
208 let xinput12 = "xinput1_2.dll";
209 let xinput11 = "xinput1_1.dll";
210 let xinput91 = "xinput9_1_0.dll";
211
212 for lib_name in [xinput14, xinput13, xinput12, xinput11, xinput91] {
213 if let Ok(handle) = XInputHandle::load(lib_name) {
214 return Ok(handle);
215 }
216 }
217
218 debug!("Failure: XInput could not be loaded.");
219 Err(XInputLoadingFailure::NoDLL)
220 }
221
222 pub fn load<S: AsRef<str>>(s: S) -> Result<XInputHandle, XInputLoadingFailure> {
224 let lib_name = wide_null(s);
225 trace!(
226 "Attempting to load XInput DLL: {:?}",
227 WideNullU16(&lib_name)
228 );
229 let xinput_handle = unsafe { LoadLibraryW(lib_name.as_ptr()) };
232 if !xinput_handle.is_null() {
233 debug!("Success: XInput Loaded: {:?}", WideNullU16(&lib_name));
234 }
235
236 let enable_name = b"XInputEnable\0";
237 let get_state_name = b"XInputGetState\0";
238 let set_state_name = b"XInputSetState\0";
239 let get_capabilities_name = b"XInputGetCapabilities\0";
240 let get_keystroke_name = b"XInputGetKeystroke\0";
241 let get_battery_information_name = b"XInputGetBatteryInformation\0";
242 let get_audio_device_ids_name = b"XInputGetAudioDeviceIds\0";
243 let get_dsound_audio_device_guids_name = b"XInputGetDSoundAudioDeviceGuids\0";
244
245 let mut opt_xinput_enable = None;
246 let mut opt_xinput_get_state = None;
247 let mut opt_xinput_get_state_ex = None;
248 let mut opt_xinput_set_state = None;
249 let mut opt_xinput_get_capabilities = None;
250 let mut opt_xinput_get_capabilities_ex = None;
251 let mut opt_xinput_get_keystroke = None;
252 let mut opt_xinput_get_battery_information = None;
253 let mut opt_xinput_get_audio_device_ids = None;
254 let mut opt_xinput_get_dsound_audio_device_guids = None;
255
256 unsafe {
257 let enable_ptr = GetProcAddress(xinput_handle, enable_name.as_ptr() as *mut i8);
258 if !enable_ptr.is_null() {
259 trace!("Found XInputEnable.");
260 opt_xinput_enable = Some(::std::mem::transmute(enable_ptr));
261 } else {
262 trace!("Could not find XInputEnable.");
263 }
264 }
265
266 unsafe {
267 let get_state_ptr = GetProcAddress(xinput_handle, get_state_name.as_ptr() as *mut i8);
268 if !get_state_ptr.is_null() {
269 trace!("Found XInputGetState.");
270 opt_xinput_get_state = Some(::std::mem::transmute(get_state_ptr));
271 } else {
272 trace!("Could not find XInputGetState.");
273 }
274 }
275
276 unsafe {
277 let set_state_ptr = GetProcAddress(xinput_handle, set_state_name.as_ptr() as *mut i8);
278 if !set_state_ptr.is_null() {
279 trace!("Found XInputSetState.");
280 opt_xinput_set_state = Some(::std::mem::transmute(set_state_ptr));
281 } else {
282 trace!("Could not find XInputSetState.");
283 }
284 }
285
286 unsafe {
287 let get_state_ex_ptr = GetProcAddress(xinput_handle, 100_i32 as winapi::um::winnt::LPCSTR);
288 if !get_state_ex_ptr.is_null() {
289 trace!("Found XInputGetStateEx.");
290 opt_xinput_get_state_ex = Some(::std::mem::transmute(get_state_ex_ptr));
291 } else {
292 trace!("Could not find XInputGetStateEx.");
293 }
294 }
295
296 unsafe {
297 let get_capabilities_ptr =
298 GetProcAddress(xinput_handle, get_capabilities_name.as_ptr() as *mut i8);
299 if !get_capabilities_ptr.is_null() {
300 trace!("Found XInputGetCapabilities.");
301 opt_xinput_get_capabilities = Some(::std::mem::transmute(get_capabilities_ptr));
302 } else {
303 trace!("Could not find XInputGetCapabilities.");
304 }
305 }
306
307 unsafe {
308 let get_capabilities_ptr =
309 GetProcAddress(xinput_handle, 108_i32 as winapi::um::winnt::LPCSTR);
310 if !get_capabilities_ptr.is_null() {
311 trace!("Found XInputGetCapabilities.");
312 opt_xinput_get_capabilities_ex = Some(::std::mem::transmute(get_capabilities_ptr));
313 } else {
314 trace!("Could not find XInputGetCapabilitiesEx.");
315 }
316 }
317
318 unsafe {
319 let get_keystroke_ptr = GetProcAddress(xinput_handle, get_keystroke_name.as_ptr() as *mut i8);
320 if !get_keystroke_ptr.is_null() {
321 trace!("Found XInputGetKeystroke.");
322 opt_xinput_get_keystroke = Some(::std::mem::transmute(get_keystroke_ptr));
323 } else {
324 trace!("Could not find XInputGetKeystroke.");
325 }
326 }
327
328 unsafe {
329 let get_battery_information_ptr = GetProcAddress(
330 xinput_handle,
331 get_battery_information_name.as_ptr() as *mut i8,
332 );
333 if !get_battery_information_ptr.is_null() {
334 trace!("Found XInputGetBatteryInformation.");
335 opt_xinput_get_battery_information =
336 Some(::std::mem::transmute(get_battery_information_ptr));
337 } else {
338 trace!("Could not find XInputGetBatteryInformation.");
339 }
340 }
341
342 unsafe {
343 let get_dsound_audio_device_guids_ptr = GetProcAddress(
344 xinput_handle,
345 get_dsound_audio_device_guids_name.as_ptr() as *mut i8,
346 );
347 if !get_dsound_audio_device_guids_ptr.is_null() {
348 trace!("Found XInputGetDSoundAudioDeviceGuids.");
349 opt_xinput_get_dsound_audio_device_guids =
350 Some(::std::mem::transmute(get_dsound_audio_device_guids_ptr));
351 } else {
352 trace!("Could not find XInputGetDSoundAudioDeviceGuids.");
353 }
354 }
355
356 unsafe {
357 let get_audio_device_ids_ptr =
358 GetProcAddress(xinput_handle, get_audio_device_ids_name.as_ptr() as *mut i8);
359 if !get_audio_device_ids_ptr.is_null() {
360 trace!("Found XInputGetAudioDeviceIds.");
361 opt_xinput_get_audio_device_ids = Some(::std::mem::transmute(get_audio_device_ids_ptr));
362 } else {
363 trace!("Could not find XInputGetAudioDeviceIds.");
364 }
365 }
366
367 #[allow(clippy::unnecessary_unwrap)]
368 if opt_xinput_enable.is_some()
369 && opt_xinput_get_state.is_some()
370 && opt_xinput_set_state.is_some()
371 && opt_xinput_get_capabilities.is_some()
372 {
373 debug!("All function pointers loaded successfully.");
374 Ok(XInputHandle {
375 handle: xinput_handle,
376 xinput_enable: opt_xinput_enable.unwrap(),
377 xinput_get_state: opt_xinput_get_state.unwrap(),
378 xinput_set_state: opt_xinput_set_state.unwrap(),
379 xinput_get_capabilities: opt_xinput_get_capabilities.unwrap(),
380 opt_xinput_get_capabilities_ex,
381 opt_xinput_get_state_ex,
382 opt_xinput_get_keystroke,
383 opt_xinput_get_battery_information,
384 _opt_xinput_get_dsound_audio_device_guids: opt_xinput_get_dsound_audio_device_guids,
385 _opt_xinput_get_audio_device_ids: opt_xinput_get_audio_device_ids,
386 })
387 } else {
388 debug!("Could not load the function pointers.");
389 Err(XInputLoadingFailure::NoPointers)
390 }
391 }
392}
393
394#[deprecated]
422pub fn dynamic_load_xinput() -> Result<(), XInputLoadingFailure> {
423 if let Err(err) = *GLOBAL_XINPUT_HANDLE {
424 Err(err)
425 } else {
426 Ok(())
427 }
428}
429
430#[derive(Copy, Clone)]
446pub struct XInputState {
447 pub raw: XINPUT_STATE,
449}
450
451impl ::std::default::Default for XInputState {
452 #[inline]
453 #[must_use]
454 fn default() -> Self {
455 Self {
456 raw: XINPUT_STATE {
457 dwPacketNumber: 0,
458 Gamepad: XINPUT_GAMEPAD {
459 wButtons: 0,
460 bLeftTrigger: 0,
461 bRightTrigger: 0,
462 sThumbLX: 0,
463 sThumbLY: 0,
464 sThumbRX: 0,
465 sThumbRY: 0,
466 },
467 },
468 }
469 }
470}
471
472impl ::std::cmp::PartialEq for XInputState {
473 fn eq(&self, other: &XInputState) -> bool {
478 self.raw.dwPacketNumber == other.raw.dwPacketNumber
479 }
480}
481
482impl ::std::cmp::Eq for XInputState {}
483
484impl ::std::fmt::Debug for XInputState {
485 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
486 write!(f, "XInputState (_)")
487 }
488}
489
490impl XInputState {
491 #[inline]
497 pub fn north_button(&self) -> bool {
498 self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_Y != 0
499 }
500
501 #[inline]
507 pub fn south_button(&self) -> bool {
508 self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_A != 0
509 }
510
511 #[inline]
517 pub fn east_button(&self) -> bool {
518 self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_B != 0
519 }
520
521 #[inline]
527 pub fn west_button(&self) -> bool {
528 self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_X != 0
529 }
530
531 #[inline]
533 pub fn arrow_up(&self) -> bool {
534 self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP != 0
535 }
536
537 #[inline]
539 pub fn arrow_down(&self) -> bool {
540 self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN != 0
541 }
542
543 #[inline]
545 pub fn arrow_left(&self) -> bool {
546 self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT != 0
547 }
548
549 #[inline]
551 pub fn arrow_right(&self) -> bool {
552 self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT != 0
553 }
554
555 #[inline]
561 pub fn start_button(&self) -> bool {
562 self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_START != 0
563 }
564
565 #[inline]
571 pub fn select_button(&self) -> bool {
572 self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_BACK != 0
573 }
574
575 #[inline]
581 pub fn guide_button(&self) -> bool {
582 self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE != 0
583 }
584
585 #[inline]
591 pub fn left_shoulder(&self) -> bool {
592 self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER != 0
593 }
594
595 #[inline]
601 pub fn right_shoulder(&self) -> bool {
602 self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER != 0
603 }
604
605 pub const TRIGGER_THRESHOLD: u8 = XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
607
608 #[inline]
616 pub fn left_trigger(&self) -> u8 {
617 self.raw.Gamepad.bLeftTrigger
618 }
619
620 #[inline]
628 pub fn right_trigger(&self) -> u8 {
629 self.raw.Gamepad.bRightTrigger
630 }
631
632 #[inline]
638 pub fn left_trigger_bool(&self) -> bool {
639 self.left_trigger() >= XInputState::TRIGGER_THRESHOLD
640 }
641
642 #[inline]
648 pub fn right_trigger_bool(&self) -> bool {
649 self.right_trigger() >= XInputState::TRIGGER_THRESHOLD
650 }
651
652 #[inline]
658 pub fn left_thumb_button(&self) -> bool {
659 self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB != 0
660 }
661
662 #[inline]
668 pub fn right_thumb_button(&self) -> bool {
669 self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB != 0
670 }
671
672 pub const LEFT_STICK_DEADZONE: i16 = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
674
675 pub const RIGHT_STICK_DEADZONE: i16 = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
677
678 #[inline]
682 pub fn left_stick_raw(&self) -> (i16, i16) {
683 (self.raw.Gamepad.sThumbLX, self.raw.Gamepad.sThumbLY)
684 }
685
686 #[inline]
690 pub fn right_stick_raw(&self) -> (i16, i16) {
691 (self.raw.Gamepad.sThumbRX, self.raw.Gamepad.sThumbRY)
692 }
693
694 #[inline]
698 pub fn left_stick_normalized(&self) -> (f32, f32) {
699 XInputState::normalize_raw_stick_value(self.left_stick_raw(), XInputState::LEFT_STICK_DEADZONE)
700 }
701
702 #[inline]
706 pub fn right_stick_normalized(&self) -> (f32, f32) {
707 XInputState::normalize_raw_stick_value(
708 self.right_stick_raw(),
709 XInputState::RIGHT_STICK_DEADZONE,
710 )
711 }
712
713 #[inline]
723 pub fn normalize_raw_stick_value(raw_stick: (i16, i16), deadzone: i16) -> (f32, f32) {
724 let deadzone_float = deadzone.max(0).min(i16::max_value() - 1) as f32;
725 let raw_float = (raw_stick.0 as f32, raw_stick.1 as f32);
726 let length = (raw_float.0 * raw_float.0 + raw_float.1 * raw_float.1).sqrt();
727 let normalized = (raw_float.0 / length, raw_float.1 / length);
728 if length > deadzone_float {
729 let length = length.min(32_767.0);
731 let scale = (length - deadzone_float) / (32_767.0 - deadzone_float);
732 (normalized.0 * scale, normalized.1 * scale)
733 } else {
734 (0.0, 0.0)
735 }
736 }
737}
738
739#[test]
740#[rustfmt::skip]
741fn normalize_raw_stick_value_test() {
742 for x in [i16::min_value(), i16::max_value()] {
743 for y in [i16::min_value(), i16::max_value()] {
744 for deadzone in [i16::min_value(), 0, i16::max_value() / 2,
745 i16::max_value() - 1, i16::max_value()] {
746 let f = XInputState::normalize_raw_stick_value((x, y), deadzone);
747 assert!(f.0.abs() <= 1.0, "XFail: x {}, y {}, dz {} f {:?}", x, y, deadzone, f);
748 assert!(f.1.abs() <= 1.0, "YFail: x {}, y {}, dz {} f {:?}", x, y, deadzone, f);
749 }
750 }
751 }
752}
753
754#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
757pub enum XInputUsageError {
758 XInputNotLoaded,
760 InvalidControllerID,
762 DeviceNotConnected,
764 UnknownError(u32),
767}
768
769#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
772pub enum XInputOptionalFnUsageError {
773 XInputNotLoaded,
775 InvalidControllerID,
777 DeviceNotConnected,
779 FunctionNotLoaded,
781 UnknownError(u32),
784}
785
786impl XInputHandle {
787 pub fn enable(&self, enable: bool) {
791 unsafe { (self.xinput_enable)(enable as BOOL) };
792 }
793
794 pub fn get_state(&self, user_index: u32) -> Result<XInputState, XInputUsageError> {
821 if user_index >= 4 {
822 Err(XInputUsageError::InvalidControllerID)
823 } else {
824 let mut output: XINPUT_STATE = unsafe { ::std::mem::zeroed() };
825 let return_status = unsafe { (self.xinput_get_state)(user_index, &mut output) };
826 match return_status {
827 ERROR_SUCCESS => Ok(XInputState { raw: output }),
828 ERROR_DEVICE_NOT_CONNECTED => Err(XInputUsageError::DeviceNotConnected),
829 s => {
830 trace!("Unexpected error code: {}", s);
831 Err(XInputUsageError::UnknownError(s))
832 }
833 }
834 }
835 }
836
837 pub fn get_state_ex(&self, user_index: u32) -> Result<XInputState, XInputUsageError> {
846 if user_index >= 4 {
847 Err(XInputUsageError::InvalidControllerID)
848 } else {
849 let mut output: XINPUT_STATE = unsafe { ::std::mem::zeroed() };
850 let return_status = match self.opt_xinput_get_state_ex {
851 Some(f) => unsafe { f(user_index, &mut output) },
852 None => return Err(XInputUsageError::XInputNotLoaded),
853 };
854 match return_status {
855 ERROR_SUCCESS => Ok(XInputState { raw: output }),
856 ERROR_DEVICE_NOT_CONNECTED => Err(XInputUsageError::DeviceNotConnected),
857 s => {
858 trace!("Unexpected error code: {}", s);
859 Err(XInputUsageError::UnknownError(s))
860 }
861 }
862 }
863 }
864}
865
866#[deprecated]
868pub fn xinput_get_state(user_index: u32) -> Result<XInputState, XInputUsageError> {
869 match *GLOBAL_XINPUT_HANDLE {
870 Ok(ref handle) => handle.get_state(user_index),
871 Err(_) => Err(XInputUsageError::XInputNotLoaded),
872 }
873}
874
875impl XInputHandle {
876 pub fn set_state(
895 &self,
896 user_index: u32,
897 left_motor_speed: u16,
898 right_motor_speed: u16,
899 ) -> Result<(), XInputUsageError> {
900 if user_index >= 4 {
901 Err(XInputUsageError::InvalidControllerID)
902 } else {
903 let mut input = XINPUT_VIBRATION {
904 wLeftMotorSpeed: left_motor_speed,
905 wRightMotorSpeed: right_motor_speed,
906 };
907 let return_status = unsafe { (self.xinput_set_state)(user_index, &mut input) };
908 match return_status {
909 ERROR_SUCCESS => Ok(()),
910 ERROR_DEVICE_NOT_CONNECTED => Err(XInputUsageError::DeviceNotConnected),
911 s => {
912 trace!("Unexpected error code: {}", s);
913 Err(XInputUsageError::UnknownError(s))
914 }
915 }
916 }
917 }
918}
919
920#[deprecated]
922pub fn xinput_set_state(
923 user_index: u32,
924 left_motor_speed: u16,
925 right_motor_speed: u16,
926) -> Result<(), XInputUsageError> {
927 match *GLOBAL_XINPUT_HANDLE {
928 Ok(ref handle) => handle.set_state(user_index, left_motor_speed, right_motor_speed),
929 Err(_) => Err(XInputUsageError::XInputNotLoaded),
930 }
931}
932
933impl XInputHandle {
934 pub fn get_capabilities(&self, user_index: u32) -> Result<XINPUT_CAPABILITIES, XInputUsageError> {
938 if user_index >= 4 {
939 Err(XInputUsageError::InvalidControllerID)
940 } else {
941 unsafe {
942 let mut capabilities = std::mem::zeroed();
943 let return_status = (self.xinput_get_capabilities)(user_index, 0, &mut capabilities);
944 match return_status {
945 ERROR_SUCCESS => Ok(capabilities),
946 ERROR_DEVICE_NOT_CONNECTED => Err(XInputUsageError::DeviceNotConnected),
947 s => {
948 trace!("Unexpected error code: {}", s);
949 Err(XInputUsageError::UnknownError(s))
950 }
951 }
952 }
953 }
954 }
955 pub fn get_capabilities_ex(
965 &self,
966 user_index: u32,
967 ) -> Result<XINPUT_CAPABILITIES_EX, XInputUsageError> {
968 if user_index >= 4 {
969 Err(XInputUsageError::InvalidControllerID)
970 } else {
971 unsafe {
972 let mut capabilities_ex = std::mem::zeroed();
973 let return_status = match self.opt_xinput_get_capabilities_ex {
974 None => return Err(XInputUsageError::XInputNotLoaded),
975 Some(f) => f(1, user_index, 0, &mut capabilities_ex),
976 };
977 match return_status {
978 ERROR_SUCCESS => Ok(capabilities_ex),
979 ERROR_DEVICE_NOT_CONNECTED => Err(XInputUsageError::DeviceNotConnected),
980 s => {
981 trace!("Unexpected error code: {}", s);
982 Err(XInputUsageError::UnknownError(s))
983 }
984 }
985 }
986 }
987 }
988
989 pub fn get_keystroke(
993 &self,
994 user_index: u32,
995 ) -> Result<Option<XINPUT_KEYSTROKE>, XInputOptionalFnUsageError> {
996 if user_index >= 4 {
997 Err(XInputOptionalFnUsageError::InvalidControllerID)
998 } else if let Some(func) = self.opt_xinput_get_keystroke {
999 unsafe {
1000 let mut keystroke = std::mem::zeroed();
1001 let return_status = (func)(user_index, 0, &mut keystroke);
1002 match return_status {
1003 ERROR_SUCCESS => Ok(Some(keystroke)),
1004 ERROR_EMPTY => Ok(None),
1005 ERROR_DEVICE_NOT_CONNECTED => Err(XInputOptionalFnUsageError::DeviceNotConnected),
1006 s => {
1007 trace!("Unexpected error code: {}", s);
1008 Err(XInputOptionalFnUsageError::UnknownError(s))
1009 }
1010 }
1011 }
1012 } else {
1013 Err(XInputOptionalFnUsageError::FunctionNotLoaded)
1014 }
1015 }
1016}
1017
1018#[derive(Copy, Clone, Eq, PartialEq)]
1020pub struct BatteryType(pub BYTE);
1021
1022impl BatteryType {
1023 pub const DISCONNECTED: Self = BatteryType(BATTERY_TYPE_DISCONNECTED);
1025 pub const WIRED: Self = BatteryType(BATTERY_TYPE_WIRED);
1027 pub const ALKALINE: Self = BatteryType(BATTERY_TYPE_ALKALINE);
1029 pub const NIMH: Self = BatteryType(BATTERY_TYPE_NIMH);
1031 pub const UNKNOWN: Self = BatteryType(BATTERY_TYPE_UNKNOWN);
1033}
1034
1035impl Debug for BatteryType {
1036 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
1037 let kind: &dyn Debug = match *self {
1038 BatteryType::DISCONNECTED => &"DISCONNECTED",
1039 BatteryType::WIRED => &"WIRED",
1040 BatteryType::ALKALINE => &"ALKALINE",
1041 BatteryType::NIMH => &"NIMH",
1042 BatteryType::UNKNOWN => &"UNKNOWN",
1043 _ => &self.0,
1044 };
1045
1046 f.debug_tuple("BatteryType").field(kind).finish()
1047 }
1048}
1049
1050#[derive(Copy, Clone, Eq, PartialEq)]
1052pub struct BatteryLevel(pub BYTE);
1053
1054impl BatteryLevel {
1055 pub const EMPTY: Self = BatteryLevel(BATTERY_LEVEL_EMPTY);
1057 pub const LOW: Self = BatteryLevel(BATTERY_LEVEL_LOW);
1059 pub const MEDIUM: Self = BatteryLevel(BATTERY_LEVEL_MEDIUM);
1061 pub const FULL: Self = BatteryLevel(BATTERY_LEVEL_FULL);
1063}
1064
1065impl Debug for BatteryLevel {
1066 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
1067 let level: &dyn Debug = match *self {
1068 BatteryLevel::EMPTY => &"EMPTY",
1069 BatteryLevel::LOW => &"LOW",
1070 BatteryLevel::MEDIUM => &"MEDIUM",
1071 BatteryLevel::FULL => &"FULL",
1072 _ => &self.0,
1073 };
1074
1075 f.debug_tuple("BatteryLevel").field(level).finish()
1076 }
1077}
1078
1079#[derive(Debug, Copy, Clone)]
1083pub struct XInputBatteryInformation {
1084 pub battery_type: BatteryType,
1086 pub battery_level: BatteryLevel,
1088}
1089
1090impl XInputHandle {
1091 fn xinput_get_battery_information(
1092 &self,
1093 user_index: u32,
1094 dev_type: BYTE,
1095 ) -> Result<XInputBatteryInformation, XInputOptionalFnUsageError> {
1096 if user_index >= 4 {
1097 Err(XInputOptionalFnUsageError::InvalidControllerID)
1098 } else if let Some(func) = self.opt_xinput_get_battery_information {
1099 let mut output: XINPUT_BATTERY_INFORMATION = unsafe { ::std::mem::zeroed() };
1100
1101 let return_status = unsafe { func(user_index, dev_type, &mut output) };
1102
1103 match return_status {
1104 ERROR_SUCCESS => {
1105 return Ok(XInputBatteryInformation {
1106 battery_type: BatteryType(output.BatteryType),
1107 battery_level: BatteryLevel(output.BatteryLevel),
1108 })
1109 }
1110 s => {
1111 trace!("Unexpected error code: {}", s);
1112 Err(XInputOptionalFnUsageError::UnknownError(s))
1113 }
1114 }
1115 } else {
1116 Err(XInputOptionalFnUsageError::FunctionNotLoaded)
1117 }
1118 }
1119
1120 pub fn get_gamepad_battery_information(
1124 &self,
1125 user_index: u32,
1126 ) -> Result<XInputBatteryInformation, XInputOptionalFnUsageError> {
1127 self.xinput_get_battery_information(user_index, BATTERY_DEVTYPE_GAMEPAD)
1128 }
1129
1130 pub fn get_headset_battery_information(
1134 &self,
1135 user_index: u32,
1136 ) -> Result<XInputBatteryInformation, XInputOptionalFnUsageError> {
1137 self.xinput_get_battery_information(user_index, BATTERY_DEVTYPE_HEADSET)
1138 }
1139}
1140
1141#[deprecated]
1143pub fn xinput_get_gamepad_battery_information(
1144 user_index: u32,
1145) -> Result<XInputBatteryInformation, XInputOptionalFnUsageError> {
1146 match *GLOBAL_XINPUT_HANDLE {
1147 Ok(ref handle) => handle.get_gamepad_battery_information(user_index),
1148 Err(_) => Err(XInputOptionalFnUsageError::XInputNotLoaded),
1149 }
1150}
1151
1152#[deprecated]
1154pub fn xinput_get_headset_battery_information(
1155 user_index: u32,
1156) -> Result<XInputBatteryInformation, XInputOptionalFnUsageError> {
1157 match *GLOBAL_XINPUT_HANDLE {
1158 Ok(ref handle) => handle.get_headset_battery_information(user_index),
1159 Err(_) => Err(XInputOptionalFnUsageError::XInputNotLoaded),
1160 }
1161}