rustdesk/libs/enigo/src/win/win_impl.rs
fufesou 97cf85d1b7 mouse forward back support on windows
Signed-off-by: fufesou <shuanglongchen@yeah.net>
2023-01-05 17:47:26 +08:00

423 lines
14 KiB
Rust

use self::winapi::ctypes::c_int;
use self::winapi::shared::{basetsd::ULONG_PTR, minwindef::*, windef::*};
use self::winapi::um::winbase::*;
use self::winapi::um::winuser::*;
use winapi;
use crate::win::keycodes::*;
use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
use std::mem::*;
extern "system" {
pub fn GetLastError() -> DWORD;
}
/// The main struct for handling the event emitting
#[derive(Default)]
pub struct Enigo;
static mut LAYOUT: HKL = std::ptr::null_mut();
/// The dwExtraInfo value in keyboard and mouse structure that used in SendInput()
pub const ENIGO_INPUT_EXTRA_VALUE: ULONG_PTR = 100;
fn mouse_event(flags: u32, data: u32, dx: i32, dy: i32) -> DWORD {
let mut input: INPUT = unsafe { std::mem::MaybeUninit::zeroed().assume_init() };
input.type_ = INPUT_MOUSE;
unsafe {
let dst_ptr = (&mut input.u as *mut _) as *mut u8;
let m = MOUSEINPUT {
dx,
dy,
mouseData: data,
dwFlags: flags,
time: 0,
dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE,
};
let src_ptr = (&m as *const _) as *const u8;
std::ptr::copy_nonoverlapping(src_ptr, dst_ptr, size_of::<MOUSEINPUT>());
}
unsafe { SendInput(1, &mut input as LPINPUT, size_of::<INPUT>() as c_int) }
}
fn keybd_event(flags: u32, vk: u16, scan: u16) -> DWORD {
let mut scan = scan;
unsafe {
// https://github.com/rustdesk/rustdesk/issues/366
if scan == 0 {
if LAYOUT.is_null() {
let current_window_thread_id =
GetWindowThreadProcessId(GetForegroundWindow(), std::ptr::null_mut());
LAYOUT = GetKeyboardLayout(current_window_thread_id);
}
scan = MapVirtualKeyExW(vk as _, 0, LAYOUT) as _;
}
}
let mut input: INPUT = unsafe { std::mem::MaybeUninit::zeroed().assume_init() };
input.type_ = INPUT_KEYBOARD;
unsafe {
let dst_ptr = (&mut input.u as *mut _) as *mut u8;
let k = KEYBDINPUT {
wVk: vk,
wScan: scan,
dwFlags: flags,
time: 0,
dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE,
};
let src_ptr = (&k as *const _) as *const u8;
std::ptr::copy_nonoverlapping(src_ptr, dst_ptr, size_of::<KEYBDINPUT>());
}
unsafe { SendInput(1, &mut input as LPINPUT, size_of::<INPUT>() as c_int) }
}
fn get_error() -> String {
unsafe {
let buff_size = 256;
let mut buff: Vec<u16> = Vec::with_capacity(buff_size);
buff.resize(buff_size, 0);
let errno = GetLastError();
let chars_copied = FormatMessageW(
FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_ARGUMENT_ARRAY,
std::ptr::null(),
errno,
0,
buff.as_mut_ptr(),
(buff_size + 1) as u32,
std::ptr::null_mut(),
);
if chars_copied == 0 {
return "".to_owned();
}
let mut curr_char: usize = chars_copied as usize;
while curr_char > 0 {
let ch = buff[curr_char];
if ch >= ' ' as u16 {
break;
}
curr_char -= 1;
}
let sl = std::slice::from_raw_parts(buff.as_ptr(), curr_char);
let err_msg = String::from_utf16(sl);
return err_msg.unwrap_or("".to_owned());
}
}
impl MouseControllable for Enigo {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_mut_any(&mut self) -> &mut dyn std::any::Any {
self
}
fn mouse_move_to(&mut self, x: i32, y: i32) {
mouse_event(
MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK,
0,
(x - unsafe { GetSystemMetrics(SM_XVIRTUALSCREEN) }) * 65535
/ unsafe { GetSystemMetrics(SM_CXVIRTUALSCREEN) },
(y - unsafe { GetSystemMetrics(SM_YVIRTUALSCREEN) }) * 65535
/ unsafe { GetSystemMetrics(SM_CYVIRTUALSCREEN) },
);
}
fn mouse_move_relative(&mut self, x: i32, y: i32) {
mouse_event(MOUSEEVENTF_MOVE, 0, x, y);
}
fn mouse_down(&mut self, button: MouseButton) -> crate::ResultType {
let res = mouse_event(
match button {
MouseButton::Left => MOUSEEVENTF_LEFTDOWN,
MouseButton::Middle => MOUSEEVENTF_MIDDLEDOWN,
MouseButton::Right => MOUSEEVENTF_RIGHTDOWN,
MouseButton::Back => MOUSEEVENTF_XDOWN,
MouseButton::Forward => MOUSEEVENTF_XDOWN,
_ => unimplemented!(),
},
match button {
MouseButton::Back => XBUTTON1 as _,
MouseButton::Forward => XBUTTON2 as _,
_ => 0,
},
0,
0,
);
if res == 0 {
let err = get_error();
if !err.is_empty() {
return Err(err.into());
}
}
Ok(())
}
fn mouse_up(&mut self, button: MouseButton) {
mouse_event(
match button {
MouseButton::Left => MOUSEEVENTF_LEFTUP,
MouseButton::Middle => MOUSEEVENTF_MIDDLEUP,
MouseButton::Right => MOUSEEVENTF_RIGHTUP,
MouseButton::Back => MOUSEEVENTF_XUP,
MouseButton::Forward => MOUSEEVENTF_XUP,
_ => unimplemented!(),
},
match button {
MouseButton::Back => XBUTTON1 as _,
MouseButton::Forward => XBUTTON2 as _,
_ => 0,
},
0,
0,
);
}
fn mouse_click(&mut self, button: MouseButton) {
self.mouse_down(button).ok();
self.mouse_up(button);
}
fn mouse_scroll_x(&mut self, length: i32) {
mouse_event(MOUSEEVENTF_HWHEEL, unsafe { transmute(length * 120) }, 0, 0);
}
fn mouse_scroll_y(&mut self, length: i32) {
mouse_event(MOUSEEVENTF_WHEEL, unsafe { transmute(length * 120) }, 0, 0);
}
}
impl KeyboardControllable for Enigo {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_mut_any(&mut self) -> &mut dyn std::any::Any {
self
}
fn key_sequence(&mut self, sequence: &str) {
let mut buffer = [0; 2];
for c in sequence.chars() {
// Windows uses uft-16 encoding. We need to check
// for variable length characters. As such some
// characters can be 32 bit long and those are
// encoded in such called hight and low surrogates
// each 16 bit wide that needs to be send after
// another to the SendInput function without
// being interrupted by "keyup"
let result = c.encode_utf16(&mut buffer);
if result.len() == 1 {
self.unicode_key_click(result[0]);
} else {
for utf16_surrogate in result {
self.unicode_key_down(utf16_surrogate.clone());
}
// do i need to produce a keyup?
// self.unicode_key_up(0);
}
}
}
fn key_click(&mut self, key: Key) {
let vk = self.key_to_keycode(key);
keybd_event(0, vk, 0);
keybd_event(KEYEVENTF_KEYUP, vk, 0);
}
fn key_down(&mut self, key: Key) -> crate::ResultType {
let code = self.key_to_keycode(key);
if code == 0 || code == 65535 {
return Err("".into());
}
let res = keybd_event(0, code, 0);
if res == 0 {
let err = get_error();
if !err.is_empty() {
return Err(err.into());
}
}
Ok(())
}
fn key_up(&mut self, key: Key) {
keybd_event(KEYEVENTF_KEYUP, self.key_to_keycode(key), 0);
}
fn get_key_state(&mut self, key: Key) -> bool {
let keycode = self.key_to_keycode(key);
let x = unsafe { GetKeyState(keycode as _) };
if key == Key::CapsLock || key == Key::NumLock || key == Key::Scroll {
return (x & 0x1) == 0x1;
}
return (x as u16 & 0x8000) == 0x8000;
}
}
impl Enigo {
/// Gets the (width, height) of the main display in screen coordinates
/// (pixels).
///
/// # Example
///
/// ```no_run
/// use enigo::*;
/// let mut size = Enigo::main_display_size();
/// ```
pub fn main_display_size() -> (usize, usize) {
let w = unsafe { GetSystemMetrics(SM_CXSCREEN) as usize };
let h = unsafe { GetSystemMetrics(SM_CYSCREEN) as usize };
(w, h)
}
/// Gets the location of mouse in screen coordinates (pixels).
///
/// # Example
///
/// ```no_run
/// use enigo::*;
/// let mut location = Enigo::mouse_location();
/// ```
pub fn mouse_location() -> (i32, i32) {
let mut point = POINT { x: 0, y: 0 };
let result = unsafe { GetCursorPos(&mut point) };
if result != 0 {
(point.x, point.y)
} else {
(0, 0)
}
}
fn unicode_key_click(&self, unicode_char: u16) {
self.unicode_key_down(unicode_char);
self.unicode_key_up(unicode_char);
}
fn unicode_key_down(&self, unicode_char: u16) {
keybd_event(KEYEVENTF_UNICODE, 0, unicode_char);
}
fn unicode_key_up(&self, unicode_char: u16) {
keybd_event(KEYEVENTF_UNICODE | KEYEVENTF_KEYUP, 0, unicode_char);
}
fn key_to_keycode(&self, key: Key) -> u16 {
unsafe {
LAYOUT = std::ptr::null_mut();
}
// do not use the codes from crate winapi they're
// wrongly typed with i32 instead of i16 use the
// ones provided by win/keycodes.rs that are prefixed
// with an 'E' infront of the original name
#[allow(deprecated)]
// I mean duh, we still need to support deprecated keys until they're removed
match key {
Key::Alt => EVK_MENU,
Key::Backspace => EVK_BACK,
Key::CapsLock => EVK_CAPITAL,
Key::Control => EVK_LCONTROL,
Key::Delete => EVK_DELETE,
Key::DownArrow => EVK_DOWN,
Key::End => EVK_END,
Key::Escape => EVK_ESCAPE,
Key::F1 => EVK_F1,
Key::F10 => EVK_F10,
Key::F11 => EVK_F11,
Key::F12 => EVK_F12,
Key::F2 => EVK_F2,
Key::F3 => EVK_F3,
Key::F4 => EVK_F4,
Key::F5 => EVK_F5,
Key::F6 => EVK_F6,
Key::F7 => EVK_F7,
Key::F8 => EVK_F8,
Key::F9 => EVK_F9,
Key::Home => EVK_HOME,
Key::LeftArrow => EVK_LEFT,
Key::Option => EVK_MENU,
Key::PageDown => EVK_NEXT,
Key::PageUp => EVK_PRIOR,
Key::Return => EVK_RETURN,
Key::RightArrow => EVK_RIGHT,
Key::Shift => EVK_SHIFT,
Key::Space => EVK_SPACE,
Key::Tab => EVK_TAB,
Key::UpArrow => EVK_UP,
Key::Numpad0 => EVK_NUMPAD0,
Key::Numpad1 => EVK_NUMPAD1,
Key::Numpad2 => EVK_NUMPAD2,
Key::Numpad3 => EVK_NUMPAD3,
Key::Numpad4 => EVK_NUMPAD4,
Key::Numpad5 => EVK_NUMPAD5,
Key::Numpad6 => EVK_NUMPAD6,
Key::Numpad7 => EVK_NUMPAD7,
Key::Numpad8 => EVK_NUMPAD8,
Key::Numpad9 => EVK_NUMPAD9,
Key::Cancel => EVK_CANCEL,
Key::Clear => EVK_CLEAR,
Key::Pause => EVK_PAUSE,
Key::Kana => EVK_KANA,
Key::Hangul => EVK_HANGUL,
Key::Junja => EVK_JUNJA,
Key::Final => EVK_FINAL,
Key::Hanja => EVK_HANJA,
Key::Kanji => EVK_KANJI,
Key::Convert => EVK_CONVERT,
Key::Select => EVK_SELECT,
Key::Print => EVK_PRINT,
Key::Execute => EVK_EXECUTE,
Key::Snapshot => EVK_SNAPSHOT,
Key::Insert => EVK_INSERT,
Key::Help => EVK_HELP,
Key::Sleep => EVK_SLEEP,
Key::Separator => EVK_SEPARATOR,
Key::Mute => EVK_VOLUME_MUTE,
Key::VolumeDown => EVK_VOLUME_DOWN,
Key::VolumeUp => EVK_VOLUME_UP,
Key::Scroll => EVK_SCROLL,
Key::NumLock => EVK_NUMLOCK,
Key::RWin => EVK_RWIN,
Key::Apps => EVK_APPS,
Key::Add => EVK_ADD,
Key::Multiply => EVK_MULTIPLY,
Key::Decimal => EVK_DECIMAL,
Key::Subtract => EVK_SUBTRACT,
Key::Divide => EVK_DIVIDE,
Key::NumpadEnter => EVK_RETURN,
Key::Equals => '=' as _,
Key::RightShift => EVK_RSHIFT,
Key::RightControl => EVK_RCONTROL,
Key::RightAlt => EVK_RMENU,
Key::Raw(raw_keycode) => raw_keycode,
Key::Layout(c) => self.get_layoutdependent_keycode(c.to_string()),
Key::Super | Key::Command | Key::Windows | Key::Meta => EVK_LWIN,
}
}
fn get_layoutdependent_keycode(&self, string: String) -> u16 {
// get the first char from the string ignore the rest
// ensure its not a multybyte char
if let Some(chr) = string.chars().nth(0) {
// NOTE VkKeyScanW uses the current keyboard LAYOUT
// to specify a LAYOUT use VkKeyScanExW and GetKeyboardLayout
// or load one with LoadKeyboardLayoutW
let current_window_thread_id =
unsafe { GetWindowThreadProcessId(GetForegroundWindow(), std::ptr::null_mut()) };
unsafe { LAYOUT = GetKeyboardLayout(current_window_thread_id) };
let keycode_and_shiftstate = unsafe { VkKeyScanExW(chr as _, LAYOUT) };
if keycode_and_shiftstate == (EVK_DECIMAL as i16) && chr == '.' {
// a workaround of italian keyboard shift + '.' issue
EVK_PERIOD as _
} else {
keycode_and_shiftstate as _
}
} else {
0
}
}
}