Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multimedia keys (1.2 revision) #5

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
206 changes: 103 additions & 103 deletions Cargo.lock

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rdev"
version = "0.6.3"
version = "0.8.0"
authors = ["Nicolas Patry <[email protected]>", "David M <[email protected]>"]
edition = "2021"
publish = false
Expand All @@ -17,6 +17,7 @@ license = "MIT"
[dependencies]
serde = {version = "1.0", features = ["derive"], optional=true}
lazy_static = "1.4"
tokio = "1.37"

[features]
serialize = ["serde"]
Expand All @@ -37,7 +38,7 @@ epoll = {version = "4.3", optional=true}
inotify = {version = "0.10", default-features=false, optional=true}

[target.'cfg(target_os = "windows")'.dependencies]
windows-sys = { version = "0.48.0", features = [
windows-sys = { version = "0.52.0", features = [
"Win32_UI_WindowsAndMessaging",
"Win32_Foundation",
"Win32_System_Threading",
Expand All @@ -50,8 +51,8 @@ serde_json = "1.0"
# Some tests interact with the real OS. We can't hit the OS in parallel
# because that leads to unexpected behavior and flaky tests, so we need
# to run thoses tests in sequence instead.
serial_test = "2.0"
tokio = {version = "1.5", features=["sync", "macros", "rt-multi-thread"]}
serial_test = "3.1"
tokio = {version = "1.37", features=["sync", "macros", "rt-multi-thread"]}

[[example]]
name = "serialize"
Expand Down
19 changes: 16 additions & 3 deletions examples/grab.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rdev::{grab, Event, EventType, Key};
use rdev::{grab, simulate, Event, EventType, Key};

fn main() {
// This will block.
Expand All @@ -8,12 +8,25 @@ fn main() {
}

fn callback(event: Event) -> Option<Event> {
println!("My callback {:?}", event);
match event.event_type {
EventType::SimulatedKeyRelease(_) | EventType::SimulatedKeyPress(_) => {
println!("{:?}", event.event_type);
Some(event)
}

EventType::KeyPress(Key::Tab) => {
println!("Cancelling tab !");
println!("Pressed TAB: {:?}", event.name.unwrap_or_default());

simulate(&EventType::KeyPress(Key::KeyA)).unwrap();
std::thread::sleep(std::time::Duration::from_millis(20));
simulate(&EventType::KeyRelease(Key::KeyA)).unwrap();
None
}
EventType::KeyRelease(_) | EventType::KeyPress(_) => {
println!("{:?}", event.event_type);
Some(event)
}

_ => Some(event),
}
}
8 changes: 4 additions & 4 deletions src/linux/simulate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ unsafe fn send_native(event_type: &EventType, display: *mut xlib::Display) -> Op
Button::Left => xtest::XTestFakeButtonEvent(display, 1, TRUE, 0),
Button::Middle => xtest::XTestFakeButtonEvent(display, 2, TRUE, 0),
Button::Right => xtest::XTestFakeButtonEvent(display, 3, TRUE, 0),
Button::Forward => xtest::XTestFakeButtonEvent(display, 4 , TRUE, 0),
Button::Backward => xtest::XTestFakeButtonEvent(display, 5 , TRUE, 0),
Button::Forward => xtest::XTestFakeButtonEvent(display, 4, TRUE, 0),
Button::Backward => xtest::XTestFakeButtonEvent(display, 5, TRUE, 0),
Button::Unknown(code) => {
xtest::XTestFakeButtonEvent(display, (*code).try_into().ok()?, TRUE, 0)
}
Expand All @@ -31,8 +31,8 @@ unsafe fn send_native(event_type: &EventType, display: *mut xlib::Display) -> Op
Button::Left => xtest::XTestFakeButtonEvent(display, 1, FALSE, 0),
Button::Middle => xtest::XTestFakeButtonEvent(display, 2, FALSE, 0),
Button::Right => xtest::XTestFakeButtonEvent(display, 3, FALSE, 0),
Button::Forward => xtest::XTestFakeButtonEvent(display, 4 , FALSE, 0),
Button::Backward => xtest::XTestFakeButtonEvent(display, 5 , FALSE, 0),
Button::Forward => xtest::XTestFakeButtonEvent(display, 4, FALSE, 0),
Button::Backward => xtest::XTestFakeButtonEvent(display, 5, FALSE, 0),
Button::Unknown(code) => {
xtest::XTestFakeButtonEvent(display, (*code).try_into().ok()?, FALSE, 0)
}
Expand Down
16 changes: 16 additions & 0 deletions src/rdev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ pub enum Key {
VolumeMute,
VolumeDown,
VolumeUp,
NextTrack,
PrevTrack,
StopTrack,
PlayPauseTrack,
Home,
LeftArrow,
/// also known as "windows", "super", and "command"
Expand Down Expand Up @@ -249,22 +253,34 @@ pub enum EventType {
/// To the actual letter a user would use, that requires some layout logic to be added.
KeyPress(Key),
KeyRelease(Key),
SimulatedKeyPress(Key),
SimulatedKeyRelease(Key),
/// Mouse Button
ButtonPress(Button),
ButtonRelease(Button),
SimulatedButtonPress(Button),
SimulatedButtonRelease(Button),
/// Values in pixels. `EventType::MouseMove{x: 0, y: 0}` corresponds to the
/// top left corner, with x increasing downward and y increasing rightward
MouseMove {
x: f64,
y: f64,
},
SimulatedMouseMove {
x: f64,
y: f64,
},
/// `delta_y` represents vertical scroll and `delta_x` represents horizontal scroll.
/// Positive values correspond to scrolling up or right and negative values
/// correspond to scrolling down or left
Wheel {
delta_x: i64,
delta_y: i64,
},
SimulatedWheel {
delta_x: i64,
delta_y: i64,
},
}

/// When events arrive from the OS they get some additional information added from
Expand Down
147 changes: 112 additions & 35 deletions src/windows/common.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
use crate::rdev::{Button, EventType};
use crate::windows::keyboard::Keyboard;
use crate::windows::keycodes::key_from_code;
use crate::windows::{DWORD, LONG, MOUSE_BACKWARD, MOUSE_FORWARD, WORD};
use lazy_static::lazy_static;
use std::convert::TryInto;
use std::os::raw::c_int;
use std::sync::Mutex;

use lazy_static::lazy_static;
use windows_sys::Win32::Foundation::{GetLastError, WPARAM};
use windows_sys::Win32::Foundation::{HLOCAL, LPARAM, LRESULT};
use windows_sys::Win32::UI::WindowsAndMessaging::HHOOK;
Expand All @@ -15,9 +12,16 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SYSKEYDOWN,
WM_SYSKEYUP, WM_XBUTTONDOWN, WM_XBUTTONUP,
};

use crate::rdev::{Button, EventType};
use crate::windows::keyboard::Keyboard;
use crate::windows::keycodes::key_from_code;
use crate::windows::{DWORD, LONG, MOUSE_BACKWARD, MOUSE_FORWARD, WORD};

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

pub const KEYBOARDMANAGER_INJECTED_FLAG: usize = 0x1;
#[inline]
fn hiword(l: u32) -> u16 {
((l >> 16) & 0xffff) as u16
Expand All @@ -33,6 +37,13 @@ pub unsafe fn get_code(lpdata: LPARAM) -> DWORD {
kb.vkCode
}

/// Returns whether the input received was made by the rdev simulate command, or the user pressing keys.
pub unsafe fn get_simulated(lpdata: LPARAM) -> bool {
let kb = *(lpdata as *const KBDLLHOOKSTRUCT);

// Rust clippy insisted - this returns true if it equals, otherwise it returns false
kb.dwExtraInfo == KEYBOARDMANAGER_INJECTED_FLAG
}
pub unsafe fn get_scan_code(lpdata: LPARAM) -> DWORD {
let kb = *(lpdata as *const KBDLLHOOKSTRUCT);
kb.scanCode
Expand Down Expand Up @@ -60,56 +71,122 @@ pub unsafe fn convert(param: WPARAM, lpdata: LPARAM) -> Option<EventType> {
Ok(WM_KEYDOWN) | Ok(WM_SYSKEYDOWN) => {
let code = get_code(lpdata);
let key = key_from_code(code as u16);
Some(EventType::KeyPress(key))

match get_simulated(lpdata) {
true => Some(EventType::SimulatedKeyPress(key)),
false => Some(EventType::KeyPress(key)),
}
}
Ok(WM_KEYUP) | Ok(WM_SYSKEYUP) => {
let code = get_code(lpdata);
let key = key_from_code(code as u16);
Some(EventType::KeyRelease(key))

match get_simulated(lpdata) {
true => Some(EventType::SimulatedKeyRelease(key)),
false => Some(EventType::KeyRelease(key)),
}
}
Ok(WM_LBUTTONDOWN) => Some(EventType::ButtonPress(Button::Left)),
Ok(WM_LBUTTONUP) => Some(EventType::ButtonRelease(Button::Left)),
Ok(WM_MBUTTONDOWN) => Some(EventType::ButtonPress(Button::Middle)),
Ok(WM_MBUTTONUP) => Some(EventType::ButtonRelease(Button::Middle)),
Ok(WM_RBUTTONDOWN) => Some(EventType::ButtonPress(Button::Right)),
Ok(WM_RBUTTONUP) => Some(EventType::ButtonRelease(Button::Right)),
Ok(WM_LBUTTONDOWN) => match get_simulated(lpdata) {
true => Some(EventType::SimulatedButtonPress(Button::Left)),
false => Some(EventType::ButtonPress(Button::Left)),
},
Ok(WM_LBUTTONUP) => match get_simulated(lpdata) {
true => Some(EventType::SimulatedButtonRelease(Button::Left)),
false => Some(EventType::ButtonRelease(Button::Left)),
},
Ok(WM_MBUTTONDOWN) => match get_simulated(lpdata) {
true => Some(EventType::SimulatedButtonPress(Button::Middle)),
false => Some(EventType::ButtonRelease(Button::Middle)),
},
Ok(WM_MBUTTONUP) => match get_simulated(lpdata) {
true => Some(EventType::SimulatedButtonRelease(Button::Middle)),
false => Some(EventType::ButtonRelease(Button::Middle)),
},
Ok(WM_RBUTTONDOWN) => match get_simulated(lpdata) {
true => Some(EventType::SimulatedButtonPress(Button::Right)),
false => Some(EventType::ButtonPress(Button::Right)),
},
Ok(WM_RBUTTONUP) => match get_simulated(lpdata) {
true => Some(EventType::SimulatedButtonRelease(Button::Right)),
false => Some(EventType::ButtonRelease(Button::Right)),
},
Ok(WM_XBUTTONDOWN) => {
let code = get_button_code(lpdata) as u8;
match code {
num if num == MOUSE_FORWARD => Some(EventType::ButtonPress(Button::Forward)),
num if num == MOUSE_BACKWARD => Some(EventType::ButtonPress(Button::Backward)),
num => Some(EventType::ButtonPress(Button::Unknown(num))),
num if num == MOUSE_FORWARD => match get_simulated(lpdata) {
true => Some(EventType::SimulatedButtonPress(Button::Forward)),
false => Some(EventType::ButtonPress(Button::Forward)),
},
num if num == MOUSE_BACKWARD => match get_simulated(lpdata) {
true => Some(EventType::SimulatedButtonPress(Button::Backward)),
false => Some(EventType::ButtonPress(Button::Backward)),
},
num => match get_simulated(lpdata) {
true => Some(EventType::SimulatedButtonPress(Button::Unknown(num))),
false => Some(EventType::ButtonPress(Button::Unknown(num))),
},
}
}

Ok(WM_XBUTTONUP) => {
let code = get_button_code(lpdata) as u8;
match code {
num if num == MOUSE_FORWARD => Some(EventType::ButtonRelease(Button::Forward)),
num if num == MOUSE_BACKWARD => Some(EventType::ButtonRelease(Button::Backward)),
num => Some(EventType::ButtonRelease(Button::Unknown(num))),
num if num == MOUSE_FORWARD => match get_simulated(lpdata) {
true => Some(EventType::SimulatedButtonRelease(Button::Forward)),
false => Some(EventType::ButtonRelease(Button::Forward)),
},
num if num == MOUSE_BACKWARD => match get_simulated(lpdata) {
true => Some(EventType::SimulatedButtonRelease(Button::Backward)),
false => Some(EventType::ButtonRelease(Button::Backward)),
},
num => match get_simulated(lpdata) {
true => Some(EventType::SimulatedButtonRelease(Button::Unknown(num))),
false => Some(EventType::ButtonRelease(Button::Unknown(num))),
},
}
}
Ok(WM_MOUSEMOVE) => {
let (x, y) = get_point(lpdata);
Some(EventType::MouseMove {
x: x as f64,
y: y as f64,
})
match get_simulated(lpdata) {
true => Some(EventType::SimulatedMouseMove {
x: x as f64,
y: y as f64,
}),
false => Some(EventType::MouseMove {
x: x as f64,
y: y as f64,
}),
}
}
Ok(WM_MOUSEWHEEL) => {
let delta = get_delta(lpdata);
Some(EventType::Wheel {
delta_x: 0,
delta_y: (delta.checked_div(hiword(WHEEL_DELTA)).unwrap_or_default()) as i64,
})
match get_simulated(lpdata) {
true => Some(EventType::SimulatedWheel {
delta_x: 0,
delta_y: (delta.checked_div(hiword(WHEEL_DELTA)).unwrap_or_default()) as i64,
}),
false => Some(EventType::Wheel {
delta_x: 0,
delta_y: (delta.checked_div(hiword(WHEEL_DELTA)).unwrap_or_default()) as i64,
}),
}
}

Ok(WM_MOUSEHWHEEL) => {
let delta = get_delta(lpdata);
Some(EventType::Wheel {
delta_x: (delta.checked_div(hiword(WHEEL_DELTA)).unwrap_or_default()) as i64,
delta_y: 0,
})
match get_simulated(lpdata) {
true => Some(EventType::SimulatedWheel {
delta_x: (delta.checked_div(hiword(WHEEL_DELTA)).unwrap_or_default()) as i64,
delta_y: 0,
}),

false => Some(EventType::Wheel {
delta_x: (delta.checked_div(hiword(WHEEL_DELTA)).unwrap_or_default()) as i64,
delta_y: 0,
}),
}
}

_ => None,
}
}
Expand All @@ -121,8 +198,8 @@ pub enum HookError {
}

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

if hook == 0 {
let error = GetLastError();
Expand All @@ -133,8 +210,8 @@ pub unsafe fn set_key_hook(callback: RawCallback) -> Result<(), HookError> {
}

pub unsafe fn set_mouse_hook(callback: RawCallback) -> Result<(), HookError> {
let hmod: HLOCAL = 0;
let hook = SetWindowsHookExA(WH_MOUSE_LL, Some(callback), hmod, 0);
let hmod: HLOCAL = std::ptr::null_mut();
let hook = SetWindowsHookExA(WH_MOUSE_LL, Some(callback), hmod as isize, 0);
if hook == 0 {
let error = GetLastError();
return Err(HookError::Mouse(error));
Expand Down
8 changes: 5 additions & 3 deletions src/windows/keyboard.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
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 windows_sys::Win32::Foundation::LPARAM;
use windows_sys::Win32::System::Threading::{AttachThreadInput, GetCurrentThreadId};
use windows_sys::Win32::UI::Input::KeyboardAndMouse::{
Expand All @@ -11,6 +9,10 @@ use windows_sys::Win32::UI::Input::KeyboardAndMouse::{
use windows_sys::Win32::UI::TextServices::HKL;
use windows_sys::Win32::UI::WindowsAndMessaging::{GetForegroundWindow, GetWindowThreadProcessId};

use crate::rdev::{EventType, Key, KeyboardState};
use crate::windows::common::{get_code, get_scan_code, FALSE, TRUE};
use crate::windows::keycodes::code_from_key;

const VK_SHIFT_: usize = VK_SHIFT as usize;
const VK_CAPITAL_: usize = VK_CAPITAL as usize;
const VK_LSHIFT_: usize = VK_LSHIFT as usize;
Expand Down
4 changes: 4 additions & 0 deletions src/windows/keycodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ decl_keycodes! {
VolumeMute, 173,
VolumeDown, 174,
VolumeUp, 175,
NextTrack, 0xB0,
PrevTrack, 0xB1,
StopTrack, 0xB2,
PlayPauseTrack, 0xB3,
Home, 36,
LeftArrow, 37,
MetaLeft, 91,
Expand Down
Loading