From ea09dc582d7dfe8f88ac9b32c9c812ef21fbf2fc Mon Sep 17 00:00:00 2001 From: crusader Date: Thu, 26 Nov 2020 11:59:04 +0900 Subject: [PATCH] ing --- Cargo.toml | 3 +- src/{keyboard/mod.rs => keyboard.rs} | 15 +- src/keyboard/windows/key_codes.rs | 153 ---------- src/keyboard/windows/mod.rs | 178 ------------ src/lib.rs | 30 ++ src/mouse.rs | 10 + src/mouse/mod.rs | 20 -- src/mouse/windows/mod.rs | 129 --------- src/test.rs | 9 - src/windows/keyboard.rs | 405 +++++++++++++++++++++++++++ src/windows/mod.rs | 37 +++ src/windows/mouse.rs | 213 ++++++++++++++ 12 files changed, 699 insertions(+), 503 deletions(-) rename src/{keyboard/mod.rs => keyboard.rs} (83%) delete mode 100644 src/keyboard/windows/key_codes.rs delete mode 100644 src/keyboard/windows/mod.rs create mode 100644 src/mouse.rs delete mode 100644 src/mouse/mod.rs delete mode 100644 src/mouse/windows/mod.rs delete mode 100644 src/test.rs create mode 100644 src/windows/keyboard.rs create mode 100644 src/windows/mod.rs create mode 100644 src/windows/mouse.rs diff --git a/Cargo.toml b/Cargo.toml index ae9a65e..9b575aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,12 +9,13 @@ edition = "2018" [dependencies] serde = { version = "1.0.117", optional = true } serde_derive = { version = "1.0.117", optional = true } +lazy_static = "1.4.0" [features] with_serde = ["serde", "serde_derive"] [target.'cfg(target_os = "windows")'.dependencies] -winapi = { version = "0.3.9", features = ["winuser"] } +winapi = { version = "0.3.9", features = ["winuser", "errhandlingapi"] } [build-dependencies] pkg-config = "0.3.19" diff --git a/src/keyboard/mod.rs b/src/keyboard.rs similarity index 83% rename from src/keyboard/mod.rs rename to src/keyboard.rs index 724d4b2..4cdec30 100644 --- a/src/keyboard/mod.rs +++ b/src/keyboard.rs @@ -1,9 +1,5 @@ -#[cfg(target_os = "windows")] -mod windows; -#[cfg(target_os = "windows")] -pub use crate::keyboard::windows::*; - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] pub enum Key { Escape, Digit0, @@ -123,10 +119,3 @@ pub enum Key { MetaLeft, MetaRight, } - -pub enum KeyboardState { - Press, - Release, -} - -pub struct Keyboard; diff --git a/src/keyboard/windows/key_codes.rs b/src/keyboard/windows/key_codes.rs deleted file mode 100644 index e158cea..0000000 --- a/src/keyboard/windows/key_codes.rs +++ /dev/null @@ -1,153 +0,0 @@ -// // https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731 - -// pub const VK_LBUTTON: u16 = 0x01; // Left mouse button -// pub const VK_RBUTTON: u16 = 0x02; // Right mouse button -// pub const VK_CANCEL: u16 = 0x03; // Control-break processing -// pub const VK_MBUTTON: u16 = 0x04; // Middle mouse button (three-button mouse) -// pub const VK_XBUTTON1: u16 = 0x05; // X1 mouse button -// pub const VK_XBUTTON2: u16 = 0x06; // X2 mouse button -// pub const VK_BACK: u16 = 0x08; // BACKSPACE key -// pub const VK_TAB: u16 = 0x09; // TAB key -// pub const VK_CLEAR: u16 = 0x0C; // CLEAR key -// pub const VK_RETURN: u16 = 0x0D; // ENTER key -// pub const VK_SHIFT: u16 = 0x10; // SHIFT key -// pub const VK_CONTROL: u16 = 0x11; // CTRL key -// pub const VK_MENU: u16 = 0x12; // ALT key -// pub const VK_PAUSE: u16 = 0x13; // PAUSE key -// pub const VK_CAPITAL: u16 = 0x14; // CAPS LOCK key -// pub const VK_KANA: u16 = 0x15; // IME Kana mode -// pub const VK_HANGUEL: u16 = 0x15; // IME Hanguel mode (maintained for compatibility; use VK_HANGUL) -// pub const VK_HANGUL: u16 = 0x15; // IME Hangul mode -// pub const VK_IME_ON: u16 = 0x16; // IME On -// pub const VK_JUNJA: u16 = 0x17; // IME Junja mode -// pub const VK_FINAL: u16 = 0x18; // IME final mode -// pub const VK_HANJA: u16 = 0x19; // IME Hanja mode -// pub const VK_KANJI: u16 = 0x19; // IME Kanji mode -// pub const VK_IME_OFF: u16 = 0x1A; // IME Off -// pub const VK_ESCAPE: u16 = 0x1B; // ESC key -// pub const VK_CONVERT: u16 = 0x1C; // IME convert -// pub const VK_NONCONVERT: u16 = 0x1D; // IME nonconvert -// pub const VK_ACCEPT: u16 = 0x1E; // IME accept -// pub const VK_MODECHANGE: u16 = 0x1F; // IME mode change request -// pub const VK_SPACE: u16 = 0x20; // SPACEBAR -// pub const VK_PRIOR: u16 = 0x21; // PAGE UP key -// pub const VK_NEXT: u16 = 0x22; // PAGE DOWN key -// pub const VK_END: u16 = 0x23; // END key -// pub const VK_HOME: u16 = 0x24; // HOME key -// pub const VK_LEFT: u16 = 0x25; // LEFT ARROW key -// pub const VK_UP: u16 = 0x26; // UP ARROW key -// pub const VK_RIGHT: u16 = 0x27; // RIGHT ARROW key -// pub const VK_DOWN: u16 = 0x28; // DOWN ARROW key -// pub const VK_SELECT: u16 = 0x29; // SELECT key -// pub const VK_PRINT: u16 = 0x2A; // PRINT key -// pub const VK_EXECUTE: u16 = 0x2B; // EXECUTE key -// pub const VK_SNAPSHOT: u16 = 0x2C; // PRINT SCREEN key -// pub const VK_INSERT: u16 = 0x2D; // INS key -// pub const VK_DELETE: u16 = 0x2E; // DEL key -// pub const VK_HELP: u16 = 0x2F; // HELP key -// pub const VK_DIGIT_0: u16 = 0x30; // 0 key -// pub const VK_DIGIT_1: u16 = 0x31; // 1 key -// pub const VK_DIGIT_2: u16 = 0x32; // 2 key -// pub const VK_DIGIT_3: u16 = 0x33; // 3 key -// pub const VK_DIGIT_4: u16 = 0x34; // 4 key -// pub const VK_DIGIT_5: u16 = 0x35; // 5 key -// pub const VK_DIGIT_6: u16 = 0x36; // 6 key -// pub const VK_DIGIT_7: u16 = 0x37; // 7 key -// pub const VK_DIGIT_8: u16 = 0x38; // 8 key -// pub const VK_DIGIT_9: u16 = 0x39; // 9 key -// pub const VK_KEY_A: u16 = 0x41; // A key -// pub const VK_KEY_B: u16 = 0x42; // B key -// pub const VK_KEY_C: u16 = 0x43; // C key -// pub const VK_KEY_D: u16 = 0x44; // D key -// pub const VK_KEY_E: u16 = 0x45; // E key -// pub const VK_KEY_F: u16 = 0x46; // F key -// pub const VK_KEY_G: u16 = 0x47; // G key -// pub const VK_KEY_H: u16 = 0x48; // H key -// pub const VK_KEY_I: u16 = 0x49; // I key -// pub const VK_KEY_J: u16 = 0x4A; // J key -// pub const VK_KEY_K: u16 = 0x4B; // K key -// pub const VK_KEY_L: u16 = 0x4C; // L key -// pub const VK_KEY_M: u16 = 0x4D; // M key -// pub const VK_KEY_N: u16 = 0x4E; // N key -// pub const VK_KEY_O: u16 = 0x4F; // O key -// pub const VK_KEY_P: u16 = 0x50; // P key -// pub const VK_KEY_Q: u16 = 0x51; // Q key -// pub const VK_KEY_R: u16 = 0x52; // R key -// pub const VK_KEY_S: u16 = 0x53; // S key -// pub const VK_KEY_T: u16 = 0x54; // T key -// pub const VK_KEY_U: u16 = 0x55; // U key -// pub const VK_KEY_V: u16 = 0x56; // V key -// pub const VK_KEY_W: u16 = 0x57; // W key -// pub const VK_KEY_X: u16 = 0x58; // X key -// pub const VK_KEY_Y: u16 = 0x59; // Y key -// pub const VK_KEY_Z: u16 = 0x5A; // Z key -// pub const VK_LWIN: u16 = 0x5B; // Left Windows key (Natural keyboard) -// pub const VK_RWIN: u16 = 0x5C; // Right Windows key (Natural keyboard) -// pub const VK_APPS: u16 = 0x5D; // Applications key (Natural keyboard) -// pub const VK_SLEEP: u16 = 0x5F; // Computer Sleep key -// pub const VK_NUMPAD0: u16 = 0x60; // Numeric keypad 0 key -// pub const VK_NUMPAD1: u16 = 0x61; // Numeric keypad 1 key -// pub const VK_NUMPAD2: u16 = 0x62; // Numeric keypad 2 key -// pub const VK_NUMPAD3: u16 = 0x63; // Numeric keypad 3 key -// pub const VK_NUMPAD4: u16 = 0x64; // Numeric keypad 4 key -// pub const VK_NUMPAD5: u16 = 0x65; // Numeric keypad 5 key -// pub const VK_NUMPAD6: u16 = 0x66; // Numeric keypad 6 key -// pub const VK_NUMPAD7: u16 = 0x67; // Numeric keypad 7 key -// pub const VK_NUMPAD8: u16 = 0x68; // Numeric keypad 8 key -// pub const VK_NUMPAD9: u16 = 0x69; // Numeric keypad 9 key -// pub const VK_MULTIPLY: u16 = 0x6A; // Multiply key -// pub const VK_ADD: u16 = 0x6B; // Add key -// pub const VK_SEPARATOR: u16 = 0x6C; // Separator key -// pub const VK_SUBTRACT: u16 = 0x6D; // Subtract key -// pub const VK_DECIMAL: u16 = 0x6E; // Decimal key -// pub const VK_DIVIDE: u16 = 0x6F; // Divide key -// pub const VK_F1: u16 = 0x70; // F1 key -// pub const VK_F2: u16 = 0x71; // F2 key -// pub const VK_F3: u16 = 0x72; // F3 key -// pub const VK_F4: u16 = 0x73; // F4 key -// pub const VK_F5: u16 = 0x74; // F5 key -// pub const VK_F6: u16 = 0x75; // F6 key -// pub const VK_F7: u16 = 0x76; // F7 key -// pub const VK_F8: u16 = 0x77; // F8 key -// pub const VK_F9: u16 = 0x78; // F9 key -// pub const VK_F10: u16 = 0x79; // F10 key -// pub const VK_F11: u16 = 0x7A; // F11 key -// pub const VK_F12: u16 = 0x7B; // F12 key -// pub const VK_F13: u16 = 0x7C; // F13 key -// pub const VK_F14: u16 = 0x7D; // F14 key -// pub const VK_F15: u16 = 0x7E; // F15 key -// pub const VK_F16: u16 = 0x7F; // F16 key -// pub const VK_F17: u16 = 0x80; // F17 key -// pub const VK_F18: u16 = 0x81; // F18 key -// pub const VK_F19: u16 = 0x82; // F19 key -// pub const VK_F20: u16 = 0x83; // F20 key -// pub const VK_F21: u16 = 0x84; // F21 key -// pub const VK_F22: u16 = 0x85; // F22 key -// pub const VK_F23: u16 = 0x86; // F23 key -// pub const VK_F24: u16 = 0x87; // F24 key -// pub const VK_NUMLOCK: u16 = 0x90; // NUM LOCK key -// pub const VK_SCROLL: u16 = 0x91; // SCROLL LOCK key -// pub const VK_LSHIFT: u16 = 0xA0; // Left SHIFT key -// pub const VK_RSHIFT: u16 = 0xA1; // Right SHIFT key -// pub const VK_LCONTROL: u16 = 0xA2; // Left CONTROL key -// pub const VK_RCONTROL: u16 = 0xA3; // Right CONTROL key -// pub const VK_LMENU: u16 = 0xA4; // Left MENU key -// pub const VK_RMENU: u16 = 0xA5; // Right MENU key -// pub const VK_BROWSER_BACK: u16 = 0xA6; // Browser Back key -// pub const VK_BROWSER_FORWARD: u16 = 0xA7; // Browser Forward key -// pub const VK_BROWSER_REFRESH: u16 = 0xA8; // Browser Refresh key -// pub const VK_BROWSER_STOP: u16 = 0xA9; // Browser Stop key -// pub const VK_BROWSER_SEARCH: u16 = 0xAA; // Browser Search key -// pub const VK_BROWSER_FAVORITES: u16 = 0xAB; // Browser Favorites key -// pub const VK_BROWSER_HOME: u16 = 0xAC; // Browser Start and Home key -// pub const VK_VOLUME_MUTE: u16 = 0xAD; // Volume Mute key -// pub const VK_VOLUME_DOWN: u16 = 0xAE; // Volume Down key -// pub const VK_VOLUME_UP: u16 = 0xAF; // Volume Up key -// pub const VK_MEDIA_NEXT_TRACK: u16 = 0xB0; // Next Track key -// pub const VK_MEDIA_PREV_TRACK: u16 = 0xB1; // Previous Track key -// pub const VK_MEDIA_STOP: u16 = 0xB2; // Stop Media key -// pub const VK_MEDIA_PLAY_PAUSE: u16 = 0xB3; // Play/Pause Media key -// pub const VK_LAUNCH_MAIL: u16 = 0xB4; // Start Mail key -// pub const VK_LAUNCH_MEDIA_SELECT: u16 = 0xB5; // Select Media key -// pub const VK_LAUNCH_APP1: u16 = 0xB6; // Start Application 1 key -// pub const VK_LAUNCH_APP2: u16 = 0xB7; // Start Application 2 key diff --git a/src/keyboard/windows/mod.rs b/src/keyboard/windows/mod.rs deleted file mode 100644 index 1003a60..0000000 --- a/src/keyboard/windows/mod.rs +++ /dev/null @@ -1,178 +0,0 @@ -use std::mem::{size_of, transmute_copy}; -use winapi::ctypes::c_int; -use winapi::shared::minwindef::WORD; -use winapi::um::winuser::{ - GetAsyncKeyState, MapVirtualKeyW, SendInput, INPUT, INPUT_KEYBOARD, KEYBDINPUT, KEYEVENTF_KEYUP, - KEYEVENTF_SCANCODE, LPINPUT, -}; - -use crate::keyboard::{Key, Keyboard}; - -impl Keyboard { - pub fn press(key: Key) { - let dw_flags = KEYEVENTF_SCANCODE; - - let mut input = INPUT { - type_: INPUT_KEYBOARD, - u: unsafe { - transmute_copy(&KEYBDINPUT { - wVk: 0, - wScan: Self::key_to_scancode(key), - dwFlags: dw_flags, - time: 0, - dwExtraInfo: 0, - }) - }, - }; - unsafe { SendInput(1, &mut input as LPINPUT, size_of::() as c_int) }; - } - - pub fn release(key: Key) { - let dw_flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; - - let mut input = INPUT { - type_: INPUT_KEYBOARD, - u: unsafe { - transmute_copy(&KEYBDINPUT { - wVk: 0, - wScan: Self::key_to_scancode(key), - dwFlags: dw_flags, - time: 0, - dwExtraInfo: 0, - }) - }, - }; - unsafe { SendInput(1, &mut input as LPINPUT, size_of::() as c_int) }; - } - - pub fn pressed(key: Key) -> bool { - (unsafe { GetAsyncKeyState(Self::key_2_virtual_key(key) as i32) } >> 15) != 0 - } - - fn key_to_scancode(key: Key) -> u16 { - let vk = Self::key_2_virtual_key(key); - unsafe { MapVirtualKeyW(vk as u32, 0) as u16 } - } - - fn key_2_virtual_key(key: Key) -> WORD { - match key { - Key::Digit0 => 0x30, - Key::Digit1 => 0x31, - Key::Digit2 => 0x32, - Key::Digit3 => 0x33, - Key::Digit4 => 0x34, - Key::Digit5 => 0x35, - Key::Digit6 => 0x36, - Key::Digit7 => 0x37, - Key::Digit8 => 0x38, - Key::Digit9 => 0x39, - Key::KeyA => 0x41, - Key::KeyB => 0x42, - Key::KeyC => 0x43, - Key::KeyD => 0x44, - Key::KeyE => 0x45, - Key::KeyF => 0x46, - Key::KeyG => 0x47, - Key::KeyH => 0x48, - Key::KeyI => 0x49, - Key::KeyJ => 0x4A, - Key::KeyK => 0x4B, - Key::KeyL => 0x4C, - Key::KeyM => 0x4D, - Key::KeyN => 0x4E, - Key::KeyO => 0x4F, - Key::KeyP => 0x50, - Key::KeyQ => 0x51, - Key::KeyR => 0x52, - Key::KeyS => 0x53, - Key::KeyT => 0x54, - Key::KeyU => 0x55, - Key::KeyV => 0x56, - Key::KeyW => 0x57, - Key::KeyX => 0x58, - Key::KeyY => 0x59, - Key::KeyZ => 0x5A, - Key::F1 => 0x70, - Key::F2 => 0x71, - Key::F3 => 0x72, - Key::F4 => 0x73, - Key::F5 => 0x74, - Key::F6 => 0x75, - Key::F7 => 0x76, - Key::F8 => 0x77, - Key::F9 => 0x78, - Key::F10 => 0x79, - Key::F11 => 0x7A, - Key::F12 => 0x7B, - Key::F13 => 0x7C, - Key::F14 => 0x7D, - Key::F15 => 0x7E, - Key::F16 => 0x7F, - Key::F17 => 0x80, - Key::F18 => 0x81, - Key::F19 => 0x82, - Key::F20 => 0x83, - Key::F21 => 0x84, - Key::F22 => 0x85, - Key::F23 => 0x86, - Key::F24 => 0x87, - Key::Numpad0 => 0x60, - Key::Numpad1 => 0x61, - Key::Numpad2 => 0x62, - Key::Numpad3 => 0x63, - Key::Numpad4 => 0x64, - Key::Numpad5 => 0x65, - Key::Numpad6 => 0x66, - Key::Numpad7 => 0x67, - Key::Numpad8 => 0x68, - Key::Numpad9 => 0x69, - Key::NumpadAdd => 0x6B, // Add - Key::NumpadSubtract => 0x6D, // Subtract - Key::NumpadMultiply => 0x6A, // Multiply - Key::NumpadDivide => 0x6F, // Divide - Key::NumpadDecimal => 0x6E, // Decimal - Key::NumpadEqual => '=' as u16, - Key::NumpadComma => ',' as u16, - Key::NumpadEnter => 0x0D, - Key::NumLock => 0x90, - Key::Minus => '-' as u16, - Key::Equal => '=' as u16, - Key::BracketLeft => '{' as u16, - Key::BracketRight => '}' as u16, - Key::Semicolon => ';' as u16, - Key::Quote => '\'' as u16, - Key::Backquote => '`' as u16, - Key::Backslash => '\\' as u16, - Key::Comma => ',' as u16, - Key::Period => '~' as u16, - Key::Slash => '/' as u16, - Key::Escape => 0x1B, - Key::Enter => 0x0D, - Key::Backspace => 0x08, - Key::Tab => 0x09, - Key::Space => 0x20, - Key::CapsLock => 0x14, - Key::ShiftLeft => 0xA0, - Key::ShiftRight => 0xA1, - Key::ControlLeft => 0xA2, - Key::ControlRight => 0xA3, - Key::AltLeft => 0xA4, - Key::AltRight => 0xA5, - Key::Pause => 0x13, - Key::ScrollLock => 0x91, - Key::PrintScreen => 0x2C, - Key::ArrowLeft => 0x25, - Key::ArrowUp => 0x26, - Key::ArrowRight => 0x27, - Key::ArrowDown => 0x28, - Key::PageUp => 0x21, - Key::PageDown => 0x22, - Key::Home => 0x24, - Key::End => 0x23, - Key::Insert => 0x2D, - Key::Delete => 0x2E, - Key::MetaLeft => 0x5B, - Key::MetaRight => 0x5C, - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 73cdf76..0fc176a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,32 @@ +#[cfg(target_os = "windows")] +mod windows; +#[cfg(target_os = "windows")] +pub use crate::windows::*; + +use crate::keyboard::Key; +use crate::mouse::Button; +use std::time::SystemTime; + pub mod keyboard; pub mod mouse; + +#[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +pub enum EventType { + KeyPress(Key), + KeyRelease(Key), + MousePress(Button), + MouseRelease(Button), + MouseMoveTo { x: f64, y: f64 }, +} + +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +pub struct Event { + pub time: SystemTime, + pub event_type: EventType, +} + +pub type HookCallback = fn(event: Event); + +pub struct Robot; diff --git a/src/mouse.rs b/src/mouse.rs new file mode 100644 index 0000000..eb3ff8a --- /dev/null +++ b/src/mouse.rs @@ -0,0 +1,10 @@ +#[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +pub enum Button { + Left, + Middle, + Right, + X1, + X2, + Other(u32), +} diff --git a/src/mouse/mod.rs b/src/mouse/mod.rs deleted file mode 100644 index abe1fa9..0000000 --- a/src/mouse/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -#[cfg(target_os = "windows")] -mod windows; -#[cfg(target_os = "windows")] -pub use crate::mouse::windows::*; - -pub enum Button { - Left, - Middle, - Right, - X1, - X2, - Other(u32), -} - -pub enum ButtonState { - Press, - Release, -} - -pub struct Mouse; diff --git a/src/mouse/windows/mod.rs b/src/mouse/windows/mod.rs deleted file mode 100644 index 6736ae9..0000000 --- a/src/mouse/windows/mod.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::mem::{size_of, transmute}; -use winapi::ctypes::c_int; -use winapi::shared::minwindef::DWORD; -use winapi::um::winuser::{ - GetAsyncKeyState, GetSystemMetrics, SendInput, INPUT, INPUT_MOUSE, LPINPUT, MOUSEEVENTF_ABSOLUTE, - MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP, - MOUSEEVENTF_MOVE, MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, MOUSEEVENTF_VIRTUALDESK, - MOUSEINPUT, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, - VK_LBUTTON, VK_MBUTTON, VK_RBUTTON, VK_XBUTTON1, VK_XBUTTON2, -}; - -use crate::mouse::{Button, ButtonState, Mouse}; - -impl Mouse { - pub fn move_to(dx: i32, dy: i32) { - let width = unsafe { GetSystemMetrics(SM_CXVIRTUALSCREEN) }; - let height = unsafe { GetSystemMetrics(SM_CYVIRTUALSCREEN) }; - if width == 0 || height == 0 { - return; - } - let pressed_left = Self::pressed(Button::Left); - let pressed_right = Self::pressed(Button::Right); - let pressed_middle = Self::pressed(Button::Middle); - let mut dw_flags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK; - - if pressed_left { - dw_flags = dw_flags | MOUSEEVENTF_LEFTDOWN; - } - if pressed_right { - dw_flags = dw_flags | MOUSEEVENTF_RIGHTDOWN; - } - if pressed_middle { - dw_flags = dw_flags | MOUSEEVENTF_MIDDLEDOWN; - } - - let mut input = INPUT { - type_: INPUT_MOUSE, - u: unsafe { - transmute(MOUSEINPUT { - dx: (dx - { GetSystemMetrics(SM_XVIRTUALSCREEN) }) * 65535 / { - GetSystemMetrics(SM_CXVIRTUALSCREEN) - }, - dy: (dy - { GetSystemMetrics(SM_YVIRTUALSCREEN) }) * 65535 / { - GetSystemMetrics(SM_CYVIRTUALSCREEN) - }, - mouseData: 0, - dwFlags: dw_flags, - time: 0, - dwExtraInfo: 0, - }) - }, - }; - unsafe { SendInput(1, &mut input as LPINPUT, size_of::() as c_int) }; - } - - pub fn press(button: Button) { - let btn = Self::button_event(button, ButtonState::Press); - - let dw_flags = btn; - - let mut input = INPUT { - type_: INPUT_MOUSE, - u: unsafe { - transmute(MOUSEINPUT { - dx: 0, - dy: 0, - mouseData: 0, - dwFlags: dw_flags, - time: 0, - dwExtraInfo: 0, - }) - }, - }; - unsafe { SendInput(1, &mut input as LPINPUT, size_of::() as c_int) }; - } - - pub fn release(button: Button) { - let btn = Self::button_event(button, ButtonState::Release); - - let dw_flags = btn; - - let mut input = INPUT { - type_: INPUT_MOUSE, - u: unsafe { - transmute(MOUSEINPUT { - dx: 0, - dy: 0, - mouseData: 0, - dwFlags: dw_flags, - time: 0, - dwExtraInfo: 0, - }) - }, - }; - unsafe { SendInput(1, &mut input as LPINPUT, size_of::() as c_int) }; - } - - pub fn pressed(button: Button) -> bool { - (unsafe { GetAsyncKeyState(Self::button_virtual_key(button)) } >> 15) != 0 - } - - fn button_event(button: Button, state: ButtonState) -> DWORD { - match state { - ButtonState::Press => match button { - Button::Left => MOUSEEVENTF_LEFTDOWN, - Button::Middle => MOUSEEVENTF_MIDDLEDOWN, - Button::Right => MOUSEEVENTF_RIGHTDOWN, - _ => unimplemented!(), - }, - ButtonState::Release => match button { - Button::Left => MOUSEEVENTF_LEFTUP, - Button::Middle => MOUSEEVENTF_MIDDLEUP, - Button::Right => MOUSEEVENTF_RIGHTUP, - _ => unimplemented!(), - }, - } - } - - fn button_virtual_key(button: Button) -> c_int { - match button { - Button::Left => VK_LBUTTON, - Button::Middle => VK_MBUTTON, - Button::Right => VK_RBUTTON, - Button::X1 => VK_XBUTTON1, - Button::X2 => VK_XBUTTON2, - _ => unimplemented!(), - } - } -} diff --git a/src/test.rs b/src/test.rs deleted file mode 100644 index 8915d42..0000000 --- a/src/test.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::mouse::Mouse; - -#[cfg(test)] -mod tests { - #[test] - fn mouse_moto_to() { - Mouse::move_to(true, 500, 30); - } -} diff --git a/src/windows/keyboard.rs b/src/windows/keyboard.rs new file mode 100644 index 0000000..09ce404 --- /dev/null +++ b/src/windows/keyboard.rs @@ -0,0 +1,405 @@ +use crate::windows; +use crate::{Event, EventType, HookCallback, Key, Robot}; +use lazy_static::lazy_static; +use std::collections::HashMap; +use std::convert::TryInto; +use std::mem::{size_of, transmute_copy}; +use std::ptr::null_mut; +use std::time::SystemTime; +use winapi::ctypes::c_int; +use winapi::shared::minwindef::{LPARAM, LRESULT, WORD, WPARAM}; +use winapi::shared::windef::HHOOK; +use winapi::um::winuser::{ + CallNextHookEx, GetAsyncKeyState, MapVirtualKeyW, SendInput, HC_ACTION, INPUT, INPUT_KEYBOARD, + KBDLLHOOKSTRUCT, KEYBDINPUT, KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE, LPINPUT, WH_KEYBOARD_LL, + WM_KEYDOWN, WM_KEYUP, +}; + +lazy_static! { + static ref KEY_2_VK_MAP: HashMap = { + let mut m = HashMap::new(); + m.insert(Key::Digit0 , 0x30); + m.insert(Key::Digit1 , 0x31); + m.insert(Key::Digit2 , 0x32); + m.insert(Key::Digit3 , 0x33); + m.insert(Key::Digit4 , 0x34); + m.insert(Key::Digit5 , 0x35); + m.insert(Key::Digit6 , 0x36); + m.insert(Key::Digit7 , 0x37); + m.insert(Key::Digit8 , 0x38); + m.insert(Key::Digit9 , 0x39); + m.insert(Key::KeyA , 0x41); + m.insert(Key::KeyB , 0x42); + m.insert(Key::KeyC , 0x43); + m.insert(Key::KeyD , 0x44); + m.insert(Key::KeyE , 0x45); + m.insert(Key::KeyF , 0x46); + m.insert(Key::KeyG , 0x47); + m.insert(Key::KeyH , 0x48); + m.insert(Key::KeyI , 0x49); + m.insert(Key::KeyJ , 0x4A); + m.insert(Key::KeyK , 0x4B); + m.insert(Key::KeyL , 0x4C); + m.insert(Key::KeyM , 0x4D); + m.insert(Key::KeyN , 0x4E); + m.insert(Key::KeyO , 0x4F); + m.insert(Key::KeyP , 0x50); + m.insert(Key::KeyQ , 0x51); + m.insert(Key::KeyR , 0x52); + m.insert(Key::KeyS , 0x53); + m.insert(Key::KeyT , 0x54); + m.insert(Key::KeyU , 0x55); + m.insert(Key::KeyV , 0x56); + m.insert(Key::KeyW , 0x57); + m.insert(Key::KeyX , 0x58); + m.insert(Key::KeyY , 0x59); + m.insert(Key::KeyZ , 0x5A); + m.insert(Key::F1 , 0x70); + m.insert(Key::F2 , 0x71); + m.insert(Key::F3 , 0x72); + m.insert(Key::F4 , 0x73); + m.insert(Key::F5 , 0x74); + m.insert(Key::F6 , 0x75); + m.insert(Key::F7 , 0x76); + m.insert(Key::F8 , 0x77); + m.insert(Key::F9 , 0x78); + m.insert(Key::F10 , 0x79); + m.insert(Key::F11 , 0x7A); + m.insert(Key::F12 , 0x7B); + m.insert(Key::F13 , 0x7C); + m.insert(Key::F14 , 0x7D); + m.insert(Key::F15 , 0x7E); + m.insert(Key::F16 , 0x7F); + m.insert(Key::F17 , 0x80); + m.insert(Key::F18 , 0x81); + m.insert(Key::F19 , 0x82); + m.insert(Key::F20 , 0x83); + m.insert(Key::F21 , 0x84); + m.insert(Key::F22 , 0x85); + m.insert(Key::F23 , 0x86); + m.insert(Key::F24 , 0x87); + m.insert(Key::Numpad0 , 0x60); + m.insert(Key::Numpad1 , 0x61); + m.insert(Key::Numpad2 , 0x62); + m.insert(Key::Numpad3 , 0x63); + m.insert(Key::Numpad4 , 0x64); + m.insert(Key::Numpad5 , 0x65); + m.insert(Key::Numpad6 , 0x66); + m.insert(Key::Numpad7 , 0x67); + m.insert(Key::Numpad8 , 0x68); + m.insert(Key::Numpad9 , 0x69); + m.insert(Key::NumpadAdd , 0x6B); // Add + m.insert(Key::NumpadSubtract , 0x6D); // Subtract + m.insert(Key::NumpadMultiply , 0x6A); // Multiply + m.insert(Key::NumpadDivide , 0x6F); // Divide + m.insert(Key::NumpadDecimal , 0x6E); // Decimal + m.insert(Key::NumpadEqual , '=' as u16); + m.insert(Key::NumpadComma , ',' as u16); + m.insert(Key::NumpadEnter , 0x0D); + m.insert(Key::NumLock , 0x90); + m.insert(Key::Minus , '-' as u16); + m.insert(Key::Equal , '=' as u16); + m.insert(Key::BracketLeft , '{' as u16); + m.insert(Key::BracketRight , '}' as u16); + m.insert(Key::Semicolon , ';' as u16); + m.insert(Key::Quote , '\'' as u16); + m.insert(Key::Backquote , '`' as u16); + m.insert(Key::Backslash , '\\' as u16); + m.insert(Key::Comma , ',' as u16); + m.insert(Key::Period , '~' as u16); + m.insert(Key::Slash , '/' as u16); + m.insert(Key::Escape , 0x1B); + m.insert(Key::Enter , 0x0D); + m.insert(Key::Backspace , 0x08); + m.insert(Key::Tab , 0x09); + m.insert(Key::Space , 0x20); + m.insert(Key::CapsLock , 0x14); + m.insert(Key::ShiftLeft , 0xA0); + m.insert(Key::ShiftRight , 0xA1); + m.insert(Key::ControlLeft , 0xA2); + m.insert(Key::ControlRight , 0xA3); + m.insert(Key::AltLeft , 0xA4); + m.insert(Key::AltRight , 0xA5); + m.insert(Key::Pause , 0x13); + m.insert(Key::ScrollLock , 0x91); + m.insert(Key::PrintScreen , 0x2C); + m.insert(Key::ArrowLeft , 0x25); + m.insert(Key::ArrowUp , 0x26); + m.insert(Key::ArrowRight , 0x27); + m.insert(Key::ArrowDown , 0x28); + m.insert(Key::PageUp , 0x21); + m.insert(Key::PageDown , 0x22); + m.insert(Key::Home , 0x24); + m.insert(Key::End , 0x23); + m.insert(Key::Insert , 0x2D); + m.insert(Key::Delete , 0x2E); + m.insert(Key::MetaLeft , 0x5B); + m.insert(Key::MetaRight , 0x5C); + m + }; + + static ref VK_2_KEY_MAP: HashMap = { + let mut m = HashMap::new(); + m.insert(0x30, Key::Digit0); + m.insert(0x31, Key::Digit1); + m.insert(0x32, Key::Digit2); + m.insert(0x33, Key::Digit3); + m.insert(0x34, Key::Digit4); + m.insert(0x35, Key::Digit5); + m.insert(0x36, Key::Digit6); + m.insert(0x37, Key::Digit7); + m.insert(0x38, Key::Digit8); + m.insert(0x39, Key::Digit9); + m.insert(0x41, Key::KeyA); + m.insert(0x42, Key::KeyB); + m.insert(0x43, Key::KeyC); + m.insert(0x44, Key::KeyD); + m.insert(0x45, Key::KeyE); + m.insert(0x46, Key::KeyF); + m.insert(0x47, Key::KeyG); + m.insert(0x48, Key::KeyH); + m.insert(0x49, Key::KeyI); + m.insert(0x4A, Key::KeyJ); + m.insert(0x4B, Key::KeyK); + m.insert(0x4C, Key::KeyL); + m.insert(0x4D, Key::KeyM); + m.insert(0x4E, Key::KeyN); + m.insert(0x4F, Key::KeyO); + m.insert(0x50, Key::KeyP); + m.insert(0x51, Key::KeyQ); + m.insert(0x52, Key::KeyR); + m.insert(0x53, Key::KeyS); + m.insert(0x54, Key::KeyT); + m.insert(0x55, Key::KeyU); + m.insert(0x56, Key::KeyV); + m.insert(0x57, Key::KeyW); + m.insert(0x58, Key::KeyX); + m.insert(0x59, Key::KeyY); + m.insert(0x5A, Key::KeyZ); + m.insert(0x70, Key::F1); + m.insert(0x71, Key::F2); + m.insert(0x72, Key::F3); + m.insert(0x73, Key::F4); + m.insert(0x74, Key::F5); + m.insert(0x75, Key::F6); + m.insert(0x76, Key::F7); + m.insert(0x77, Key::F8); + m.insert(0x78, Key::F9); + m.insert(0x79, Key::F10); + m.insert(0x7A, Key::F11); + m.insert(0x7B, Key::F12); + m.insert(0x7C, Key::F13); + m.insert(0x7D, Key::F14); + m.insert(0x7E, Key::F15); + m.insert(0x7F, Key::F16); + m.insert(0x80, Key::F17); + m.insert(0x81, Key::F18); + m.insert(0x82, Key::F19); + m.insert(0x83, Key::F20); + m.insert(0x84, Key::F21); + m.insert(0x85, Key::F22); + m.insert(0x86, Key::F23); + m.insert(0x87, Key::F24); + m.insert(0x60, Key::Numpad0); + m.insert(0x61, Key::Numpad1); + m.insert(0x62, Key::Numpad2); + m.insert(0x63, Key::Numpad3); + m.insert(0x64, Key::Numpad4); + m.insert(0x65, Key::Numpad5); + m.insert(0x66, Key::Numpad6); + m.insert(0x67, Key::Numpad7); + m.insert(0x68, Key::Numpad8); + m.insert(0x69, Key::Numpad9); + m.insert(0x6B, Key::NumpadAdd); // Add + m.insert(0x6D, Key::NumpadSubtract); // Subtract + m.insert(0x6A, Key::NumpadMultiply); // Multiply + m.insert(0x6F, Key::NumpadDivide); // Divide + m.insert(0x6E, Key::NumpadDecimal); // Decimal + m.insert( 0x0D, Key::NumpadEnter); + m.insert( 0x90, Key::NumLock); + m.insert('-' as u16, Key::Minus); + m.insert('=' as u16, Key::Equal); + m.insert('{' as u16, Key::BracketLeft); + m.insert('}' as u16, Key::BracketRight); + m.insert(';' as u16, Key::Semicolon); + m.insert('\'' as u16, Key::Quote); + m.insert('`' as u16, Key::Backquote); + m.insert('\\' as u16, Key::Backslash); + m.insert(',' as u16, Key::Comma); + m.insert('~' as u16, Key::Period); + m.insert('/' as u16, Key::Slash); + m.insert(0x1B, Key::Escape); + m.insert( 0x0D, Key::Enter); + m.insert( 0x08, Key::Backspace); + m.insert( 0x09, Key::Tab); + m.insert( 0x20, Key::Space); + m.insert(0x14, Key::CapsLock); + m.insert(0xA0, Key::ShiftLeft); + m.insert(0xA1, Key::ShiftRight); + m.insert( 0xA2, Key::ControlLeft); + m.insert( 0xA3, Key::ControlRight); + m.insert( 0xA4, Key::AltLeft); + m.insert(0xA5, Key::AltRight); + m.insert(0x13, Key::Pause); + m.insert(0x91, Key::ScrollLock); + m.insert(0x2C, Key::PrintScreen); + m.insert(0x25, Key::ArrowLeft); + m.insert(0x26, Key::ArrowUp); + m.insert(0x27, Key::ArrowRight); + m.insert(0x28, Key::ArrowDown); + m.insert(0x21, Key::PageUp); + m.insert(0x22, Key::PageDown); + m.insert( 0x24, Key::Home); + m.insert( 0x23, Key::End); + m.insert(0x2D, Key::Insert); + m.insert(0x2E, Key::Delete); + m.insert(0x5B, Key::MetaLeft); + m.insert(0x5C, Key::MetaRight); + m + }; +} + +fn default_callback(event: Event) { + println!("Default : Event {:?}", event); +} + +static mut HOOK: HHOOK = null_mut(); +static mut HOOK_CALLBACK: HookCallback = default_callback; + +impl Robot { + pub unsafe fn key_listen(callback: HookCallback) -> Result<(), windows::HookError> { + if HOOK.is_null() { + let r = windows::hook(WH_KEYBOARD_LL, Self::key_raw_callback); + + if r.is_err() { + return Err(windows::HookError::Keyboard(r.err().unwrap())); + } + HOOK = r.ok().unwrap(); + } + + HOOK_CALLBACK = callback; + + Ok(()) + } + + pub unsafe fn key_unlisten() -> Result<(), windows::HookError> { + if !HOOK.is_null() { + let r = windows::unhook(HOOK); + if r.is_err() { + return Err(windows::HookError::Keyboard(r.err().unwrap())); + } + HOOK = null_mut(); + } + + HOOK_CALLBACK = default_callback; + + Ok(()) + } + + pub fn key_press(key: Key) { + let dw_flags = KEYEVENTF_SCANCODE; + + let mut input = INPUT { + type_: INPUT_KEYBOARD, + u: unsafe { + transmute_copy(&KEYBDINPUT { + wVk: 0, + wScan: Self::key_to_scancode(key), + dwFlags: dw_flags, + time: 0, + dwExtraInfo: 0, + }) + }, + }; + unsafe { SendInput(1, &mut input as LPINPUT, size_of::() as c_int) }; + } + + pub fn key_release(key: Key) { + let dw_flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; + + let mut input = INPUT { + type_: INPUT_KEYBOARD, + u: unsafe { + transmute_copy(&KEYBDINPUT { + wVk: 0, + wScan: Self::key_to_scancode(key), + dwFlags: dw_flags, + time: 0, + dwExtraInfo: 0, + }) + }, + }; + unsafe { SendInput(1, &mut input as LPINPUT, size_of::() as c_int) }; + } + + pub fn key_pressed(key: Key) -> bool { + match Self::key_2_virtual_key(key) { + Some(vk) => (unsafe { GetAsyncKeyState(vk as i32) } >> 15) != 0, + None => false, + } + } + + fn key_to_scancode(key: Key) -> u16 { + match Self::key_2_virtual_key(key) { + Some(vk) => unsafe { MapVirtualKeyW(vk as u32, 0) as u16 }, + None => 0, + } + } + + unsafe fn get_virtual_key(lpdata: isize) -> u32 { + let kb = *(lpdata as *const KBDLLHOOKSTRUCT); + kb.vkCode + } + + unsafe extern "system" fn key_raw_callback( + code: c_int, + param: WPARAM, + lpdata: LPARAM, + ) -> LRESULT { + if code == HC_ACTION { + let opt = Self::key_convert(param, lpdata); + if let Some(event_type) = opt { + let event = Event { + event_type, + time: SystemTime::now(), + }; + HOOK_CALLBACK(event); + } + } + CallNextHookEx(HOOK, code, param, lpdata) + } + + unsafe fn key_convert(wparam: WPARAM, lparam: LPARAM) -> Option { + match wparam.try_into() { + Ok(WM_KEYDOWN) => { + let code = Self::get_virtual_key(lparam); + match Self::virtual_key_2_key(code as u16) { + Some(k) => Some(EventType::KeyPress(k)), + None => None, + } + } + Ok(WM_KEYUP) => { + let code = Self::get_virtual_key(lparam); + match Self::virtual_key_2_key(code as u16) { + Some(k) => Some(EventType::KeyRelease(k)), + None => None, + } + } + _ => None, + } + } + + fn key_2_virtual_key(key: Key) -> Option { + match KEY_2_VK_MAP.get(&key) { + Some(v) => Some(*v), + None => None, + } + } + + fn virtual_key_2_key(virtual_key: WORD) -> Option { + match VK_2_KEY_MAP.get(&virtual_key) { + Some(v) => Some(*v), + None => None, + } + } +} diff --git a/src/windows/mod.rs b/src/windows/mod.rs new file mode 100644 index 0000000..32a378b --- /dev/null +++ b/src/windows/mod.rs @@ -0,0 +1,37 @@ +use std::ptr::null_mut; +use winapi::ctypes::c_int; +use winapi::shared::minwindef::{DWORD, LPARAM, LRESULT, WPARAM}; +use winapi::shared::windef::HHOOK; +use winapi::um::errhandlingapi::GetLastError; +use winapi::um::winuser::{SetWindowsHookExA, UnhookWindowsHookEx}; + +pub mod keyboard; +pub mod mouse; + +pub type HookCallback = + unsafe extern "system" fn(code: c_int, param: WPARAM, lpdata: LPARAM) -> LRESULT; + +pub enum HookError { + Mouse(DWORD), + Keyboard(DWORD), +} + +pub unsafe fn hook(id_hook: c_int, callback: HookCallback) -> Result { + let hook = SetWindowsHookExA(id_hook, Some(callback), null_mut(), 0); + + if hook.is_null() { + let error = GetLastError(); + return Err(error); + } + + Ok(hook) +} + +pub unsafe fn unhook(hook: HHOOK) -> Result<(), DWORD> { + if hook.is_null() { + return Ok(()); + } + + UnhookWindowsHookEx(hook); + Ok(()) +} diff --git a/src/windows/mouse.rs b/src/windows/mouse.rs new file mode 100644 index 0000000..dc9b4b6 --- /dev/null +++ b/src/windows/mouse.rs @@ -0,0 +1,213 @@ +use crate::windows; +use crate::{Button, Event, EventType, HookCallback, Robot}; +use std::convert::TryInto; +use std::mem::{size_of, transmute}; +use std::ptr::null_mut; +use std::time::SystemTime; +use winapi::ctypes::c_int; +use winapi::shared::minwindef::{DWORD, LPARAM, LRESULT, WPARAM}; +use winapi::shared::windef::HHOOK; +use winapi::um::winuser::{ + CallNextHookEx, GetAsyncKeyState, GetSystemMetrics, SendInput, HC_ACTION, INPUT, INPUT_MOUSE, + LPINPUT, MOUSEEVENTF_ABSOLUTE, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEDOWN, + MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_MOVE, MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, + MOUSEEVENTF_VIRTUALDESK, MOUSEINPUT, MSLLHOOKSTRUCT, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, + SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, VK_LBUTTON, VK_MBUTTON, VK_RBUTTON, VK_XBUTTON1, + VK_XBUTTON2, WH_MOUSE_LL, WM_LBUTTONDOWN, +}; + +fn default_callback(event: Event) { + println!("Default : Event {:?}", event); +} + +static mut HOOK: HHOOK = null_mut(); +static mut HOOK_CALLBACK: HookCallback = default_callback; + +impl Robot { + pub unsafe fn mouse_listen(callback: HookCallback) -> Result<(), windows::HookError> { + if HOOK.is_null() { + let r = windows::hook(WH_MOUSE_LL, Self::mouse_raw_callback); + + if r.is_err() { + return Err(windows::HookError::Mouse(r.err().unwrap())); + } + HOOK = r.ok().unwrap(); + } + + HOOK_CALLBACK = callback; + + Ok(()) + } + + pub unsafe fn mouse_unlisten() -> Result<(), windows::HookError> { + if !HOOK.is_null() { + let r = windows::unhook(HOOK); + if r.is_err() { + return Err(windows::HookError::Mouse(r.err().unwrap())); + } + HOOK = null_mut(); + } + + HOOK_CALLBACK = default_callback; + + Ok(()) + } + + pub fn mouse_move_to(dx: i32, dy: i32) { + let width = unsafe { GetSystemMetrics(SM_CXVIRTUALSCREEN) }; + let height = unsafe { GetSystemMetrics(SM_CYVIRTUALSCREEN) }; + if width == 0 || height == 0 { + return; + } + let pressed_left = Self::mouse_pressed(Button::Left); + let pressed_right = Self::mouse_pressed(Button::Right); + let pressed_middle = Self::mouse_pressed(Button::Middle); + let mut dw_flags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK; + + if pressed_left { + dw_flags = dw_flags | MOUSEEVENTF_LEFTDOWN; + } + if pressed_right { + dw_flags = dw_flags | MOUSEEVENTF_RIGHTDOWN; + } + if pressed_middle { + dw_flags = dw_flags | MOUSEEVENTF_MIDDLEDOWN; + } + + let mut input = INPUT { + type_: INPUT_MOUSE, + u: unsafe { + transmute(MOUSEINPUT { + dx: (dx - { GetSystemMetrics(SM_XVIRTUALSCREEN) }) * 65535 / { + GetSystemMetrics(SM_CXVIRTUALSCREEN) + }, + dy: (dy - { GetSystemMetrics(SM_YVIRTUALSCREEN) }) * 65535 / { + GetSystemMetrics(SM_CYVIRTUALSCREEN) + }, + mouseData: 0, + dwFlags: dw_flags, + time: 0, + dwExtraInfo: 0, + }) + }, + }; + unsafe { SendInput(1, &mut input as LPINPUT, size_of::() as c_int) }; + } + + pub fn mouse_press(button: Button) { + let btn = Self::mouse_button_event(EventType::MousePress(button)); + + let dw_flags = btn; + + let mut input = INPUT { + type_: INPUT_MOUSE, + u: unsafe { + transmute(MOUSEINPUT { + dx: 0, + dy: 0, + mouseData: 0, + dwFlags: dw_flags, + time: 0, + dwExtraInfo: 0, + }) + }, + }; + unsafe { SendInput(1, &mut input as LPINPUT, size_of::() as c_int) }; + } + + pub fn mouse_release(button: Button) { + let btn = Self::mouse_button_event(EventType::MouseRelease(button)); + + let dw_flags = btn; + + let mut input = INPUT { + type_: INPUT_MOUSE, + u: unsafe { + transmute(MOUSEINPUT { + dx: 0, + dy: 0, + mouseData: 0, + dwFlags: dw_flags, + time: 0, + dwExtraInfo: 0, + }) + }, + }; + unsafe { SendInput(1, &mut input as LPINPUT, size_of::() as c_int) }; + } + + pub fn mouse_pressed(button: Button) -> bool { + (unsafe { GetAsyncKeyState(Self::mouse_button_virtual_key(button)) } >> 15) != 0 + } + + fn mouse_button_event(ev: EventType) -> DWORD { + match ev { + EventType::MousePress(button) => match button { + Button::Left => MOUSEEVENTF_LEFTDOWN, + Button::Middle => MOUSEEVENTF_MIDDLEDOWN, + Button::Right => MOUSEEVENTF_RIGHTDOWN, + _ => unimplemented!(), + }, + EventType::MouseRelease(button) => match button { + Button::Left => MOUSEEVENTF_LEFTUP, + Button::Middle => MOUSEEVENTF_MIDDLEUP, + Button::Right => MOUSEEVENTF_RIGHTUP, + _ => unimplemented!(), + }, + _ => unimplemented!(), + } + } + + fn mouse_button_virtual_key(button: Button) -> c_int { + match button { + Button::Left => VK_LBUTTON, + Button::Middle => VK_MBUTTON, + Button::Right => VK_RBUTTON, + Button::X1 => VK_XBUTTON1, + Button::X2 => VK_XBUTTON2, + _ => unimplemented!(), + } + } + + unsafe fn get_mouse_point(lpdata: isize) -> (i32, i32) { + let mouse = *(lpdata as *const MSLLHOOKSTRUCT); + (mouse.pt.x, mouse.pt.y) + } + + unsafe extern "system" fn mouse_raw_callback( + code: c_int, + param: WPARAM, + lpdata: LPARAM, + ) -> LRESULT { + if code == HC_ACTION { + let opt = Self::mouse_convert(param, lpdata); + if let Some(event_type) = opt { + let event = Event { + event_type, + time: SystemTime::now(), + }; + HOOK_CALLBACK(event); + } + } + CallNextHookEx(HOOK, code, param, lpdata) + } + + unsafe fn mouse_convert(wparam: WPARAM, lparam: LPARAM) -> Option { + match wparam.try_into() { + Ok(WM_LBUTTONDOWN) => Some(EventType::MousePress(Button::Left)), + Ok(WM_LBUTTONUP) => Some(EventType::MouseRelease(Button::Left)), + Ok(WM_MBUTTONDOWN) => Some(EventType::MousePress(Button::Middle)), + Ok(WM_MBUTTONUP) => Some(EventType::MouseRelease(Button::Middle)), + Ok(WM_RBUTTONDOWN) => Some(EventType::MousePress(Button::Right)), + Ok(WM_RBUTTONUP) => Some(EventType::MouseRelease(Button::Right)), + Ok(WM_MOUSEMOVE) => { + let (x, y) = Self::get_mouse_point(lparam); + Some(EventType::MouseMoveTo { + x: x as f64, + y: y as f64, + }) + } + _ => None, + } + } +}