winapi-virtual-input 0.1.3

Crate for interacting with virtual devices using winapi
Documentation
use crate::display;
use crate::display::DisplayInfo;
use std::mem::{size_of, zeroed, MaybeUninit};
use virtual_input::mouse::{Mouse, MouseButton, MousePosition};
use winapi::shared::windef::POINT;
use winapi::um::winuser::{
    GetAsyncKeyState, GetCursorPos, SendInput, SetCursorPos, INPUT, INPUT_MOUSE,
    MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP,
    MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, MOUSEINPUT, VK_LBUTTON, VK_MBUTTON, VK_RBUTTON,
};

pub struct WinApiMouse {
    primary_display_info: DisplayInfo,
    displays_info: Vec<DisplayInfo>,
}

impl WinApiMouse {
    pub fn init() -> Self {
        let displays_info = display::get_displays_info();

        // By default we use primary display.
        let primary_display_info = *displays_info.iter().find(|d| d.primary).unwrap();

        WinApiMouse {
            primary_display_info,
            displays_info,
        }
    }
}

impl Mouse for WinApiMouse {
    fn press(&self, button: MouseButton) -> bool {
        send_mouse_button_input(button, false)
    }

    fn release(&self, button: MouseButton) -> bool {
        send_mouse_button_input(button, true)
    }

    fn is_pressed(&self, button: MouseButton) -> bool {
        let virtual_key_code = get_virtual_key_code(button);
        let key_state = unsafe { GetAsyncKeyState(virtual_key_code) } as u16;

        // If the most significant bit is set, the key is pressed.
        key_state >> 15 == 1
    }

    fn move_to(&self, x: u16, y: u16, display_index: Option<usize>) -> bool {
        let display_info = match display_index {
            Some(display_index) => self
                .displays_info
                .get(display_index)
                .unwrap_or_else(|| &self.primary_display_info),
            _ => &self.primary_display_info,
        };

        let offset_x = self
            .displays_info
            .iter()
            .filter(|d| d.position.right < display_info.position.right)
            .map(|d| d.width as i32)
            .sum::<i32>();

        let offset_y = self
            .displays_info
            .iter()
            .filter(|d| d.position.top < display_info.position.top)
            .map(|d| d.height as i32)
            .sum::<i32>();

        unsafe { SetCursorPos(x as i32 + offset_x, y as i32 + offset_y) == 1 }
    }

    fn get_position(&self) -> MousePosition {
        let mut maybe_point = MaybeUninit::<POINT>::uninit();

        unsafe {
            GetCursorPos(maybe_point.as_mut_ptr());
        }

        let point = unsafe { maybe_point.assume_init() };

        let global_x = point.x;
        let global_y = point.y;

        let (display_index, x) = self
            .displays_info
            .iter()
            .enumerate()
            .map(|(index, info)| (index, &info.position))
            .find(|(_, position)| global_x >= position.left && global_x <= position.right)
            .map(|(index, position)| (index, (global_x - position.left) as u16))
            .unwrap();

        // For simplicity, we assume that all monitors will be lined up and will have the same height.
        let y = global_y as u16;

        MousePosition {
            display_index,
            x,
            y,
        }
    }
}

fn send_mouse_button_input(button: MouseButton, release: bool) -> bool {
    unsafe {
        let mut input = INPUT {
            type_: INPUT_MOUSE,
            u: zeroed(),
        };

        let flags = if release {
            match button {
                MouseButton::Left => MOUSEEVENTF_LEFTUP,
                MouseButton::Middle => MOUSEEVENTF_MIDDLEUP,
                MouseButton::Right => MOUSEEVENTF_RIGHTUP,
            }
        } else {
            match button {
                MouseButton::Left => MOUSEEVENTF_LEFTDOWN,
                MouseButton::Middle => MOUSEEVENTF_MIDDLEDOWN,
                MouseButton::Right => MOUSEEVENTF_RIGHTDOWN,
            }
        };

        *input.u.mi_mut() = MOUSEINPUT {
            dx: 0,
            dy: 0,
            mouseData: 0,
            dwFlags: flags,
            time: 0,
            dwExtraInfo: 0,
        };

        SendInput(1, &mut input, size_of::<INPUT>() as i32) == 1
    }
}

fn get_virtual_key_code(button: MouseButton) -> i32 {
    match button {
        MouseButton::Left => VK_LBUTTON,
        MouseButton::Middle => VK_MBUTTON,
        MouseButton::Right => VK_RBUTTON,
    }
}