Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@ epoll = {version = "4.1.0", optional=true}
inotify = {version = "0.8.2", default-features=false, optional=true}

[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["winuser", "errhandlingapi", "processthreadsapi"] }
windows-sys = { version = "0.45.0", features = [
"Win32_UI_WindowsAndMessaging",
"Win32_Foundation",
"Win32_System_Threading",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_TextServices"
] }

[dev-dependencies]
serde_json = "1.0"
Expand Down
48 changes: 30 additions & 18 deletions src/windows/common.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
use crate::rdev::{Button, EventType};
use crate::windows::keyboard::Keyboard;
use crate::windows::keycodes::key_from_code;
use crate::windows::{DWORD, LONG, WORD};
use lazy_static::lazy_static;
use std::convert::TryInto;
use std::os::raw::{c_int, c_short};
use std::ptr::null_mut;
use std::os::raw::c_int;
use std::sync::Mutex;
use winapi::shared::minwindef::{DWORD, HIWORD, LPARAM, LRESULT, WORD, WPARAM};
use winapi::shared::ntdef::LONG;
use winapi::shared::windef::HHOOK;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::winuser::{
use windows_sys::Win32::Foundation::{GetLastError, WPARAM};
use windows_sys::Win32::Foundation::{HINSTANCE, LPARAM, LRESULT};
use windows_sys::Win32::UI::WindowsAndMessaging::HHOOK;
use windows_sys::Win32::UI::WindowsAndMessaging::{
SetWindowsHookExA, KBDLLHOOKSTRUCT, MSLLHOOKSTRUCT, WHEEL_DELTA, WH_KEYBOARD_LL, WH_MOUSE_LL,
WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP,
WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SYSKEYDOWN,
WM_SYSKEYUP, WM_XBUTTONDOWN, WM_XBUTTONUP,
};

pub const TRUE: i32 = 1;
pub const FALSE: i32 = 0;

pub static mut HOOK: HHOOK = null_mut();
#[inline]
fn hiword(l: u32) -> u16 {
((l >> 16) & 0xffff) as u16
}

pub static mut HOOK: HHOOK = 0;
lazy_static! {
pub(crate) static ref KEYBOARD: Mutex<Keyboard> = Mutex::new(Keyboard::new().unwrap());
}
Expand All @@ -28,24 +33,28 @@ pub unsafe fn get_code(lpdata: LPARAM) -> DWORD {
let kb = *(lpdata as *const KBDLLHOOKSTRUCT);
kb.vkCode
}

pub unsafe fn get_scan_code(lpdata: LPARAM) -> DWORD {
let kb = *(lpdata as *const KBDLLHOOKSTRUCT);
kb.scanCode
}

pub unsafe fn get_point(lpdata: LPARAM) -> (LONG, LONG) {
let mouse = *(lpdata as *const MSLLHOOKSTRUCT);
(mouse.pt.x, mouse.pt.y)
}

// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644986(v=vs.85)
/// confusingly, this function returns a WORD (unsigned), but may be
/// interpreted as either signed or unsigned depending on context
pub unsafe fn get_delta(lpdata: LPARAM) -> WORD {
let mouse = *(lpdata as *const MSLLHOOKSTRUCT);
HIWORD(mouse.mouseData)
hiword(mouse.mouseData)
}

pub unsafe fn get_button_code(lpdata: LPARAM) -> WORD {
let mouse = *(lpdata as *const MSLLHOOKSTRUCT);
HIWORD(mouse.mouseData)
hiword(mouse.mouseData)
}

pub unsafe fn convert(param: WPARAM, lpdata: LPARAM) -> Option<EventType> {
Expand Down Expand Up @@ -82,16 +91,16 @@ pub unsafe fn convert(param: WPARAM, lpdata: LPARAM) -> Option<EventType> {
})
}
Ok(WM_MOUSEWHEEL) => {
let delta = get_delta(lpdata) as c_short;
let delta = get_delta(lpdata);
Some(EventType::Wheel {
delta_x: 0,
delta_y: (delta / WHEEL_DELTA) as i64,
delta_y: (delta / hiword(WHEEL_DELTA)) as i64,
})
}
Ok(WM_MOUSEHWHEEL) => {
let delta = get_delta(lpdata) as c_short;
let delta = get_delta(lpdata);
Some(EventType::Wheel {
delta_x: (delta / WHEEL_DELTA) as i64,
delta_x: (delta / hiword(WHEEL_DELTA)) as i64,
delta_y: 0,
})
}
Expand All @@ -100,15 +109,17 @@ pub unsafe fn convert(param: WPARAM, lpdata: LPARAM) -> Option<EventType> {
}

