use std::thread;
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use windows::{
core::*,
Win32::{
Foundation::*,
System::LibraryLoader::GetModuleHandleW,
UI::{
Input::*,
WindowsAndMessaging::*,
Input::KeyboardAndMouse::*,
},
},
};
use hidapi::DeviceInfo;
use crate::utils::{u64_to_mac, calculate_hash};
use super::LISTEN_DEVICE_VEC;
pub fn start_raw_input_task() {
thread::spawn(move||{
let hwnd = match create_dummy_window() {
Ok(v) => v,
Err(e) => {
log::warn!("create_dummy_window failed: {:?}", e);
return;
}
};
log::debug!("create dummy window success");
if let Err(e) = register_raw_input_devices(hwnd) {
log::warn!("register_raw_input_devices failed: {:?}", e);
return;
}
log::debug!("register raw_input_class success");
log::debug!("start message dispatch");
unsafe {
let mut msg = MSG::default();
while GetMessageW(&mut msg, None, 0, 0).into() {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
});
}
fn create_dummy_window() -> Result<HWND> {
let instance = unsafe {GetModuleHandleW(None)?};
let window_class = w!("RawInputWindowClass");
let wc = WNDCLASSW {
hInstance: instance.into(),
lpszClassName: window_class,
lpfnWndProc: Some(dummy_window_proc),
..Default::default()
};
let res = unsafe { RegisterClassW(&wc) };
if res == 0 {
log::debug!("RegisterClassW return 0, register failed ");
return Ok(HWND(0))
}
log::debug!("RegisterClassW success: {}", res);
let hwnd = unsafe {
CreateWindowExW(
WINDOW_EX_STYLE::default(),
window_class,
w!("Raw Input Catch"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
None,
None,
instance,
None,
)
};
Ok(hwnd)
}
extern "system" fn dummy_window_proc(
hwnd: HWND,
msg: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
#[allow(clippy::single_match)]
match msg {
WM_INPUT => {
let raw_input = get_raw_input_data(lparam);
let keyboard= unsafe { raw_input.data.keyboard };
log::trace!("WM_INPUT: {}, {}", keyboard.VKey, keyboard.Flags);
if keyboard.Flags == 1 {
return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) };
}
if raw_input.header.dwType == RIM_TYPEKEYBOARD.0 {
let mut devices = LISTEN_DEVICE_VEC.lock().unwrap();
if let Some(tx) = devices.get(&raw_input.header.hDevice.0) {
log::trace!("Found device tx: {}", raw_input.header.hDevice.0);
let is_shift_press = unsafe { GetKeyState(VK_SHIFT.0 as i32) < 0 };
if let Err(e) = tx.send((keyboard, is_shift_press)) {
let device_name = get_device_name(raw_input.header.hDevice);
log::warn!("Device {} channel failed {}", device_name, e);
devices.remove(&raw_input.header.hDevice.0);
}
}
else {
log::trace!("Not found device tx: {}", raw_input.header.hDevice.0);
}
drop(devices);
}
},
_ => {},
}
unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }
}
pub(crate) fn get_device_handle_0(query_device_id: &str) -> Option<isize> {
let device_vec = {
let mut device_count = 0u32;
unsafe {
GetRawInputDeviceList(
None,
&mut device_count,
std::mem::size_of::<RAWINPUTDEVICELIST>() as u32,
)
};
if device_count == 0 {
log::debug!("No input devices found");
return None;
}
let mut res = vec![RAWINPUTDEVICELIST::default(); device_count as usize];
unsafe {
GetRawInputDeviceList(
Some(res.as_mut_ptr()),
&mut device_count,
std::mem::size_of::<RAWINPUTDEVICELIST>() as u32,
)
};
res
};
let check_id = query_device_id.replace(":", "");
for device in device_vec {
if device.dwType != RIM_TYPEKEYBOARD {
continue;
}
let device_name = get_device_name(device.hDevice);
if device_name.contains(&check_id) {
return Some(device.hDevice.0)
}
let wired_check_id = get_wired_check_id_from_path(&device_name);
if wired_check_id == check_id {
return Some(device.hDevice.0)
}
}
None
}
fn get_wired_check_id_from_path(device_name: &str) -> String {
let mut path = device_name.to_string();
if let Some(idx) = path.rfind("#{"){
path.truncate(idx);
}
let n = calculate_hash(&path);
let raw_uid = u64_to_mac(n);
raw_uid.replace(":", "")
}
fn get_device_name(h_device: HANDLE) ->String {
let mut name_size = 0u32;
unsafe {
GetRawInputDeviceInfoW(
h_device,
RIDI_DEVICENAME,
None,
&mut name_size,
)
};
let mut name_buffer = vec![0u16; name_size as usize];
unsafe {
GetRawInputDeviceInfoW(
h_device,
RIDI_DEVICENAME,
Some(name_buffer.as_mut_ptr() as _),
&mut name_size,
)
};
let device_name = OsString::from_wide(&name_buffer[..name_buffer.len() - 1]);
device_name.to_string_lossy().to_string()
}
pub(crate) fn register_raw_input_devices(hwnd: HWND) -> Result<()> {
let devices = [
RAWINPUTDEVICE {
usUsagePage: 0x01, usUsage: 0x06, dwFlags: RIDEV_INPUTSINK, hwndTarget: hwnd, }
];
let cb_size = std::mem::size_of::<RAWINPUTDEVICE>() as u32;
unsafe {
RegisterRawInputDevices(&devices, cb_size)
}
}
fn get_raw_input_data(lparam: LPARAM) -> RAWINPUT {
let mut pcb_size = 0;
let dbg_info = unsafe {
GetRawInputData(
HRAWINPUT(lparam.0 as _ ),
RID_INPUT,
None,
&mut pcb_size,
std::mem::size_of::<RAWINPUTHEADER>() as u32,
)
};
log::trace!("GetRawInputData 1 : size: {}, dbg_info: {}", pcb_size, dbg_info);
let mut buffer = vec![0u8; pcb_size as usize];
let dbg_info = unsafe {
GetRawInputData(
HRAWINPUT(lparam.0 as _ ),
RID_INPUT,
Some(buffer.as_mut_ptr() as _),
&mut pcb_size,
std::mem::size_of::<RAWINPUTHEADER>() as u32,
)
};
log::trace!("GetRawInputData 2 : size: {}, dbg_info: {}", pcb_size, dbg_info);
unsafe { std::ptr::read(buffer.as_ptr() as *const RAWINPUT) }
}
pub(crate) fn get_uid_from_device_info(device_info: &DeviceInfo)->String {
let mut path = device_info.path().to_string_lossy().to_string();
if let Some(idx) = path.rfind("#{"){
path.truncate(idx);
}
if path.starts_with("\\\\?\\HID#{") && device_info.serial_number().is_some() {
let sn_str = device_info.serial_number().unwrap();
return sn_str.chars()
.collect::<Vec<_>>()
.chunks(2)
.map(|chunk| chunk.iter().collect::<String>())
.collect::<Vec<_>>()
.join(":");
}
else if path.starts_with("\\\\?\\HID#VID_") {
log::trace!("wired device {}", path);
}
let n = calculate_hash(&path);
return u64_to_mac(n)
}
pub(crate) fn raw_keyboard_to_digit_str(raw_keyboard: RAWKEYBOARD, is_shift_press: bool) -> &'static str{
match (VIRTUAL_KEY(raw_keyboard.VKey), is_shift_press) {
(VK_NUMPAD0, _) | (VK_0, false) => "0",
(VK_NUMPAD1, _) | (VK_1, false) => "1",
(VK_NUMPAD2, _) | (VK_2, false) => "2",
(VK_NUMPAD3, _) | (VK_3, false) => "3",
(VK_NUMPAD4, _) | (VK_4, false) => "4",
(VK_NUMPAD5, _) | (VK_5, false) => "5",
(VK_NUMPAD6, _) | (VK_6, false) => "6",
(VK_NUMPAD7, _) | (VK_7, false) => "7",
(VK_NUMPAD8, _) | (VK_8, false) => "8",
(VK_NUMPAD9, _) | (VK_9, false) => "9",
(VK_SUBTRACT, _) | (VK_OEM_MINUS, false) => "-",
(VK_ADD, _) | (VK_OEM_PLUS, true) => "+",
(VK_DECIMAL, _) | (VK_OEM_PERIOD, false) => ".",
(VK_RETURN, _) => "\n",
(VK_TAB, _) => "\t",
_ => "",
}
}
#[allow(dead_code)]
pub(crate) fn get_uid_device_raw_path(path: &str) -> String {
let mut path = path.to_string();
if let Some(idx) = path.rfind("#{"){
path.truncate(idx);
}
if path.starts_with("\\\\?\\HID#{") {
if let Some(idx) = path.find("}") {
if path.len() > idx + 10 {
let mac_str = &path[idx+2..idx+14];
return mac_str.chars()
.collect::<Vec<char>>()
.chunks(2)
.map(|chunk| chunk.iter().collect::<String>())
.collect::<Vec<_>>()
.join(":");
}
} else {
log::warn!("Wireless device path not in rule: {}", path)
}
}
else if path.starts_with("\\\\?\\HID#VID_") {
log::trace!("wired device {}", path);
}
let n = calculate_hash(&path);
u64_to_mac(n)
}