hid-api-rs 3.0.1

Hardware proxy for injecting & intercepting keyboard/mouse inputs
Documentation
use std::io::{BufWriter, Error, ErrorKind};
use std::thread;
use std::time::Duration;

use once_cell::sync::Lazy;

use gadgets::keyboard::{self, Keyboard, KeyboardState};
use gadgets::mouse::{self, Mouse};

use crate::gadgets::mouse::MouseRaw;

pub mod gadgets;
pub mod hid;

#[derive(Clone)]
pub struct HidSpecification {
    pub mouse_inputs: Option<Vec<HidMouse>>,
    pub keyboard_inputs: Option<Vec<String>>,
    pub gadget_output: String,
}

#[derive(Clone)]
pub struct HidMouse {
    pub mouse_path: String,
    pub mouse_poll_rate: Option<i32>,
    pub mouse_side_buttons: bool,
}

static mut HID_SPEC: Option<HidSpecification> = None;
static mut MOUSE_INTERFACES: Lazy<Vec<Mouse>> = Lazy::new(Vec::new);
static mut MOUSE_READING: bool = true;

static mut KEYBOARD_INTERFACES: Vec<Keyboard> = Vec::new();
static mut KEYBOARD_READING: bool = true;

static mut GLOBAL_KEYBOARD_STATE: Lazy<KeyboardState> = Lazy::new(KeyboardState::default);

pub fn start_pass_through(specification: HidSpecification) -> Result<(), Error> {
    unsafe {
        HID_SPEC = Some(specification.clone());
    }

    start_hot_reload(specification.mouse_inputs.clone(), specification.keyboard_inputs.clone());

    unsafe {
        if specification.mouse_inputs.is_some() {
            let mouse_spec = specification.clone();
            thread::spawn(move || {
                static mut MOUSE_THREADS: Vec<String> = Vec::new();
                loop {
                    if !MOUSE_READING {
                        return;
                    }

                    if MOUSE_INTERFACES.is_empty() {
                        thread::sleep(Duration::from_millis(1));
                        continue;
                    }

                    for (mouse_interface_index, mouse) in MOUSE_INTERFACES.iter_mut().enumerate() {
                        if !MOUSE_THREADS.contains(&mouse.mouse_path) || MOUSE_THREADS.is_empty() {
                            let gadget_mouse = match hid::open_gadget_device(mouse_spec.gadget_output.clone()) {
                                Ok(gadget_device) => gadget_device,
                                Err(_) => continue,
                            };

                            MOUSE_THREADS.push(mouse.mouse_path.clone());

                            let mut mouse_writer = BufWriter::new(gadget_mouse);
                            thread::spawn(move || {
                                loop {
                                    if !MOUSE_READING {
                                        break;
                                    }

                                    if mouse::attempt_read(mouse, &mut mouse_writer).is_err() {
                                        MOUSE_INTERFACES.remove(mouse_interface_index);
                                        MOUSE_THREADS.remove(mouse_interface_index);

                                        break;
                                    };
                                }
                            });
                        }
                    }

                    thread::sleep(Duration::from_millis(1));
                }
            });
        }

        if specification.keyboard_inputs.is_some() {
            let keyboard_spec = specification.clone();
            thread::spawn(move || {
                static mut KEYBOARD_THREADS: Vec<String> = Vec::new();
                loop {
                    if !KEYBOARD_READING {
                        break;
                    }

                    if KEYBOARD_INTERFACES.is_empty() {
                        thread::sleep(Duration::from_millis(1));
                        continue;
                    }

                    for (keyboard_interface_index, keyboard) in KEYBOARD_INTERFACES.iter_mut().enumerate() {
                        if !KEYBOARD_THREADS.contains(&keyboard.keyboard_path) || KEYBOARD_THREADS.is_empty() {
                            let gadget_keyboard = match hid::open_gadget_device(keyboard_spec.gadget_output.clone()) {
                                Ok(gadget_device) => gadget_device,
                                Err(_) => continue,
                            };

                            KEYBOARD_THREADS.push(keyboard.keyboard_path.clone());

                            let mut keyboard_writer = BufWriter::new(gadget_keyboard);
                            thread::spawn(move || {
                                loop {
                                    if !KEYBOARD_READING {
                                        break;
                                    }

                                    if keyboard::attempt_read(keyboard, &mut GLOBAL_KEYBOARD_STATE, &mut keyboard_writer).is_err() {
                                        KEYBOARD_INTERFACES.remove(keyboard_interface_index);
                                        KEYBOARD_THREADS.remove(keyboard_interface_index);

                                        break;
                                    };
                                }
                            });
                        }
                    }

                    thread::sleep(Duration::from_millis(1));
                }
            });
        }
    }

    Ok(())
}

pub fn stop_pass_through() -> Result<(), Error> {
    unsafe {
        MOUSE_READING = false;
        KEYBOARD_READING = false;

        match &HID_SPEC {
            Some(spec) => {
                let gadget_device = match hid::open_gadget_device(spec.gadget_output.clone()) {
                    Ok(gadget_device) => gadget_device,
                    Err(err) => return Err(err),
                };

                let mut gadget_writer = BufWriter::new(gadget_device);

                MOUSE_INTERFACES.clear();
                mouse::push_mouse_event(MouseRaw::default(), None, &mut gadget_writer)?;

                KEYBOARD_INTERFACES.clear();
                static mut DEFAULT_KEYBOARD_STATE: Lazy<KeyboardState> =
                    Lazy::new(KeyboardState::default);
                keyboard::attempt_flush(&mut DEFAULT_KEYBOARD_STATE, &mut gadget_writer)?;

                Ok(())
            }
            None => Err(Error::new(
                ErrorKind::Other,
                String::from("Hid specification not defined cannot open gadget device"),
            ))
        }
    }
}

fn start_hot_reload(
    mouse_inputs: Option<Vec<HidMouse>>,
    keyboard_inputs: Option<Vec<String>>,
) {
    if let Some(mouse_inputs) = mouse_inputs {
        if !mouse_inputs.is_empty() {
            thread::spawn(move || unsafe {
                loop {
                    if !MOUSE_READING {
                        break;
                    }

                    mouse::check_mouses(&mouse_inputs, &mut MOUSE_INTERFACES);
                }
            });
        }
    }

    if let Some(keyboard_inputs) = keyboard_inputs {
        if !keyboard_inputs.is_empty() {
            thread::spawn(move || unsafe {
                loop {
                    if !KEYBOARD_READING {
                        break;
                    }

                    keyboard::check_keyboards(&keyboard_inputs, &mut KEYBOARD_INTERFACES);
                }
            });
        }
    }
}

pub fn get_mouses() -> &'static mut Lazy<Vec<Mouse>, fn() -> Vec<Mouse>> {
    unsafe { &mut MOUSE_INTERFACES }
}

pub fn get_keyboard() -> &'static mut KeyboardState {
    unsafe { &mut GLOBAL_KEYBOARD_STATE }
}