#![allow(non_upper_case_globals)]
#![warn(missing_docs)]
#![forbid(missing_debug_implementations)]
#![cfg(windows)]
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
extern crate winapi;
use winapi::shared::guiddef::GUID;
use winapi::shared::minwindef::{BOOL, BYTE, DWORD, HMODULE, UINT};
use winapi::shared::ntdef::LPWSTR;
use winapi::shared::winerror::{ERROR_DEVICE_NOT_CONNECTED, ERROR_EMPTY, ERROR_SUCCESS};
use winapi::um::libloaderapi::{FreeLibrary, GetProcAddress, LoadLibraryW};
use winapi::um::xinput::*;
use std::fmt::{self, Debug, Formatter};
use std::sync::Arc;
type XInputEnableFunc = unsafe extern "system" fn(BOOL);
type XInputGetStateFunc = unsafe extern "system" fn(DWORD, *mut XINPUT_STATE) -> DWORD;
type XInputSetStateFunc = unsafe extern "system" fn(DWORD, *mut XINPUT_VIBRATION) -> DWORD;
type XInputGetCapabilitiesFunc =
unsafe extern "system" fn(DWORD, DWORD, *mut XINPUT_CAPABILITIES) -> DWORD;
type XInputGetDSoundAudioDeviceGuidsFunc =
unsafe extern "system" fn(DWORD, *mut GUID, *mut GUID) -> DWORD;
type XInputGetKeystrokeFunc = unsafe extern "system" fn(DWORD, DWORD, PXINPUT_KEYSTROKE) -> DWORD;
type XInputGetBatteryInformationFunc =
unsafe extern "system" fn(DWORD, BYTE, *mut XINPUT_BATTERY_INFORMATION) -> DWORD;
type XInputGetAudioDeviceIdsFunc =
unsafe extern "system" fn(DWORD, LPWSTR, *mut UINT, LPWSTR, *mut UINT) -> DWORD;
struct ScopedHMODULE(HMODULE);
impl Drop for ScopedHMODULE {
fn drop(&mut self) {
unsafe { FreeLibrary(self.0) };
}
}
#[derive(Clone)]
pub struct XInputHandle {
handle: Arc<ScopedHMODULE>,
xinput_enable: XInputEnableFunc,
xinput_get_state: XInputGetStateFunc,
xinput_set_state: XInputSetStateFunc,
xinput_get_capabilities: XInputGetCapabilitiesFunc,
opt_xinput_get_keystroke: Option<XInputGetKeystrokeFunc>,
opt_xinput_get_battery_information: Option<XInputGetBatteryInformationFunc>,
opt_xinput_get_audio_device_ids: Option<XInputGetAudioDeviceIdsFunc>,
opt_xinput_get_dsound_audio_device_guids: Option<XInputGetDSoundAudioDeviceGuidsFunc>,
}
impl Debug for XInputHandle {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "XInputHandle(handle = {:?})", self.handle.0)
}
}
unsafe impl Send for XInputHandle {}
unsafe impl Sync for XInputHandle {}
lazy_static! {
static ref GLOBAL_XINPUT_HANDLE: Result<XInputHandle, XInputLoadingFailure> =
XInputHandle::load_default();
}
pub(crate) struct WideNullU16<'a>(&'a [u16; ::winapi::shared::minwindef::MAX_PATH]);
impl<'a> ::std::fmt::Debug for WideNullU16<'a> {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
for &u in self.0.iter() {
if u == 0 {
break;
} else {
write!(f, "{}", u as u8 as char)?
}
}
Ok(())
}
}
pub(crate) fn wide_null<S: AsRef<str>>(s: S) -> [u16; ::winapi::shared::minwindef::MAX_PATH] {
let mut output: [u16; ::winapi::shared::minwindef::MAX_PATH] =
[0; ::winapi::shared::minwindef::MAX_PATH];
let mut i = 0;
for u in s.as_ref().encode_utf16() {
if i == output.len() - 1 {
break;
} else {
output[i] = u;
}
i += 1;
}
output[i] = 0;
output
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum XInputLoadingFailure {
#[deprecated]
AlreadyLoading,
AlreadyActive,
UnknownState,
NoDLL,
NoPointers,
}
impl XInputHandle {
pub fn load_default() -> Result<XInputHandle, XInputLoadingFailure> {
let xinput14 = "xinput1_4.dll";
let xinput13 = "xinput1_3.dll";
let xinput12 = "xinput1_2.dll";
let xinput11 = "xinput1_1.dll";
let xinput91 = "xinput9_1_0.dll";
for lib_name in [xinput14, xinput13, xinput12, xinput11, xinput91].into_iter() {
if let Ok(handle) = XInputHandle::load(lib_name) {
return Ok(handle);
}
}
debug!("Failure: XInput could not be loaded.");
Err(XInputLoadingFailure::NoDLL)
}
pub fn load<S: AsRef<str>>(s: S) -> Result<XInputHandle, XInputLoadingFailure> {
let lib_name = wide_null(s);
trace!(
"Attempting to load XInput DLL: {:?}",
WideNullU16(&lib_name)
);
let xinput_handle = unsafe { LoadLibraryW(lib_name.as_ptr()) };
if !xinput_handle.is_null() {
debug!("Success: XInput Loaded: {:?}", WideNullU16(&lib_name));
}
let xinput_handle = ScopedHMODULE(xinput_handle);
let enable_name = b"XInputEnable\0";
let get_state_name = b"XInputGetState\0";
let set_state_name = b"XInputSetState\0";
let get_capabilities_name = b"XInputGetCapabilities\0";
let get_keystroke_name = b"XInputGetKeystroke\0";
let get_battery_information_name = b"XInputGetBatteryInformation\0";
let get_audio_device_ids_name = b"XInputGetAudioDeviceIds\0";
let get_dsound_audio_device_guids_name = b"XInputGetDSoundAudioDeviceGuids\0";
let mut opt_xinput_enable = None;
let mut opt_xinput_get_state = None;
let mut opt_xinput_set_state = None;
let mut opt_xinput_get_capabilities = None;
let mut opt_xinput_get_keystroke = None;
let mut opt_xinput_get_battery_information = None;
let mut opt_xinput_get_audio_device_ids = None;
let mut opt_xinput_get_dsound_audio_device_guids = None;
unsafe {
let enable_ptr = GetProcAddress(xinput_handle.0, enable_name.as_ptr() as *mut i8);
if !enable_ptr.is_null() {
trace!("Found XInputEnable.");
opt_xinput_enable = Some(::std::mem::transmute(enable_ptr));
} else {
trace!("Could not find XInputEnable.");
}
}
unsafe {
let get_state_ptr = GetProcAddress(xinput_handle.0, get_state_name.as_ptr() as *mut i8);
if !get_state_ptr.is_null() {
trace!("Found XInputGetState.");
opt_xinput_get_state = Some(::std::mem::transmute(get_state_ptr));
} else {
trace!("Could not find XInputGetState.");
}
}
unsafe {
let set_state_ptr = GetProcAddress(xinput_handle.0, set_state_name.as_ptr() as *mut i8);
if !set_state_ptr.is_null() {
trace!("Found XInputSetState.");
opt_xinput_set_state = Some(::std::mem::transmute(set_state_ptr));
} else {
trace!("Could not find XInputSetState.");
}
}
unsafe {
let get_capabilities_ptr =
GetProcAddress(xinput_handle.0, get_capabilities_name.as_ptr() as *mut i8);
if !get_capabilities_ptr.is_null() {
trace!("Found XInputGetCapabilities.");
opt_xinput_get_capabilities = Some(::std::mem::transmute(get_capabilities_ptr));
} else {
trace!("Could not find XInputGetCapabilities.");
}
}
unsafe {
let get_keystroke_ptr =
GetProcAddress(xinput_handle.0, get_keystroke_name.as_ptr() as *mut i8);
if !get_keystroke_ptr.is_null() {
trace!("Found XInputGetKeystroke.");
opt_xinput_get_keystroke = Some(::std::mem::transmute(get_keystroke_ptr));
} else {
trace!("Could not find XInputGetKeystroke.");
}
}
unsafe {
let get_battery_information_ptr = GetProcAddress(
xinput_handle.0,
get_battery_information_name.as_ptr() as *mut i8,
);
if !get_battery_information_ptr.is_null() {
trace!("Found XInputGetBatteryInformation.");
opt_xinput_get_battery_information =
Some(::std::mem::transmute(get_battery_information_ptr));
} else {
trace!("Could not find XInputGetBatteryInformation.");
}
}
unsafe {
let get_dsound_audio_device_guids_ptr = GetProcAddress(
xinput_handle.0,
get_dsound_audio_device_guids_name.as_ptr() as *mut i8,
);
if !get_dsound_audio_device_guids_ptr.is_null() {
trace!("Found XInputGetDSoundAudioDeviceGuids.");
opt_xinput_get_dsound_audio_device_guids =
Some(::std::mem::transmute(get_dsound_audio_device_guids_ptr));
} else {
trace!("Could not find XInputGetDSoundAudioDeviceGuids.");
}
}
unsafe {
let get_audio_device_ids_ptr = GetProcAddress(
xinput_handle.0,
get_audio_device_ids_name.as_ptr() as *mut i8,
);
if !get_audio_device_ids_ptr.is_null() {
trace!("Found XInputGetAudioDeviceIds.");
opt_xinput_get_audio_device_ids = Some(::std::mem::transmute(get_audio_device_ids_ptr));
} else {
trace!("Could not find XInputGetAudioDeviceIds.");
}
}
if opt_xinput_enable.is_some()
&& opt_xinput_get_state.is_some()
&& opt_xinput_set_state.is_some()
&& opt_xinput_get_capabilities.is_some()
{
debug!("All function pointers loaded successfully.");
Ok(XInputHandle {
handle: Arc::new(xinput_handle),
xinput_enable: opt_xinput_enable.unwrap(),
xinput_get_state: opt_xinput_get_state.unwrap(),
xinput_set_state: opt_xinput_set_state.unwrap(),
xinput_get_capabilities: opt_xinput_get_capabilities.unwrap(),
opt_xinput_get_keystroke,
opt_xinput_get_battery_information,
opt_xinput_get_dsound_audio_device_guids,
opt_xinput_get_audio_device_ids,
})
} else {
debug!("Could not load the function pointers.");
Err(XInputLoadingFailure::NoPointers)
}
}
}
#[deprecated]
pub fn dynamic_load_xinput() -> Result<(), XInputLoadingFailure> {
if let Err(err) = *GLOBAL_XINPUT_HANDLE {
Err(err)
} else {
Ok(())
}
}
pub struct XInputState {
pub raw: XINPUT_STATE,
}
impl ::std::cmp::PartialEq for XInputState {
fn eq(&self, other: &XInputState) -> bool {
self.raw.dwPacketNumber == other.raw.dwPacketNumber
}
}
impl ::std::cmp::Eq for XInputState {}
impl ::std::fmt::Debug for XInputState {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "XInputState (_)")
}
}
impl XInputState {
#[inline]
pub fn north_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_Y != 0
}
#[inline]
pub fn south_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_A != 0
}
#[inline]
pub fn east_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_B != 0
}
#[inline]
pub fn west_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_X != 0
}
#[inline]
pub fn arrow_up(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP != 0
}
#[inline]
pub fn arrow_down(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN != 0
}
#[inline]
pub fn arrow_left(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT != 0
}
#[inline]
pub fn arrow_right(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT != 0
}
#[inline]
pub fn start_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_START != 0
}
#[inline]
pub fn select_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_BACK != 0
}
#[inline]
pub fn left_shoulder(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER != 0
}
#[inline]
pub fn right_shoulder(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER != 0
}
pub const TRIGGER_THRESHOLD: u8 = XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
#[inline]
pub fn left_trigger(&self) -> u8 {
self.raw.Gamepad.bLeftTrigger
}
#[inline]
pub fn right_trigger(&self) -> u8 {
self.raw.Gamepad.bRightTrigger
}
#[inline]
pub fn left_trigger_bool(&self) -> bool {
self.left_trigger() >= XInputState::TRIGGER_THRESHOLD
}
#[inline]
pub fn right_trigger_bool(&self) -> bool {
self.right_trigger() >= XInputState::TRIGGER_THRESHOLD
}
#[inline]
pub fn left_thumb_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB != 0
}
#[inline]
pub fn right_thumb_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB != 0
}
pub const LEFT_STICK_DEADZONE: i16 = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
pub const RIGHT_STICK_DEADZONE: i16 = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
#[inline]
pub fn left_stick_raw(&self) -> (i16, i16) {
(self.raw.Gamepad.sThumbLX, self.raw.Gamepad.sThumbLY)
}
#[inline]
pub fn right_stick_raw(&self) -> (i16, i16) {
(self.raw.Gamepad.sThumbRX, self.raw.Gamepad.sThumbRY)
}
#[inline]
pub fn left_stick_normalized(&self) -> (f32, f32) {
XInputState::normalize_raw_stick_value(self.left_stick_raw(), XInputState::LEFT_STICK_DEADZONE)
}
#[inline]
pub fn right_stick_normalized(&self) -> (f32, f32) {
XInputState::normalize_raw_stick_value(
self.right_stick_raw(),
XInputState::RIGHT_STICK_DEADZONE,
)
}
#[inline]
pub fn normalize_raw_stick_value(raw_stick: (i16, i16), deadzone: i16) -> (f32, f32) {
let deadzone_float = deadzone.max(0).min(i16::max_value() - 1) as f32;
let raw_float = (raw_stick.0 as f32, raw_stick.1 as f32);
let length = (raw_float.0 * raw_float.0 + raw_float.1 * raw_float.1).sqrt();
let normalized = (raw_float.0 / length, raw_float.1 / length);
if length > deadzone_float {
let length = length.min(32_767.0);
let scale = (length - deadzone_float) / (32_767.0 - deadzone_float);
(normalized.0 * scale, normalized.1 * scale)
} else {
(0.0, 0.0)
}
}
}
#[test]
fn normalize_raw_stick_value_test() {
for &x in [i16::min_value(), i16::max_value()].into_iter() {
for &y in [i16::min_value(), i16::max_value()].into_iter() {
#[cfg_attr(rustfmt, rustfmt_skip)]
for &deadzone in [i16::min_value(), 0, i16::max_value() / 2,
i16::max_value() - 1, i16::max_value()].into_iter() {
let f = XInputState::normalize_raw_stick_value((x, y), deadzone);
#[cfg_attr(rustfmt, rustfmt_skip)]
assert!(f.0.abs() <= 1.0, "XFail: x {}, y {}, dz {} f {:?}", x, y, deadzone, f);
#[cfg_attr(rustfmt, rustfmt_skip)]
assert!(f.1.abs() <= 1.0, "YFail: x {}, y {}, dz {} f {:?}", x, y, deadzone, f);
}
}
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum XInputUsageError {
XInputNotLoaded,
InvalidControllerID,
DeviceNotConnected,
UnknownError(u32),
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum XInputOptionalFnUsageError {
XInputNotLoaded,
InvalidControllerID,
DeviceNotConnected,
FunctionNotLoaded,
UnknownError(u32),
}
impl XInputHandle {
pub fn enable(&self, enable: bool) -> () {
unsafe { (self.xinput_enable)(enable as BOOL) };
}
pub fn get_state(&self, user_index: u32) -> Result<XInputState, XInputUsageError> {
if user_index >= 4 {
Err(XInputUsageError::InvalidControllerID)
} else {
let mut output: XINPUT_STATE = unsafe { ::std::mem::zeroed() };
let return_status = unsafe { (self.xinput_get_state)(user_index, &mut output) };
match return_status {
ERROR_SUCCESS => return Ok(XInputState { raw: output }),
ERROR_DEVICE_NOT_CONNECTED => Err(XInputUsageError::DeviceNotConnected),
s => {
trace!("Unexpected error code: {}", s);
Err(XInputUsageError::UnknownError(s))
}
}
}
}
}
#[deprecated]
pub fn xinput_get_state(user_index: u32) -> Result<XInputState, XInputUsageError> {
match *GLOBAL_XINPUT_HANDLE {
Ok(ref handle) => handle.get_state(user_index),
Err(_) => Err(XInputUsageError::XInputNotLoaded),
}
}
impl XInputHandle {
pub fn set_state(
&self, user_index: u32, left_motor_speed: u16, right_motor_speed: u16,
) -> Result<(), XInputUsageError> {
if user_index >= 4 {
Err(XInputUsageError::InvalidControllerID)
} else {
let mut input = XINPUT_VIBRATION {
wLeftMotorSpeed: left_motor_speed,
wRightMotorSpeed: right_motor_speed,
};
let return_status = unsafe { (self.xinput_set_state)(user_index, &mut input) };
match return_status {
ERROR_SUCCESS => Ok(()),
ERROR_DEVICE_NOT_CONNECTED => Err(XInputUsageError::DeviceNotConnected),
s => {
trace!("Unexpected error code: {}", s);
Err(XInputUsageError::UnknownError(s))
}
}
}
}
}
#[deprecated]
pub fn xinput_set_state(
user_index: u32, left_motor_speed: u16, right_motor_speed: u16,
) -> Result<(), XInputUsageError> {
match *GLOBAL_XINPUT_HANDLE {
Ok(ref handle) => handle.set_state(user_index, left_motor_speed, right_motor_speed),
Err(_) => Err(XInputUsageError::XInputNotLoaded),
}
}
impl XInputHandle {
pub fn get_capabilities(&self, user_index: u32) -> Result<XINPUT_CAPABILITIES, XInputUsageError> {
if user_index >= 4 {
Err(XInputUsageError::InvalidControllerID)
} else {
unsafe {
let mut capabilities = std::mem::uninitialized();
let return_status = (self.xinput_get_capabilities)(user_index, 0, &mut capabilities);
match return_status {
ERROR_SUCCESS => Ok(capabilities),
ERROR_DEVICE_NOT_CONNECTED => Err(XInputUsageError::DeviceNotConnected),
s => {
trace!("Unexpected error code: {}", s);
Err(XInputUsageError::UnknownError(s))
}
}
}
}
}
pub fn get_keystroke(
&self, user_index: u32,
) -> Result<Option<XINPUT_KEYSTROKE>, XInputOptionalFnUsageError> {
if user_index >= 4 {
Err(XInputOptionalFnUsageError::InvalidControllerID)
} else if let Some(func) = self.opt_xinput_get_keystroke {
unsafe {
let mut keystroke = std::mem::uninitialized();
let return_status = (func)(user_index, 0, &mut keystroke);
match return_status {
ERROR_SUCCESS => Ok(Some(keystroke)),
ERROR_EMPTY => Ok(None),
ERROR_DEVICE_NOT_CONNECTED => Err(XInputOptionalFnUsageError::DeviceNotConnected),
s => {
trace!("Unexpected error code: {}", s);
Err(XInputOptionalFnUsageError::UnknownError(s))
}
}
}
} else {
Err(XInputOptionalFnUsageError::FunctionNotLoaded)
}
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct BatteryType(pub BYTE);
impl BatteryType {
pub const DISCONNECTED: Self = BatteryType(BATTERY_TYPE_DISCONNECTED);
pub const WIRED: Self = BatteryType(BATTERY_TYPE_WIRED);
pub const ALKALINE: Self = BatteryType(BATTERY_TYPE_ALKALINE);
pub const NIMH: Self = BatteryType(BATTERY_TYPE_NIMH);
pub const UNKNOWN: Self = BatteryType(BATTERY_TYPE_UNKNOWN);
}
impl Debug for BatteryType {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
let kind: &Debug = match *self {
BatteryType::DISCONNECTED => &"DISCONNECTED",
BatteryType::WIRED => &"WIRED",
BatteryType::ALKALINE => &"ALKALINE",
BatteryType::NIMH => &"NIMH",
BatteryType::UNKNOWN => &"UNKNOWN",
_ => &self.0,
};
f.debug_tuple("BatteryType").field(kind).finish()
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct BatteryLevel(pub BYTE);
impl BatteryLevel {
pub const EMPTY: Self = BatteryLevel(BATTERY_LEVEL_EMPTY);
pub const LOW: Self = BatteryLevel(BATTERY_LEVEL_LOW);
pub const MEDIUM: Self = BatteryLevel(BATTERY_LEVEL_MEDIUM);
pub const FULL: Self = BatteryLevel(BATTERY_LEVEL_FULL);
}
impl Debug for BatteryLevel {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
let level: &Debug = match *self {
BatteryLevel::EMPTY => &"EMPTY",
BatteryLevel::LOW => &"LOW",
BatteryLevel::MEDIUM => &"MEDIUM",
BatteryLevel::FULL => &"FULL",
_ => &self.0,
};
f.debug_tuple("BatteryLevel").field(level).finish()
}
}
#[derive(Debug, Copy, Clone)]
pub struct XInputBatteryInformation {
pub battery_type: BatteryType,
pub battery_level: BatteryLevel,
}
impl XInputHandle {
fn xinput_get_battery_information(
&self, user_index: u32, dev_type: BYTE,
) -> Result<XInputBatteryInformation, XInputOptionalFnUsageError> {
if user_index >= 4 {
Err(XInputOptionalFnUsageError::InvalidControllerID)
} else if let Some(func) = self.opt_xinput_get_battery_information {
let mut output: XINPUT_BATTERY_INFORMATION = unsafe { ::std::mem::zeroed() };
let return_status = unsafe { func(user_index, dev_type, &mut output) };
match return_status {
ERROR_SUCCESS => {
return Ok(XInputBatteryInformation {
battery_type: BatteryType(output.BatteryType),
battery_level: BatteryLevel(output.BatteryLevel),
})
}
s => {
trace!("Unexpected error code: {}", s);
Err(XInputOptionalFnUsageError::UnknownError(s))
}
}
} else {
Err(XInputOptionalFnUsageError::FunctionNotLoaded)
}
}
pub fn get_gamepad_battery_information(
&self, user_index: u32,
) -> Result<XInputBatteryInformation, XInputOptionalFnUsageError> {
self.xinput_get_battery_information(user_index, BATTERY_DEVTYPE_GAMEPAD)
}
pub fn get_headset_battery_information(
&self, user_index: u32,
) -> Result<XInputBatteryInformation, XInputOptionalFnUsageError> {
self.xinput_get_battery_information(user_index, BATTERY_DEVTYPE_HEADSET)
}
}
#[deprecated]
pub fn xinput_get_gamepad_battery_information(
user_index: u32,
) -> Result<XInputBatteryInformation, XInputOptionalFnUsageError> {
match *GLOBAL_XINPUT_HANDLE {
Ok(ref handle) => handle.get_gamepad_battery_information(user_index),
Err(_) => Err(XInputOptionalFnUsageError::XInputNotLoaded),
}
}
#[deprecated]
pub fn xinput_get_headset_battery_information(
user_index: u32,
) -> Result<XInputBatteryInformation, XInputOptionalFnUsageError> {
match *GLOBAL_XINPUT_HANDLE {
Ok(ref handle) => handle.get_headset_battery_information(user_index),
Err(_) => Err(XInputOptionalFnUsageError::XInputNotLoaded),
}
}