rusty_xinput/
lib.rs

1//! This module lets you load an XInput DLL and use it.
2//!
3//! ## How To Use This
4//!
5//! 1) Call `dynamic_load_xinput()`. This will attempt to load in a DLL that
6//!    supports XInput. Note that the user might not have XInput installed, so
7//!    be prepared to fall back to a keyboard/mouse if that happens.
8//! 2) Call `xinput_get_state(controller)` to get your data. Usually you do this
9//!    once at the start of each frame of the game. You can poll for controllers
10//!    0, 1, 2, or 3. If a controller is connected you'll get `Ok(data)`.
11//!    Otherwise you'll get some sort of `Err` info.
12//! 3) Call `xinput_set_state(controller, left_speed, right_speed)` to set a
13//!    rumble effect on the controller. As with `xinput_get_state`, you can
14//!    select slots 0, 1, 2 or 3, and missing controllers or out of bounds
15//!    selections will give an `Err` of some kind. Devices other than literal
16//!    XBox 360 controllers have XInput drivers, but not all of them actually
17//!    have rumble support, so this should be an extra not an essential.
18//!
19//! If xinput isn't fully loaded, a call to get_state or set_state is still
20//! entirely safe to perform, you'll just get an `Err`.
21//!
22//! Note that there are theoretically other XInput extras you might care about,
23//! but they're only available in Windows 8+ and I use Windows 7, so oh well.
24
25#![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
45/// GetStateEx can get this in wButton
46pub const XINPUT_GAMEPAD_GUIDE: winapi::shared::minwindef::WORD = 0x0400;
47
48/// Capabilities info from the undocumented `XInputGetCapabilitiesEx` fn.
49#[repr(C)]
50#[derive(Clone, Copy)]
51#[allow(non_camel_case_types)]
52pub struct XINPUT_CAPABILITIES_EX {
53  /// The wrapped "basic capabilities" value
54  pub capabilities: XINPUT_CAPABILITIES,
55  /// USB Vendor ID of the attached controller
56  pub vendor_id: WORD,
57  /// USB Product ID of the attached controller
58  pub product_id: WORD,
59  /// USB Revision ID of the attached controller
60  pub revision_id: WORD,
61  /// unknown use
62  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
78// undocumented
79type XInputGetStateExFunc = unsafe extern "system" fn(DWORD, *mut XINPUT_STATE) -> DWORD;
80
81// undocumented
82type XInputGetCapabilitiesEx =
83  unsafe extern "system" fn(DWORD, DWORD, DWORD, *mut XINPUT_CAPABILITIES_EX) -> DWORD;
84
85// **Removed** in xinput1_4.dll.
86type XInputGetDSoundAudioDeviceGuidsFunc =
87  unsafe extern "system" fn(DWORD, *mut GUID, *mut GUID) -> DWORD;
88
89// Added in xinput1_3.dll.
90type 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
94// Added in xinput1_4.dll.
95type XInputGetAudioDeviceIdsFunc =
96  unsafe extern "system" fn(DWORD, LPWSTR, *mut UINT, LPWSTR, *mut UINT) -> DWORD;
97
98/// A handle to a loaded XInput DLL.
99#[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  // some day we should use these
111  _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
129/// Quick and dirty wrapper to let us format log messages easier.
130pub(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
144/// Converts a rusty string into a win32 string.
145pub(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/// The ways that a dynamic load of XInput can fail.
162#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
163pub enum XInputLoadingFailure {
164  /// The xinput system was already in the process of loading in some other
165  /// thread. This attempt failed because of that, but that other attempt might
166  /// still succeed.
167  #[deprecated]
168  AlreadyLoading,
169  /// The xinput system was already active. A failure of this kind leaves the
170  /// system active.
171  AlreadyActive,
172  /// The system was not loading or active, but was in some unknown state. If
173  /// you get this, it's probably a bug that you should report.
174  UnknownState,
175  /// No DLL for XInput could be found. This places the system back into an
176  /// "uninitialized" status, and you could potentially try again later if the
177  /// user fiddles with the program's DLL path or whatever.
178  NoDLL,
179  /// A DLL was found that matches one of the expected XInput DLL names, but it
180  /// didn't contain both of the expected functions. This is probably a weird
181  /// situation to find. Either way, the xinput status is set to "uninitialized"
182  /// and as with the NoDLL error you could potentially try again.
183  NoPointers,
184}
185
186impl XInputHandle {
187  /// Attempts to dynamically load an XInput DLL and get the function pointers.
188  ///
189  /// # Failure
190  ///
191  /// This can fail in a few ways, as explained in the `XInputLoadingFailure`
192  /// type. The most likely failure case is that the user's system won't have the
193  /// required DLL, in which case you should probably allow them to play with just
194  /// a keyboard/mouse instead.
195  ///
196  /// # Current DLL Names
197  ///
198  /// Currently the following DLL names are searched for in this order:
199  ///
200  /// * `xinput1_4.dll`
201  /// * `xinput1_3.dll`
202  /// * `xinput1_2.dll`
203  /// * `xinput1_1.dll`
204  /// * `xinput9_1_0.dll`
205  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  /// Attempt to load a specific XInput DLL and get the function pointers.
223  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    // It's always safe to call `LoadLibraryW`, the worst that can happen is
230    // that we get a null pointer back.
231    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/// Attempts to dynamically load an XInput DLL and get the function pointers.
395///
396/// This operation is thread-safe and can be performed at any time. If xinput
397/// hasn't been loaded yet, or if there was a failed load attempt, then
398/// `xinput_get_state` and `xinput_set_state` will safety return an `Err` value
399/// to that effect.
400///
401/// There's no way provided to unload XInput once it's been loaded, because that
402/// makes the normal operation a little faster. Why would you want to unload it
403/// anyway? Don't be silly.
404///
405/// # Failure
406///
407/// This can fail in a few ways, as explained in the `XInputLoadingFailure`
408/// type. The most likely failure case is that the user's system won't have the
409/// required DLL, in which case you should probably allow them to play with just
410/// a keyboard/mouse instead.
411///
412/// # Current DLL Names
413///
414/// Currently the following DLL names are searched for in this order:
415///
416/// * `xinput9_1_0.dll`
417/// * `xinput1_4.dll`
418/// * `xinput1_3.dll`
419/// * `xinput1_2.dll`
420/// * `xinput1_1.dll`
421#[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/// This wraps an `XINPUT_STATE` value and provides a more rusty (read-only)
431/// interface to the data it contains.
432///
433/// All three major game companies use different names for most of the buttons,
434/// so the docs for each button method list out what each of the major companies
435/// call that button. To the driver it's all the same, it's just however you
436/// want to think of them.
437///
438/// If sequential calls to `xinput_get_state` for a given controller slot have
439/// the same packet number then the controller state has not changed since the
440/// last call. The `PartialEq` and `Eq` implementations for this wrapper type
441/// reflect that. The exact value of the packet number is unimportant.
442///
443/// If you want to do something that the rust wrapper doesn't support, just use
444/// the raw field to get at the inner value.
445#[derive(Copy, Clone)]
446pub struct XInputState {
447  /// The raw value we're wrapping.
448  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  /// Equality for `XInputState` values is based _only_ on the
474  /// `dwPacketNumber` of the wrapped `XINPUT_STATE` value. This is entirely
475  /// correct for values obtained from the xinput system, but if you make your
476  /// own `XInputState` values for some reason you can confuse it.
477  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  /// The north button of the action button group.
492  ///
493  /// * Nintendo: X
494  /// * Playstation: Triangle
495  /// * XBox: Y
496  #[inline]
497  pub fn north_button(&self) -> bool {
498    self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_Y != 0
499  }
500
501  /// The south button of the action button group.
502  ///
503  /// * Nintendo: B
504  /// * Playstation: X
505  /// * XBox: A
506  #[inline]
507  pub fn south_button(&self) -> bool {
508    self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_A != 0
509  }
510
511  /// The east button of the action button group.
512  ///
513  /// * Nintendo: A
514  /// * Playstation: Circle
515  /// * XBox: B
516  #[inline]
517  pub fn east_button(&self) -> bool {
518    self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_B != 0
519  }
520
521  /// The west button of the action button group.
522  ///
523  /// * Nintendo: Y
524  /// * Playstation: Square
525  /// * XBox: X
526  #[inline]
527  pub fn west_button(&self) -> bool {
528    self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_X != 0
529  }
530
531  /// The up button on the directional pad.
532  #[inline]
533  pub fn arrow_up(&self) -> bool {
534    self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP != 0
535  }
536
537  /// The down button on the directional pad.
538  #[inline]
539  pub fn arrow_down(&self) -> bool {
540    self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN != 0
541  }
542
543  /// The left button on the directional pad.
544  #[inline]
545  pub fn arrow_left(&self) -> bool {
546    self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT != 0
547  }
548
549  /// The right button on the directional pad.
550  #[inline]
551  pub fn arrow_right(&self) -> bool {
552    self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT != 0
553  }
554
555  /// The "start" button.
556  ///
557  /// * Nintendo: Start (NES / SNES), '+' (Pro Controller)
558  /// * Playstation: Start
559  /// * XBox: Start
560  #[inline]
561  pub fn start_button(&self) -> bool {
562    self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_START != 0
563  }
564
565  /// The "not start" button.
566  ///
567  /// * Nintendo: Select (NES / NES), '-' (Pro Controller)
568  /// * Playstation: Select
569  /// * XBox: Back
570  #[inline]
571  pub fn select_button(&self) -> bool {
572    self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_BACK != 0
573  }
574
575  /// The "guide" button.
576  ///
577  /// * Nintendo: Home
578  /// * Playstation: PS
579  /// * XBox: Guide
580  #[inline]
581  pub fn guide_button(&self) -> bool {
582    self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE != 0
583  }
584
585  /// The upper left shoulder button.
586  ///
587  /// * Nintendo: L
588  /// * Playstation: L1
589  /// * XBox: LB
590  #[inline]
591  pub fn left_shoulder(&self) -> bool {
592    self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER != 0
593  }
594
595  /// The upper right shoulder button.
596  ///
597  /// * Nintendo: R
598  /// * Playstation: R1
599  /// * XBox: RB
600  #[inline]
601  pub fn right_shoulder(&self) -> bool {
602    self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER != 0
603  }
604
605  /// The default threshold to count a trigger as being "pressed".
606  pub const TRIGGER_THRESHOLD: u8 = XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
607
608  /// The lower left shoulder trigger. If you want to use this as a simple
609  /// boolean it is suggested that you compare it to the `TRIGGER_THRESHOLD`
610  /// constant.
611  ///
612  /// * Nintendo: ZL
613  /// * Playstation: L2
614  /// * XBox: LT
615  #[inline]
616  pub fn left_trigger(&self) -> u8 {
617    self.raw.Gamepad.bLeftTrigger
618  }
619
620  /// The lower right shoulder trigger. If you want to use this as a simple
621  /// boolean it is suggested that you compare it to the `TRIGGER_THRESHOLD`
622  /// constant.
623  ///
624  /// * Nintendo: ZR
625  /// * Playstation: R2
626  /// * XBox: RT
627  #[inline]
628  pub fn right_trigger(&self) -> u8 {
629    self.raw.Gamepad.bRightTrigger
630  }
631
632  /// The lower left shoulder trigger as a bool using the default threshold.
633  ///
634  /// * Nintendo: ZL
635  /// * Playstation: L2
636  /// * XBox: LT
637  #[inline]
638  pub fn left_trigger_bool(&self) -> bool {
639    self.left_trigger() >= XInputState::TRIGGER_THRESHOLD
640  }
641
642  /// The lower right shoulder trigger as a bool using the default threshold.
643  ///
644  /// * Nintendo: ZR
645  /// * Playstation: R2
646  /// * XBox: RT
647  #[inline]
648  pub fn right_trigger_bool(&self) -> bool {
649    self.right_trigger() >= XInputState::TRIGGER_THRESHOLD
650  }
651
652  /// The left thumb stick being pressed inward.
653  ///
654  /// * Nintendo: (L)
655  /// * Playstation: L3
656  /// * XBox: (L)
657  #[inline]
658  pub fn left_thumb_button(&self) -> bool {
659    self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB != 0
660  }
661
662  /// The right thumb stick being pressed inward.
663  ///
664  /// * Nintendo: (R)
665  /// * Playstation: R3
666  /// * XBox: (R)
667  #[inline]
668  pub fn right_thumb_button(&self) -> bool {
669    self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB != 0
670  }
671
672  /// The suggested default deadzone for use with the left thumb stick.
673  pub const LEFT_STICK_DEADZONE: i16 = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
674
675  /// The suggested default deadzone for use with the right thumb stick.
676  pub const RIGHT_STICK_DEADZONE: i16 = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
677
678  /// The left stick raw value.
679  ///
680  /// Positive values are to the right (X-axis) or up (Y-axis).
681  #[inline]
682  pub fn left_stick_raw(&self) -> (i16, i16) {
683    (self.raw.Gamepad.sThumbLX, self.raw.Gamepad.sThumbLY)
684  }
685
686  /// The right stick raw value.
687  ///
688  /// Positive values are to the right (X-axis) or up (Y-axis).
689  #[inline]
690  pub fn right_stick_raw(&self) -> (i16, i16) {
691    (self.raw.Gamepad.sThumbRX, self.raw.Gamepad.sThumbRY)
692  }
693
694  /// The left stick value normalized with the default dead-zone.
695  ///
696  /// See `normalize_raw_stick_value` for more.
697  #[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  /// The right stick value normalized with the default dead-zone.
703  ///
704  /// See `normalize_raw_stick_value` for more.
705  #[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  /// This helper normalizes a raw stick value using the given deadzone.
714  ///
715  /// If the raw value's 2d length is less than the deadzone the result will be
716  /// `(0.0,0.0)`, otherwise the result is normalized across the range from the
717  /// deadzone point to the maximum value.
718  ///
719  /// The `deadzone` value is clamped to the range 0 to 32,766 (inclusive)
720  /// before use. Negative inputs or maximum value inputs make the normalization
721  /// just work improperly.
722  #[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      // clip our value to the expected maximum length.
730      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/// These are all the sorts of problems that can come up when you're using the
755/// xinput system.
756#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
757pub enum XInputUsageError {
758  /// XInput isn't currently loaded.
759  XInputNotLoaded,
760  /// The controller ID you gave was 4 or more.
761  InvalidControllerID,
762  /// Not really an error, this controller is just missing.
763  DeviceNotConnected,
764  /// There was some sort of unexpected error happened, this is the error code
765  /// windows returned.
766  UnknownError(u32),
767}
768
769/// Error that can be returned by functions that are not guaranteed to be present
770/// in earlier XInput versions.
771#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
772pub enum XInputOptionalFnUsageError {
773  /// XInput isn't currently loaded.
774  XInputNotLoaded,
775  /// The controller ID you gave was 4 or more.
776  InvalidControllerID,
777  /// Not really an error, this controller is just missing.
778  DeviceNotConnected,
779  /// Function is not present in loaded DLL
780  FunctionNotLoaded,
781  /// There was some sort of unexpected error happened, this is the error code
782  /// windows returned.
783  UnknownError(u32),
784}
785
786impl XInputHandle {
787  /// Enables or disables XInput.
788  ///
789  /// See the [MSDN documentation for XInputEnable](https://docs.microsoft.com/en-us/windows/desktop/api/xinput/nf-xinput-xinputenable).
790  pub fn enable(&self, enable: bool) {
791    unsafe { (self.xinput_enable)(enable as BOOL) };
792  }
793
794  /// Polls the controller port given for the current controller state.
795  ///
796  /// This cannot detect the "Guide" button. Use
797  /// [`get_state_ex`](Self::get_state_ex) for that.
798  ///
799  /// # Notes
800  ///
801  /// It is a persistent problem with xinput (since ~2007?) that polling for the
802  /// data of a controller that isn't connected will cause a long stall. In the
803  /// area of 500,000 cpu cycles. That's like 2,000 cache misses in a row.
804  ///
805  /// Once a controller is detected as not being plugged in you are strongly
806  /// advised to not poll for its data again next frame. Instead, you should
807  /// probably only poll for one known-missing controller per frame at most.
808  ///
809  /// Alternately, you can register for your app to get plug and play events and
810  /// then wait for one of them to come in before you ever poll for a missing
811  /// controller a second time. That's up to you.
812  ///
813  /// # Errors
814  ///
815  /// A few things can cause an `Err` value to come back, as explained by the
816  /// `XInputUsageError` type.
817  ///
818  /// Most commonly, a controller will simply not be connected. Most people
819  /// don't have all four slots plugged in all the time.
820  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  /// Works like `get_state`, but can detect the "Guide" button as well.
838  ///
839  /// ## Failure
840  ///
841  /// * This function is technically an undocumented API. It was introduced in
842  ///   XInput 1.3, but may not be present in the currently loaded XInput. If
843  ///   it's not available then `XInputNotLoaded` is returned as an `Err`, even
844  ///   when other XInput functions may be available.
845  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/// See `XInputHandle::get_state`
867#[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  /// Allows you to set the rumble speeds of the left and right motors.
877  ///
878  /// Valid motor speeds are across the whole `u16` range, and the number is the
879  /// scale of the motor intensity. In other words, 0 is 0%, and 65,535 is 100%.
880  ///
881  /// On a 360 controller the left motor is low-frequency and the right motor is
882  /// high-frequency. On other controllers running through xinput this might be
883  /// the case, or the controller might not even have rumble ability at all. If
884  /// rumble is missing from the device you'll still get `Ok` return values, so
885  /// treat rumble as an extra, not an essential.
886  ///
887  /// # Errors
888  ///
889  /// A few things can cause an `Err` value to come back, as explained by the
890  /// `XInputUsageError` type.
891  ///
892  /// Most commonly, a controller will simply not be connected. Most people don't
893  /// have all four slots plugged in all the time.
894  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/// See `XInputHandle::set_state`
921#[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  /// Retrieve the capabilities of a controller.
935  ///
936  /// See the [MSDN documentation for XInputGetCapabilities](https://docs.microsoft.com/en-us/windows/desktop/api/xinput/nf-xinput-xinputgetcapabilities).
937  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  /// Retrieve the Extended capabilities of a controller.
956  ///
957  /// Undocumented!! This isn't part of the official XInput API, but is often available.
958  ///
959  /// ## Failure
960  ///
961  /// * This function is technically an undocumented API. If
962  ///   it's not available then `XInputNotLoaded` is returned as an `Err`, even
963  ///   when other XInput functions may be available.
964  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  /// Retrieve a gamepad input event.
990  ///
991  /// See the [MSDN documentation for XInputGetKeystroke](https://docs.microsoft.com/en-us/windows/desktop/api/xinput/nf-xinput-xinputgetkeystroke).
992  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/// Defines type of battery used in device, if any.
1019#[derive(Copy, Clone, Eq, PartialEq)]
1020pub struct BatteryType(pub BYTE);
1021
1022impl BatteryType {
1023  /// Device is disconnected.
1024  pub const DISCONNECTED: Self = BatteryType(BATTERY_TYPE_DISCONNECTED);
1025  /// Device does not have battery.
1026  pub const WIRED: Self = BatteryType(BATTERY_TYPE_WIRED);
1027  /// Device has alkaline battery.
1028  pub const ALKALINE: Self = BatteryType(BATTERY_TYPE_ALKALINE);
1029  /// Device has nimh battery.
1030  pub const NIMH: Self = BatteryType(BATTERY_TYPE_NIMH);
1031  /// The battery type is not known.
1032  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/// Specify how much battery is charged for devices with battery.
1051#[derive(Copy, Clone, Eq, PartialEq)]
1052pub struct BatteryLevel(pub BYTE);
1053
1054impl BatteryLevel {
1055  /// Battery is empty.
1056  pub const EMPTY: Self = BatteryLevel(BATTERY_LEVEL_EMPTY);
1057  /// Battery level is low.
1058  pub const LOW: Self = BatteryLevel(BATTERY_LEVEL_LOW);
1059  /// Battery level is medium.
1060  pub const MEDIUM: Self = BatteryLevel(BATTERY_LEVEL_MEDIUM);
1061  /// Battery is full.
1062  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/// Holds information about device's battery.
1080///
1081/// See also [XINPUT_BATTERY_INFORMATION](https://docs.microsoft.com/en-us/windows/desktop/api/xinput/ns-xinput-_xinput_battery_information).
1082#[derive(Debug, Copy, Clone)]
1083pub struct XInputBatteryInformation {
1084  /// Type of batter used in device, if any.
1085  pub battery_type: BatteryType,
1086  /// For devices with battery, contains battery level.
1087  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  /// Get battery type and charge level of a gamepad.
1121  ///
1122  /// See also [XInputGetBatteryInformation](https://docs.microsoft.com/en-us/windows/desktop/api/xinput/nf-xinput-xinputgetbatteryinformation)
1123  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  /// Get battery type and charge level of a headset.
1131  ///
1132  /// See also [XInputGetBatteryInformation](https://docs.microsoft.com/en-us/windows/desktop/api/xinput/nf-xinput-xinputgetbatteryinformation)
1133  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/// See `InputHandle::get_gamepad_battery_information`
1142#[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/// See `InputHandle::get_headset_battery_information`
1153#[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}