use super::*;
use winapi::um::libloaderapi::*;
use winapi::um::xinput::*;
unsafe impl ZeroSafe for XINPUT_BATTERY_INFORMATION {}
unsafe impl ZeroSafe for XINPUT_CAPABILITIES {}
unsafe impl ZeroSafe for XINPUT_KEYSTROKE {}
unsafe impl ZeroSafe for XINPUT_STATE {}
type XInputEnableFn = unsafe extern "system" fn(BOOL);
type XInputGetAudioDeviceIdsFn =
unsafe extern "system" fn(DWORD, LPWSTR, *mut UINT, LPWSTR, *mut UINT) -> DWORD;
type XInputGetBatteryInformationFn =
unsafe extern "system" fn(DWORD, BYTE, *mut XINPUT_BATTERY_INFORMATION) -> DWORD;
type XInputGetCapabilitiesFn =
unsafe extern "system" fn(DWORD, DWORD, *mut XINPUT_CAPABILITIES) -> DWORD;
type XInputGetDSoundAudioDeviceGuidsFn =
unsafe extern "system" fn(DWORD, *mut GUID, *mut GUID) -> DWORD;
type XInputGetKeystrokeFn = unsafe extern "system" fn(DWORD, DWORD, PXINPUT_KEYSTROKE) -> DWORD;
type XInputGetStateFn = unsafe extern "system" fn(DWORD, *mut XINPUT_STATE) -> DWORD;
type XInputSetStateFn = unsafe extern "system" fn(DWORD, *mut XINPUT_VIBRATION) -> DWORD;
const XINPUT_DLL_NAME_LIST: &[&str] = &[
"xinput1_4.dll",
"xinput1_3.dll",
"xinput1_2.dll",
"xinput1_1.dll",
"xinput9_1_0.dll",
];
const ENABLE_NAME: &[u8] = b"XInputEnable\0";
const GET_AUDIO_DEVICE_IDS_NAME: &[u8] = b"XInputGetAudioDeviceIds\0";
const GET_BATTERY_INFORMATION_NAME: &[u8] = b"XInputGetBatteryInformation\0";
const GET_CAPABILITIES_NAME: &[u8] = b"XInputGetCapabilities\0";
const GET_DSOUND_AUDIO_DEVICE_GUIDS_NAME: &[u8] = b"XInputGetDSoundAudioDeviceGuids\0";
const GET_KEYSTROKE_NAME: &[u8] = b"XInputGetKeystroke\0";
const GET_STATE_NAME: &[u8] = b"XInputGetState\0";
const SET_STATE_NAME: &[u8] = b"XInputSetState\0";
pub struct XInput {
dll_name: &'static str,
dll: HMODULE,
enable: Option<XInputEnableFn>,
get_audio_device_ids: Option<XInputGetAudioDeviceIdsFn>,
get_battery_information: Option<XInputGetBatteryInformationFn>,
get_capabilities: Option<XInputGetCapabilitiesFn>,
get_dsound_audio_device_guids: Option<XInputGetDSoundAudioDeviceGuidsFn>,
get_keystroke: Option<XInputGetKeystrokeFn>,
get_state: Option<XInputGetStateFn>,
set_state: Option<XInputSetStateFn>,
}
impl core::ops::Drop for XInput {
fn drop(&mut self) {
if !self.dll.is_null() {
unsafe { FreeLibrary(self.dll) };
}
}
}
impl std::fmt::Debug for XInput {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let yesno = |b| if b { "yes" } else { "no" };
write!(f, "XInput {{ dll_name: {:?}, enable: {}, get_audio_device_ids: {}, get_battery_information: {}, get_capabilities: {}, get_dsound_audio_device_guids: {}, get_keystroke: {}, get_state: {}, set_state: {} }}",
self.dll_name,
yesno(self.enable.is_some()),
yesno(self.get_audio_device_ids.is_some()),
yesno(self.get_battery_information.is_some()),
yesno(self.get_capabilities.is_some()),
yesno(self.get_dsound_audio_device_guids.is_some()),
yesno(self.get_keystroke.is_some()),
yesno(self.get_state.is_some()),
yesno(self.set_state.is_some()),
)
}
}
impl XInput {
#[rustfmt::skip]
pub fn load() -> Result<Self, Self> {
unsafe {
for &dll_name in XINPUT_DLL_NAME_LIST {
let wide_name = wide_null(dll_name);
let dll = LoadLibraryW(wide_name.as_ptr());
if !dll.is_null() {
use core::intrinsics::transmute;
let enable: Option<XInputEnableFn> = transmute(GetProcAddress(dll, ENABLE_NAME.as_ptr() as *const i8));
let get_audio_device_ids: Option<XInputGetAudioDeviceIdsFn> = transmute(GetProcAddress(dll, GET_AUDIO_DEVICE_IDS_NAME.as_ptr() as *const i8));
let get_battery_information: Option<XInputGetBatteryInformationFn> = transmute(GetProcAddress(dll, GET_BATTERY_INFORMATION_NAME.as_ptr() as *const i8));
let get_capabilities: Option<XInputGetCapabilitiesFn> = transmute(GetProcAddress(dll, GET_CAPABILITIES_NAME.as_ptr() as *const i8));
let get_dsound_audio_device_guids: Option<XInputGetDSoundAudioDeviceGuidsFn> = transmute(GetProcAddress(dll, GET_DSOUND_AUDIO_DEVICE_GUIDS_NAME.as_ptr() as *const i8));
let get_keystroke: Option<XInputGetKeystrokeFn> = transmute(GetProcAddress(dll, GET_KEYSTROKE_NAME.as_ptr() as *const i8));
let get_state: Option<XInputGetStateFn> = transmute(GetProcAddress(dll, GET_STATE_NAME.as_ptr() as *const i8));
let set_state: Option<XInputSetStateFn> = transmute(GetProcAddress(dll, SET_STATE_NAME.as_ptr() as *const i8));
return Ok(Self {
dll_name,
dll,
enable,
get_audio_device_ids,
get_battery_information,
get_capabilities,
get_dsound_audio_device_guids,
get_keystroke,
get_state,
set_state,
});
}
}
Err(Self {
dll_name: "None",
dll: null_mut(),
enable: None,
get_audio_device_ids: None,
get_battery_information: None,
get_capabilities: None,
get_dsound_audio_device_guids: None,
get_keystroke: None,
get_state: None,
set_state: None,
})
}
}
pub fn dll_name(&self) -> &'static str {
&self.dll_name
}
pub fn enable(&self, enabled: bool) -> Result<(), DWORD> {
self
.enable
.map(|f| unsafe { f(if enabled { TRUE } else { FALSE }) })
.ok_or(ERROR_DEVICE_NOT_CONNECTED)
}
pub fn has_enable(&self) -> bool {
self.enable.is_some()
}
pub fn get_audio_device_ids(&self, controller: Controller) -> Result<AudioDeviceIDs, DWORD> {
match self.get_audio_device_ids {
Some(f) => unsafe {
let mut render_id = Vec::with_capacity(MAX_PATH);
let mut capture_id = Vec::with_capacity(MAX_PATH);
let mut render_count = 0;
let mut capture_count = 0;
let out = f(
controller as DWORD,
render_id.as_mut_ptr(),
&mut render_count,
capture_id.as_mut_ptr(),
&mut capture_count,
);
if out == ERROR_SUCCESS {
render_id.set_len(render_count as usize);
capture_id.set_len(capture_count as usize);
Ok(AudioDeviceIDs {
render_id,
capture_id,
})
} else {
Err(out)
}
},
None => Err(ERROR_DEVICE_NOT_CONNECTED),
}
}
pub fn has_get_audio_device_ids(&self) -> bool {
self.get_audio_device_ids.is_some()
}
pub fn get_battery_information(
&self,
controller: Controller,
battery: BatteryDeviceType,
) -> Result<XINPUT_BATTERY_INFORMATION, DWORD> {
match self.get_battery_information {
Some(f) => unsafe {
let mut battery_info = XINPUT_BATTERY_INFORMATION::zeroed();
let out = f(controller as DWORD, battery as BYTE, &mut battery_info);
if out == ERROR_SUCCESS {
Ok(battery_info)
} else {
Err(out)
}
},
None => Err(ERROR_DEVICE_NOT_CONNECTED),
}
}
pub fn has_get_battery_information(&self) -> bool {
self.get_battery_information.is_some()
}
pub fn get_capabilities(
&self,
controller: Controller,
gamepad: bool,
) -> Result<XINPUT_CAPABILITIES, DWORD> {
match self.get_capabilities {
Some(f) => unsafe {
let mut capabilities = XINPUT_CAPABILITIES::zeroed();
let flags = if gamepad { XINPUT_FLAG_GAMEPAD } else { 0 };
let out = f(controller as DWORD, flags, &mut capabilities);
if out == ERROR_SUCCESS {
Ok(capabilities)
} else {
Err(out)
}
},
None => Err(ERROR_DEVICE_NOT_CONNECTED),
}
}
pub fn has_get_capabilities(&self) -> bool {
self.get_capabilities.is_some()
}
pub fn get_dsound_audio_device_guids(&self, controller: Controller) -> Result<DSoundIDs, DWORD> {
match self.get_dsound_audio_device_guids {
Some(f) => unsafe {
let mut ids: DSoundIDs = core::mem::zeroed();
let out = f(
controller as DWORD,
&mut ids.render_guid,
&mut ids.capture_guid,
);
if out == ERROR_SUCCESS {
Ok(ids)
} else {
Err(out)
}
},
None => Err(ERROR_DEVICE_NOT_CONNECTED),
}
}
pub fn has_get_dsound_audio_device_guids(&self) -> bool {
self.get_dsound_audio_device_guids.is_some()
}
pub fn get_keystroke(
&self,
opt_controller: Option<Controller>,
) -> Result<XINPUT_KEYSTROKE, DWORD> {
match self.get_keystroke {
Some(f) => unsafe {
let mut keystroke = XINPUT_KEYSTROKE::zeroed();
let out = f(
opt_controller
.map(|c| c as DWORD)
.unwrap_or(XUSER_INDEX_ANY),
0,
&mut keystroke,
);
if out == ERROR_SUCCESS {
Ok(keystroke)
} else {
Err(out)
}
},
None => Err(ERROR_DEVICE_NOT_CONNECTED),
}
}
pub fn has_get_keystroke(&self) -> bool {
self.get_keystroke.is_some()
}
pub fn get_state(&self, controller: Controller) -> Result<XINPUT_STATE, DWORD> {
match self.get_state {
Some(f) => unsafe {
let mut state = XINPUT_STATE::zeroed();
let out = f(controller as DWORD, &mut state);
if out == ERROR_SUCCESS {
Ok(state)
} else {
Err(out)
}
},
None => Err(ERROR_DEVICE_NOT_CONNECTED),
}
}
pub fn has_get_state(&self) -> bool {
self.get_state.is_some()
}
pub fn set_state(&self, controller: Controller, left: u16, right: u16) -> Result<(), DWORD> {
match self.set_state {
Some(f) => unsafe {
let mut vibration = XINPUT_VIBRATION {
wLeftMotorSpeed: left,
wRightMotorSpeed: right,
};
let out = f(controller as DWORD, &mut vibration);
if out == ERROR_SUCCESS {
Ok(())
} else {
Err(out)
}
},
None => Err(ERROR_DEVICE_NOT_CONNECTED),
}
}
pub fn has_set_state(&self) -> bool {
self.set_state.is_some()
}
}
#[derive(Debug, Clone)]
#[allow(missing_docs)]
pub struct AudioDeviceIDs {
pub render_id: Vec<u16>,
pub capture_id: Vec<u16>,
}
#[derive(Debug, Clone, Copy)]
#[allow(missing_docs)]
pub struct DSoundIDs {
pub render_guid: GUID,
pub capture_guid: GUID,
}
unsafe impl ZeroSafe for DSoundIDs {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
#[allow(missing_docs)]
pub enum BatteryDeviceType {
Gamepad = BATTERY_DEVTYPE_GAMEPAD,
Headset = BATTERY_DEVTYPE_HEADSET,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u32)]
#[allow(missing_docs)]
pub enum Controller {
One = 0,
Two = 1,
Three = 2,
Four = 3,
}
impl Controller {
pub fn all() -> [Controller; 4] {
[
Controller::One,
Controller::Two,
Controller::Three,
Controller::Four,
]
}
}