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
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ input-linux = { version = "0.7.1", optional = true }
xkbcommon = { version = "0.8", optional = true }

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

[dev-dependencies]
serde_json = "1.0"
Expand Down
36 changes: 17 additions & 19 deletions src/windows/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@ use std::convert::TryInto;
use std::os::raw::{c_int, c_short};
use std::ptr::null_mut;
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::{
KBDLLHOOKSTRUCT, MSLLHOOKSTRUCT, SetWindowsHookExA, WH_KEYBOARD_LL, WH_MOUSE_LL, WHEEL_DELTA,
WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP,
use windows_sys::Win32::Foundation::{GetLastError, LPARAM, LRESULT, WPARAM};
use windows_sys::Win32::UI::WindowsAndMessaging::{
HHOOK, KBDLLHOOKSTRUCT, MSLLHOOKSTRUCT, SetWindowsHookExA, WH_KEYBOARD_LL, WH_MOUSE_LL,
WHEEL_DELTA, 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;

Expand All @@ -24,37 +22,37 @@ lazy_static! {
pub(crate) static ref KEYBOARD: Mutex<Keyboard> = Mutex::new(Keyboard::new().unwrap());
}

pub unsafe fn get_code(lpdata: LPARAM) -> DWORD {
pub unsafe fn get_code(lpdata: LPARAM) -> u32 {
unsafe {
let kb = *(lpdata as *const KBDLLHOOKSTRUCT);
kb.vkCode
}
}
pub unsafe fn get_scan_code(lpdata: LPARAM) -> DWORD {
pub unsafe fn get_scan_code(lpdata: LPARAM) -> u32 {
unsafe {
let kb = *(lpdata as *const KBDLLHOOKSTRUCT);
kb.scanCode
}
}
pub unsafe fn get_point(lpdata: LPARAM) -> (LONG, LONG) {
pub unsafe fn get_point(lpdata: LPARAM) -> (i32, i32) {
unsafe {
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
/// confusingly, this function returns a u16 (unsigned), but may be
/// interpreted as either signed or unsigned depending on context
pub unsafe fn get_delta(lpdata: LPARAM) -> WORD {
pub unsafe fn get_delta(lpdata: LPARAM) -> u16 {
unsafe {
let mouse = *(lpdata as *const MSLLHOOKSTRUCT);
HIWORD(mouse.mouseData)
(mouse.mouseData >> 16) as u16
}
}
pub unsafe fn get_button_code(lpdata: LPARAM) -> WORD {
pub unsafe fn get_button_code(lpdata: LPARAM) -> u16 {
unsafe {
let mouse = *(lpdata as *const MSLLHOOKSTRUCT);
HIWORD(mouse.mouseData)
(mouse.mouseData >> 16) as u16
}
}

Expand Down Expand Up @@ -96,13 +94,13 @@ pub unsafe fn convert(param: WPARAM, lpdata: LPARAM) -> Option<EventType> {
let delta = get_delta(lpdata) as c_short;
Some(EventType::Wheel {
delta_x: 0,
delta_y: (delta / WHEEL_DELTA) as i64,
delta_y: (delta / WHEEL_DELTA as c_short) as i64,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're casting right above, maybe we can drop both c_short casts.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to keep both variables signed, since get_delta results may be interpreted as a signed as stated in the function
If we were to delete the delta cast we would still need to cast WHEEL_DATA to u16, and it would break the logic, since all the values would now be positive.

})
}
Ok(WM_MOUSEHWHEEL) => {
let delta = get_delta(lpdata) as c_short;
Some(EventType::Wheel {
delta_x: (delta / WHEEL_DELTA) as i64,
delta_x: (delta / WHEEL_DELTA as c_short) as i64,
delta_y: 0,
})
}
Expand All @@ -113,8 +111,8 @@ 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),
Mouse(u32),
Key(u32),
}

pub unsafe fn set_key_hook(callback: RawCallback) -> Result<(), HookError> {
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
20 changes: 10 additions & 10 deletions src/windows/grab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ use crate::rdev::{Event, EventType, GrabError};
use crate::windows::common::{HOOK, HookError, KEYBOARD, convert, set_key_hook, set_mouse_hook};
use std::ptr::null_mut;
use std::time::SystemTime;
use winapi::um::winuser::{CallNextHookEx, GetMessageA, HC_ACTION};
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 {
unsafe {
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 All @@ -24,14 +24,14 @@ unsafe extern "system" fn raw_callback(code: i32, param: usize, lpdata: isize) -
name,
};
let ptr = &raw mut GLOBAL_CALLBACK;
if let Some(callback) = &mut *ptr {
if callback(event).is_none() {
// https://stackoverflow.com/questions/42756284/blocking-windows-mouse-click-using-setwindowshookex
// https://android.developreference.com/article/14560004/Blocking+windows+mouse+click+using+SetWindowsHookEx()
// https://cboard.cprogramming.com/windows-programming/99678-setwindowshookex-wm_keyboard_ll.html
// let _result = CallNextHookEx(HOOK, code, param, lpdata);
return 1;
}
if let Some(callback) = &mut *ptr
&& callback(event).is_none()
{
// https://stackoverflow.com/questions/42756284/blocking-windows-mouse-click-using-setwindowshookex
// https://android.developreference.com/article/14560004/Blocking+windows+mouse+click+using+SetWindowsHookEx()
// https://cboard.cprogramming.com/windows-programming/99678-setwindowshookex-wm_keyboard_ll.html
// let _result = CallNextHookEx(HOOK, code, param, lpdata);
return 1;
}
}
}
Expand Down
47 changes: 23 additions & 24 deletions src/windows/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ use crate::rdev::{EventType, Key, KeyboardState};
use crate::windows::common::{FALSE, TRUE, get_code, get_scan_code};
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, HKL, ToUnicodeEx, VK_CAPITAL, VK_LSHIFT,
VK_RSHIFT, VK_SHIFT,
};
use windows_sys::Win32::UI::WindowsAndMessaging::{GetForegroundWindow, GetWindowThreadProcessId};

const VK_SHIFT_: usize = VK_SHIFT as usize;
const VK_CAPITAL_: usize = VK_CAPITAL as usize;
Expand All @@ -17,9 +17,9 @@ const VK_RSHIFT_: usize = VK_RSHIFT as usize;
const HIGHBIT: u8 = 0x80;

pub struct Keyboard {
last_code: UINT,
last_scan_code: UINT,
last_state: [BYTE; 256],
last_code: u32,
last_scan_code: u32,
last_state: [u8; 256],
last_is_dead: bool,
}

Expand Down Expand Up @@ -50,23 +50,22 @@ 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 {
// Current state of the modifiers in keyboard
let status = GetKeyboardState(state_ptr);

// Detach
winuser::AttachThreadInput(thread_id, current_window_thread_id, FALSE);
status
} else {
// Could not attach, perhaps it is this process?
GetKeyboardState(state_ptr)
};
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
AttachThreadInput(thread_id, current_window_thread_id, FALSE);
status
} else {
// Could not attach, perhaps it is this process?
GetKeyboardState(state_ptr)
};

if status != 1 {
return None;
Expand All @@ -76,7 +75,7 @@ impl Keyboard {
}
}

pub(crate) unsafe fn get_code_name(&mut self, code: UINT, scan_code: UINT) -> Option<String> {
pub(crate) unsafe fn get_code_name(&mut self, code: u32, scan_code: u32) -> Option<String> {
unsafe {
let current_window_thread_id =
GetWindowThreadProcessId(GetForegroundWindow(), null_mut());
Expand Down Expand Up @@ -122,7 +121,7 @@ impl Keyboard {
}
}

unsafe fn clear_keyboard_buffer(&self, code: UINT, scan_code: UINT, layout: HKL) {
unsafe fn clear_keyboard_buffer(&self, code: u32, scan_code: u32, layout: HKL) {
unsafe {
const BUF_LEN: i32 = 32;
let mut buff = [0_u16; BUF_LEN as usize];
Expand Down
7 changes: 3 additions & 4 deletions src/windows/keycodes.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use crate::rdev::Key;
use std::convert::TryInto;
use winapi::shared::minwindef::WORD;

macro_rules! decl_keycodes {
($($key:ident, $code:literal),*) => {
//TODO: make const when rust lang issue #49146 is fixed
pub fn code_from_key(key: Key) -> Option<WORD> {
pub fn code_from_key(key: Key) -> Option<u16> {
match key {
$(
Key::$key => Some($code),
Expand All @@ -16,7 +15,7 @@ macro_rules! decl_keycodes {
}

//TODO: make const when rust lang issue #49146 is fixed
pub fn key_from_code(code: WORD) -> Key {
pub fn key_from_code(code: u16) -> Key {
match code {
$(
$code => Key::$key,
Expand All @@ -28,7 +27,7 @@ macro_rules! decl_keycodes {
}

// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
// We redefined here for Letter and number keys which are not in winapi crate (and don't have a name either in win32)
// We redefined here for letter and number keys which do not have named Win32 constants.
decl_keycodes! {
Alt, 164,
AltGr, 165,
Expand Down
6 changes: 3 additions & 3 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::{HOOK, HookError, KEYBOARD, convert, set_key_hook, s
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 @@ -19,7 +19,7 @@ impl From<HookError> for ListenError {

unsafe extern "system" fn raw_callback(code: c_int, param: WPARAM, lpdata: LPARAM) -> LRESULT {
unsafe {
if code == HC_ACTION {
if code == HC_ACTION as c_int {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If c_int is not the native type, maybe we should switch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't understand what we should switch.
HC_ACTION in winapi was of type c_int but in windows-sys is u32.
The code variable seems to come as a parameter from a callback that is given to SetWindowsHookExA so I think it's probably better to leave it as a c_int.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am the guy from 3 year old PR #98, best to leave it for now. MS is unlikely to break compatibility.

Although it is a good idea to add a feature flag if someone wants to use windows-rs. I ended up making a fork and replacing it for a project for consistency.

let opt = convert(param, lpdata);
if let Some(event_type) = opt {
let name = match &event_type {
Expand Down
2 changes: 0 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 Down
Loading