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,
+ }
+ }
+}