br-hid 0.2.1

This is an Bluetooth HID
Documentation
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);

            // Flags
            // 0 (key down)
            // 1 (key up) 
            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);

                    // shift state
                    // pressed: -127, -128 
                    // not pressed: 0, 1
                    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
    };

    // e0:65:6a:59:3d:e9 => e0656a593de9
    let check_id = query_device_id.replace(":", "");
    
    for device in device_vec {
        if device.dwType != RIM_TYPEKEYBOARD {
            continue;
        }

        // GR-CP11224010593D
        // \\?\HID#{00001812-0000-1000-8000-00805f9b34fb}_e0656a593de9&Col03#9&2060c4ce&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}\KBD,
        let device_name = get_device_name(device.hDevice);

        // 1. wireless
        if device_name.contains(&check_id) {
            return Some(device.hDevice.0)
        }

        // 2. wired
        // \\?\HID#VID_046D&PID_C31D&MI_00#8&e805c6&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\KBD
        let wired_check_id = get_wired_check_id_from_path(&device_name);
        // log::trace!("wired_check_id: {}, path: {}", wired_check_id, 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(":", "")
}

// Function to get and cache the device name
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;

    // 调用 RegisterRawInputDevices
    unsafe {
        RegisterRawInputDevices(&devices, cb_size)
    }
}

fn get_raw_input_data(lparam: LPARAM) -> RAWINPUT {
    // log::debug!("WM_INPUT lparam: {:?}", lparam);
    let mut pcb_size = 0;

    // 1. Get the required buffer size for the RAWINPUT data
    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) }
}

// from hidapi device list
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);
    }

    // GR-CP11224010593D
    // "\\\\?\\HID#{00001812-0000-1000-8000-00805f9b34fb}_e0656a593de9&Col03#9&2060c4ce&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}\\KBD",
    // sn: Some("e0656a593de9"), rn: 0

    // GL-BLE#00
    // "\\\\?\HID#{00001812-0000-1000-8000-00805f9b34fb}_Dev_VID&01000d_PID&0000_REV&0110_b0d2783df3fa#9&e335a30&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\KBD"
    // sn: Some("b0d2783df3fa"), rn: 272
    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(":");
    }
    // 有线键盘
    // "\\\\?\\HID#VID_342D&PID_E4C6&MI_00&Col01#7&ab78108&0&0000#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}"
    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);
    }

    // "GR-CP11224010593D"
    // "\\\\?\\HID#{00001812-0000-1000-8000-00805f9b34fb}_e0656a593de9&Col03#9&2060c4ce&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}\\KBD",
    // sn: Some("e0656a593de9"), rn: 0
    // uid: e0:65:6a:59:3d:e9

    // GL
    // "\\\\?\HID#{00001812-0000-1000-8000-00805f9b34fb}_Dev_VID&01000d_PID&0000_REV&0110_b0d2783df3fa#9&e335a30&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\KBD"
    // sn: Some("b0d2783df3fa"), rn: 272

    if path.starts_with("\\\\?\\HID#{") {
        // log::debug!("wireless device {}", path);
        if let Some(idx) = path.find("}") {
            if path.len() > idx + 10 {
                let mac_str = &path[idx+2..idx+14];

                // e0:65:6a:59:3d:e9
                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)
        }
    }
    // 有线键盘
    // "\\\\?\\HID#VID_342D&PID_E4C6&MI_00&Col01#7&ab78108&0&0000#{884b96c3-56ef-11d1-bc8c-00a0c91405dd}"
    else if path.starts_with("\\\\?\\HID#VID_") {
        log::trace!("wired device {}", path);
    } 

    // unhandle case
    let n = calculate_hash(&path);
    u64_to_mac(n)
}