type RawCallback = unsafe extern "system" fn(code: c_int, param: WPARAM, lpdata: LPARAM) -> LRESULT;

pub enum HookError {
Mouse(DWORD),
Key(DWORD),
}

pub unsafe fn set_key_hook(callback: RawCallback) -> Result<(), HookError> {
let hook = SetWindowsHookExA(WH_KEYBOARD_LL, Some(callback), null_mut(), 0);
let hmod: HINSTANCE = 0;
let hook = SetWindowsHookExA(WH_KEYBOARD_LL, Some(callback), hmod, 0);

if hook.is_null() {
if hook == 0 {
let error = GetLastError();
return Err(HookError::Key(error));
}
Expand All @@ -117,8 +128,9 @@ pub unsafe fn set_key_hook(callback: RawCallback) -> Result<(), HookError> {
}

pub unsafe fn set_mouse_hook(callback: RawCallback) -> Result<(), HookError> {
let hook = SetWindowsHookExA(WH_MOUSE_LL, Some(callback), null_mut(), 0);
if hook.is_null() {
let hmod: HINSTANCE = 0;
let hook = SetWindowsHookExA(WH_MOUSE_LL, Some(callback), hmod, 0);
if hook == 0 {
let error = GetLastError();
return Err(HookError::Mouse(error));
}
Expand Down
2 changes: 1 addition & 1 deletion src/windows/display.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::rdev::DisplayError;
use std::convert::TryInto;
use winapi::um::winuser::{GetSystemMetrics, SM_CXSCREEN, SM_CYSCREEN};
use windows_sys::Win32::UI::WindowsAndMessaging::{GetSystemMetrics, SM_CXSCREEN, SM_CYSCREEN};

pub fn display_size() -> Result<(u64, u64), DisplayError> {
let w = unsafe {
Expand Down
8 changes: 5 additions & 3 deletions src/windows/grab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ use crate::rdev::{Event, EventType, GrabError};
use crate::windows::common::{convert, set_key_hook, set_mouse_hook, HookError, HOOK, KEYBOARD};
use std::ptr::null_mut;
use std::time::SystemTime;
use winapi::um::winuser::{CallNextHookEx, GetMessageA, HC_ACTION};
use windows_sys::Win32::Foundation::HWND;
use windows_sys::Win32::UI::WindowsAndMessaging::{CallNextHookEx, GetMessageA, HC_ACTION};

static mut GLOBAL_CALLBACK: Option<Box<dyn FnMut(Event) -> Option<Event>>> = None;

unsafe extern "system" fn raw_callback(code: i32, param: usize, lpdata: isize) -> isize {
if code == HC_ACTION {
if code == HC_ACTION as i32 {
let opt = convert(param, lpdata);
if let Some(event_type) = opt {
let name = match &event_type {
Expand Down Expand Up @@ -48,12 +49,13 @@ pub fn grab<T>(callback: T) -> Result<(), GrabError>
where
T: FnMut(Event) -> Option<Event> + 'static,
{
let hwnd: HWND = 0;
unsafe {
GLOBAL_CALLBACK = Some(Box::new(callback));
set_key_hook(raw_callback)?;
set_mouse_hook(raw_callback)?;

GetMessageA(null_mut(), null_mut(), 0, 0);
GetMessageA(null_mut(), hwnd, 0, 0);
}
Ok(())
}
22 changes: 13 additions & 9 deletions src/windows/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,24 @@ use crate::rdev::{EventType, Key, KeyboardState};
use crate::windows::common::{get_code, get_scan_code, FALSE, TRUE};
use crate::windows::keycodes::code_from_key;
use std::ptr::null_mut;
use winapi::shared::minwindef::{BYTE, HKL, LPARAM, UINT};
use winapi::um::processthreadsapi::GetCurrentThreadId;
use winapi::um::winuser;
use winapi::um::winuser::{
GetForegroundWindow, GetKeyState, GetKeyboardLayout, GetKeyboardState,
GetWindowThreadProcessId, ToUnicodeEx, VK_CAPITAL, VK_LSHIFT, VK_RSHIFT, VK_SHIFT,
use windows_sys::Win32::Foundation::LPARAM;
use windows_sys::Win32::System::Threading::{AttachThreadInput, GetCurrentThreadId};
use windows_sys::Win32::UI::Input::KeyboardAndMouse::{
GetKeyState, GetKeyboardLayout, GetKeyboardState, ToUnicodeEx, VK_CAPITAL, VK_LSHIFT,
VK_RSHIFT, VK_SHIFT,
};
use windows_sys::Win32::UI::TextServices::HKL;
use windows_sys::Win32::UI::WindowsAndMessaging::{GetForegroundWindow, GetWindowThreadProcessId};

const VK_SHIFT_: usize = VK_SHIFT as usize;
const VK_CAPITAL_: usize = VK_CAPITAL as usize;
const VK_LSHIFT_: usize = VK_LSHIFT as usize;
const VK_RSHIFT_: usize = VK_RSHIFT as usize;
const HIGHBIT: u8 = 0x80;

pub type UINT = u32;
pub type BYTE = u8;

pub struct Keyboard {
last_code: UINT,
last_scan_code: UINT,
Expand Down Expand Up @@ -47,16 +51,16 @@ impl Keyboard {
let mut state = [0_u8; 256];
let state_ptr = state.as_mut_ptr();

let _shift = GetKeyState(VK_SHIFT);
let _shift = GetKeyState(VK_SHIFT as i32);
let current_window_thread_id = GetWindowThreadProcessId(GetForegroundWindow(), null_mut());
let thread_id = GetCurrentThreadId();
// Attach to active thread so we can get that keyboard state
let status = if winuser::AttachThreadInput(thread_id, current_window_thread_id, TRUE) == 1 {
let status = if AttachThreadInput(thread_id, current_window_thread_id, TRUE) == 1 {
// Current state of the modifiers in keyboard
let status = GetKeyboardState(state_ptr);

// Detach
winuser::AttachThreadInput(thread_id, current_window_thread_id, FALSE);
AttachThreadInput(thread_id, current_window_thread_id, FALSE);
status
} else {
// Could not attach, perhaps it is this process?
Expand Down
2 changes: 1 addition & 1 deletion src/windows/keycodes.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::rdev::Key;
use crate::windows::WORD;
use std::convert::TryInto;
use winapi::shared::minwindef::WORD;

macro_rules! decl_keycodes {
($($key:ident, $code:literal),*) => {
Expand Down
8 changes: 4 additions & 4 deletions src/windows/listen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use crate::windows::common::{convert, set_key_hook, set_mouse_hook, HookError, H
use std::os::raw::c_int;
use std::ptr::null_mut;
use std::time::SystemTime;
use winapi::shared::minwindef::{LPARAM, LRESULT, WPARAM};
use winapi::um::winuser::{CallNextHookEx, GetMessageA, HC_ACTION};
use windows_sys::Win32::Foundation::{LPARAM, LRESULT, WPARAM};
use windows_sys::Win32::UI::WindowsAndMessaging::{CallNextHookEx, GetMessageA, HC_ACTION};

static mut GLOBAL_CALLBACK: Option<Box<dyn FnMut(Event)>> = None;

Expand All @@ -18,7 +18,7 @@ impl From<HookError> for ListenError {
}

unsafe extern "system" fn raw_callback(code: c_int, param: WPARAM, lpdata: LPARAM) -> LRESULT {
if code == HC_ACTION {
if code == HC_ACTION as i32 {
let opt = convert(param, lpdata);
if let Some(event_type) = opt {
let name = match &event_type {
Expand Down Expand Up @@ -50,7 +50,7 @@ where
set_key_hook(raw_callback)?;
set_mouse_hook(raw_callback)?;

GetMessageA(null_mut(), null_mut(), 0, 0);
GetMessageA(null_mut(), 0, 0, 0);
}
Ok(())
}
7 changes: 5 additions & 2 deletions src/windows/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
extern crate winapi;

mod common;
mod display;
#[cfg(feature = "unstable_grab")]
Expand All @@ -15,3 +13,8 @@ pub use crate::windows::grab::grab;
pub use crate::windows::keyboard::Keyboard;
pub use crate::windows::listen::listen;
pub use crate::windows::simulate::simulate;

// types not defined by windows-sys
pub type DWORD = u32;
pub type WORD = u16;
pub type LONG = i32;
48 changes: 26 additions & 22 deletions src/windows/simulate.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
use crate::rdev::{Button, EventType, SimulateError};
use crate::windows::keycodes::code_from_key;
use std::convert::TryFrom;
use std::ffi::c_int;
use std::mem::size_of;
use winapi::ctypes::{c_int, c_short};
use winapi::shared::minwindef::{DWORD, UINT, WORD};
use winapi::shared::ntdef::LONG;
use winapi::um::winuser::{
GetSystemMetrics, INPUT_u, SendInput, INPUT, INPUT_KEYBOARD, INPUT_MOUSE, KEYBDINPUT,
KEYEVENTF_KEYUP, MOUSEEVENTF_ABSOLUTE, MOUSEEVENTF_HWHEEL, MOUSEEVENTF_LEFTDOWN,
MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_MOVE,
MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, MOUSEEVENTF_VIRTUALDESK, MOUSEEVENTF_WHEEL,
MOUSEEVENTF_XDOWN, MOUSEEVENTF_XUP, MOUSEINPUT, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN,
WHEEL_DELTA,
use windows_sys::Win32::UI::WindowsAndMessaging::{
GetSystemMetrics, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, WHEEL_DELTA,
};

use crate::windows::keyboard::UINT;
use crate::windows::{DWORD, LONG, WORD};
use windows_sys::Win32::UI::Input::KeyboardAndMouse::{
SendInput, INPUT, INPUT_0, INPUT_KEYBOARD, INPUT_MOUSE, KEYBDINPUT, KEYEVENTF_KEYUP,
MOUSEEVENTF_ABSOLUTE, MOUSEEVENTF_HWHEEL, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP,
MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_MOVE, MOUSEEVENTF_RIGHTDOWN,
MOUSEEVENTF_RIGHTUP, MOUSEEVENTF_VIRTUALDESK, MOUSEEVENTF_WHEEL, MOUSEEVENTF_XDOWN,
MOUSEEVENTF_XUP, MOUSEINPUT,
};

/// Not defined in win32 but define here for clarity
static KEYEVENTF_KEYDOWN: DWORD = 0;
static KEYEVENTF_KEYDOWN: u32 = 0;

fn sim_mouse_event(flags: DWORD, data: DWORD, dx: LONG, dy: LONG) -> Result<(), SimulateError> {
let mut union: INPUT_u = unsafe { std::mem::zeroed() };
let inner_union = unsafe { union.mi_mut() };
fn sim_mouse_event(flags: DWORD, data: LONG, dx: LONG, dy: LONG) -> Result<(), SimulateError> {
let mut union: INPUT_0 = unsafe { std::mem::zeroed() };
let inner_union = unsafe { &mut union.mi };
*inner_union = MOUSEINPUT {
dx,
dy,
Expand All @@ -28,8 +32,8 @@ fn sim_mouse_event(flags: DWORD, data: DWORD, dx: LONG, dy: LONG) -> Result<(),
dwExtraInfo: 0,
};
let mut input = [INPUT {
type_: INPUT_MOUSE,
u: union,
r#type: INPUT_MOUSE,
Anonymous: union,
}; 1];
let value = unsafe {
SendInput(
Expand All @@ -46,8 +50,8 @@ fn sim_mouse_event(flags: DWORD, data: DWORD, dx: LONG, dy: LONG) -> Result<(),
}

fn sim_keyboard_event(flags: DWORD, vk: WORD, scan: WORD) -> Result<(), SimulateError> {
let mut union: INPUT_u = unsafe { std::mem::zeroed() };
let inner_union = unsafe { union.ki_mut() };
let mut union: INPUT_0 = unsafe { std::mem::zeroed() };
let inner_union = unsafe { &mut union.ki };
*inner_union = KEYBDINPUT {
wVk: vk,
wScan: scan,
Expand All @@ -56,8 +60,8 @@ fn sim_keyboard_event(flags: DWORD, vk: WORD, scan: WORD) -> Result<(), Simulate
dwExtraInfo: 0,
};
let mut input = [INPUT {
type_: INPUT_KEYBOARD,
u: union,
r#type: INPUT_KEYBOARD,
Anonymous: union,
}; 1];
let value = unsafe {
SendInput(
Expand Down Expand Up @@ -99,7 +103,7 @@ pub fn simulate(event_type: &EventType) -> Result<(), SimulateError> {
if *delta_x != 0 {
sim_mouse_event(
MOUSEEVENTF_HWHEEL,
(c_short::try_from(*delta_x).map_err(|_| SimulateError)? * WHEEL_DELTA) as u32,
(u32::try_from(*delta_x).map_err(|_| SimulateError)? * WHEEL_DELTA) as LONG,
0,
0,
)?;
Expand All @@ -108,7 +112,7 @@ pub fn simulate(event_type: &EventType) -> Result<(), SimulateError> {
if *delta_y != 0 {
sim_mouse_event(
MOUSEEVENTF_WHEEL,
(c_short::try_from(*delta_y).map_err(|_| SimulateError)? * WHEEL_DELTA) as u32,
(u32::try_from(*delta_y).map_err(|_| SimulateError)? * WHEEL_DELTA) as LONG,
0,
0,
)?;
Expand Down