use crate::dll::{Library, LibraryExt};
use winapi::shared::guiddef::GUID;
use winapi::shared::minwindef::*;
use winapi::um::processthreadsapi::*;
use winapi::um::psapi::*;
use winapi::um::winnt::*;
use winapi::um::xinput::*;
use std::convert::*;
use std::ffi::c_void;
use std::io;
use std::mem::*;
use std::ptr::null_mut;
lazy_static::lazy_static! {
static ref IMPORTS : Imports = Imports::load_from_linked_xinput().unwrap_or_default();
}
#[allow(non_snake_case)]
#[derive(Default)]
pub(crate) struct Imports {
pub XInputGetDSoundAudioDeviceGuids: Option<unsafe extern "system" fn(dwUserIndex: DWORD, pDSoundRenderGuid: *mut GUID, pDSoundCaptureGuid: *mut GUID) -> DWORD>,
pub XInputGetAudioDeviceIds: Option<unsafe extern "system" fn(dwUserIndex: DWORD, pRenderDeviceId: LPWSTR, pRenderCount: *mut UINT, pCaptureDeviceId: LPWSTR, pCaptureCount: *mut UINT) -> DWORD>,
pub _XInputGetStateEx: Option<unsafe extern "system" fn(dwUserIndex: DWORD, pState: *mut XINPUT_STATE) -> DWORD>,
pub _XInputWaitForGuideButton: Option<unsafe extern "system" fn(dwUserIndex: DWORD, dwFlag: DWORD, pUnknown: *mut c_void) -> DWORD>,
pub _XInputCancelGuideButtonWait: Option<unsafe extern "system" fn(dwUserIndex: DWORD) -> DWORD>,
pub _XInputPowerOffController: Option<unsafe extern "system" fn(dwUserIndex: DWORD) -> DWORD>,
}
impl Imports {
pub fn get() -> &'static Self { &*IMPORTS }
fn load_from_linked_xinput() -> Result<Self, io::Error> {
unsafe {
let lib = try_find_loaded_xinput().ok_or(io::ErrorKind::NotFound)?;
Ok(Self {
XInputGetDSoundAudioDeviceGuids: lib.sym_opt("XInputGetDSoundAudioDeviceGuids\0"),
XInputGetAudioDeviceIds: lib.sym_opt("XInputGetAudioDeviceIds\0"),
_XInputGetStateEx: lib.sym_opt_by_ordinal(100),
_XInputWaitForGuideButton: lib.sym_opt_by_ordinal(101),
_XInputCancelGuideButtonWait: lib.sym_opt_by_ordinal(102),
_XInputPowerOffController: lib.sym_opt_by_ordinal(103),
})
}
}
}
unsafe fn try_find_loaded_xinput() -> Option<Library> {
let proc = GetCurrentProcess();
let mut modules = Vec::<HMODULE>::new();
let mut max_retries = 64;
loop {
let available_bytes = u32::try_from(std::mem::size_of_val(&modules[..])).unwrap_or(!0);
let mut needed_bytes : u32 = 0;
let ok = EnumProcessModulesEx(proc, modules.as_mut_ptr(), available_bytes, &mut needed_bytes, LIST_MODULES_DEFAULT);
if ok == FALSE {
if max_retries == 0 { return None; }
max_retries -= 1;
continue; }
let needed_elements = usize::try_from(needed_bytes).unwrap_or(!0usize) / size_of::<HMODULE>();
if needed_bytes <= available_bytes {
modules.shrink_to(needed_elements);
break } else {
modules.resize(needed_elements, null_mut());
continue }
}
modules.retain(|&m| unsafe { Library::from_hmodule(m) }.map_or(false, |m| m.has_sym("XInputGetState\0")));
let hmodule = match modules[..] {
[] => None,
[module] => Some(module),
ref mut multiple => {
let mut name = [0u8; 4096];
multiple.sort_by_cached_key(|&m|{
let len = GetModuleBaseNameA(proc, m, name.as_mut_ptr().cast(), name.len() as _) as usize;
let name = &mut name[..len];
name.make_ascii_lowercase();
let prefix = b"xinput_";
let matching = prefix.iter().copied().zip(name.iter().copied()).position(|(x,y)| x != y).unwrap_or(prefix.len());
(matching * 1000 + 1000).saturating_sub(name.len()) });
multiple.last().copied()
},
};
hmodule.and_then(|hmodule| unsafe { Library::from_hmodule(hmodule) })
}