展开描述
用于在 MacOS、Windows 和 Linux(x11)上监听和向键盘、鼠标发送事件的简单库。
你也可以查看 Enigo ,这是另一个帮助我编写本 crate 的 crate。
这个 crate 至今仍是我用来理解 rust 生态系统的练手项目。
§监听全局事件
use rdev::{listen, Event};
// 这将阻塞。if let Err(error) = listen(callback) {
println!("Error: {:?}", error)
}
fn callback(event: Event) {
println!("My callback {:?}", event);
match event.name {
Some(string) => println!("User wrote {:?}", string),
None => (),
}
}§操作系统注意事项:
在使用 listen 函数时,请注意以下事项:
§Mac 系统
运行阻塞 listen 函数(循环)的进程必须是父进程(之前不能有 fork)。
进程需要被授予访问无障碍 API(Accessibility API)的权限(例如:如果你在 Terminal.app 内运行你的进程,那么需要将 Terminal.app 添加到 系统偏好设置 > 安全性与隐私 > 隐私 > 辅助功能 中)。
如果进程没有被授予访问无障碍 API 的权限,MacOS 将静默地忽略 rdev 的 listen 回调,不会被事件触发,也不会产生错误。
§Linux
listen 函数使用 X11 API,因此在 Wayland 或 Linux 内核虚拟控制台中无法工作。
§发送一些事件
use rdev::{simulate, Button, EventType, Key, SimulateError};
use std::{thread, time};
fn send(event_type: &EventType) {
let delay = time::Duration::from_millis(20);
match simulate(event_type) {
Ok(()) => (),
Err(SimulateError) => {
println!("We could not send {:?}", event_type);
}
}
// 让操作系统跟上(至少在 MacOS 上需要)thread::sleep(delay);
}
send(&EventType::KeyPress(Key::KeyS));
send(&EventType::KeyRelease(Key::KeyS));
send(&EventType::MouseMove { x: 0.0, y: 0.0 });
send(&EventType::MouseMove { x: 400.0, y: 400.0 });
send(&EventType::ButtonPress(Button::Left));
send(&EventType::ButtonRelease(Button::Right));
send(&EventType::Wheel {
delta_x: 0,
delta_y: 1,
});§主要结构体
§Event
为了检测用户输入的内容,我们需要接入操作系统层面的键盘状态管理(如 shift、ctrl 等修饰键,以及死键(如果存在))。
EventType 对应于一个物理事件,对应于 QWERTY 布局;
Event 对应于实际接收到的事件,Event.name 反映操作系统当时所解释的按键,它会遵循布局。
/// 当事件从系统到达时,我们可以添加一些信息
/// time 是事件被接收到的时间。
#[derive(Debug)]
pub struct Event {
pub time: SystemTime,
pub name: Option<String>,
pub event_type: EventType,
}请注意,Event::name 可能是 None,也可能是 String::from(""),并且可能包含不可显示的 Unicode 字符。我们原样发送操作系统传给我们的内容,因此在使用前请做一些合理性检查。注意:Linux 上死键功能尚未实现。
§EventType
为了兼容不同的操作系统,当前的 EventType 选择是混搭的,以涵盖所有可能的事件。 无论何种情况,都有一种安全机制来检测事件,即枚举中的 Unknown() 变体,它会包含一些操作系统特定的值。 另请注意,并非所有键都映射到了操作系统代码,因此如果你尝试发送一个未映射的键,simulate 可能会失败。 发送 Unknown() 变体总是可以工作的(操作系统仍可能会拒绝)。
/// 为了兼容不同的操作系统,当前的 EventType 选择是混搭的,
/// 以涵盖所有可能的事件。
#[derive(Debug)]
pub enum EventType {
/// 按键对应于标准 qwerty 布局,它们不对应
/// 用户实际使用的字母,这需要添加一些布局逻辑。
KeyPress(Key),
KeyRelease(Key),
/// 某些鼠标具有 3 个以上的按键,这些按键未定义,不同的操作系统会给出不同的 Unknown 编码。
ButtonPress(Button),
ButtonRelease(Button),
/// 像素值
MouseMove {
x: f64,
y: f64,
},
/// 注意:在 Linux 上,没有实际的 delta 值,delta_x 的实际值被忽略,
/// 我们仅查看 delta_y 的符号来模拟滚轮向上或向下。
Wheel {
delta_x: i64,
delta_y: i64,
},
}§获取主屏幕尺寸
use rdev::{display_size};
let (w, h) = display_size().unwrap();
assert!(w > 0);
assert!(h > 0);§键盘状态
我们可以定义一个虚拟的 Keyboard,用于检测哪种 EventType 会触发某个字符串。目前只取当前使用的键盘布局!注意:这是布局相关的。如果你的应用需要支持布局切换,请不要使用此功能!注意:Linux 上死键机制尚未实现。注意:目前仅实现了 Shift 和死键,Windows 上的 Alt + Unicode 码点输入方式无法工作。
use rdev::{Keyboard, EventType, Key, KeyboardState};
let mut keyboard = Keyboard::new().unwrap();
let string = keyboard.add(&EventType::KeyPress(Key::KeyS));
// string == Some("s")§抓取全局事件(需要 unstable_grab 特性)
使用 unstable_grab 特性安装本库会添加 grab 函数,该函数可以挂接到全局输入设备事件流。
通过向此函数提供一个回调,你可以在所有键盘和鼠标事件被传递给应用程序/窗口管理器之前拦截它们。
在回调中,返回 None 表示忽略该事件,返回事件则让其通过。
目前还无法对事件进行修改。
注意:这里的 unstable(不稳定)一词特指 grab API 是不稳定的,可能会发生变化。
#[cfg(feature = "unstable_grab")]
use rdev::{grab, Event, EventType, Key};
#[cfg(feature = "unstable_grab")]
let callback = |event: Event| -> Option<Event> {
if let EventType::KeyPress(Key::CapsLock) = event.event_type {
println!("Consuming and cancelling CapsLock");
None // CapsLock 现在实际上已被禁用}
else { Some(event) }
};
// 这将阻塞。#[cfg(feature = "unstable_grab")]
if let Err(error) = grab(callback) {
println!("Error: {:?}", error)
}§操作系统注意事项:
在使用 listen 和/或 grab 函数时,请注意以下事项:
§Mac 系统
运行阻塞 grab 函数(循环)的进程必须是父进程(之前不能有 fork)。
进程需要被授予访问无障碍 API(Accessibility API)的权限(例如:如果你在 Terminal.app 内运行你的进程,那么需要将 Terminal.app 添加到 系统偏好设置 > 安全性与隐私 > 隐私 > 辅助功能 中)。
如果进程没有被授予无障碍 API 的访问权限,grab 调用将失败并返回 EventTapError(至少在 MacOS 10.15 中如此,可能其他版本也如此)。
§Linux
grab 函数使用 evdev 库来拦截事件,因此它可以在 X11 和 Wayland 下工作。
为了使其生效,运行 listen 或 grab 循环的进程需要以 root 用户身份运行(不推荐),
或者以属于 input 组的用户身份运行(推荐)。
注意:在某些发行版上,用于 evdev 访问的组名叫做 plugdev;在某些系统上,这两个组可能同时存在。
如有疑问,请将你的用户同时添加到这两个组(如果它们都存在)。
§序列化
如果使用 serialize 特性安装本库,那么 listen 和 grab 函数返回的事件数据可以使用 Serde 进行序列化和反序列化。
结构体§
- Event
- 当事件从操作系统到达时,会根据 EventType 添加一些附加信息:事件被接收的时间,以及一个 name Option,其中包含该事件应产生的字符。这些信息依赖于操作系统的布局和键盘状态机制。注意:Linux(X11)上死键目前还无法工作——遇到死键时会收到 None,并返回原始字母而非带音标的字母。
- Keyboard
- Simulate
Error - 表示我们尝试模拟一个事件时发生的错误
枚举§
- Button
- 标准鼠标按钮。有些鼠标有超过 3 个按钮。这些按钮未定义,不同的操作系统会给出不同的
Button::Unknownvalues. - Display
Error - 获取显示器尺寸时可能发生的错误。
- Event
Type - 为了兼容不同的操作系统,当前的 EventType 选项是混合搭配的,以涵盖所有可能的事件。
- Grab
Error - 尝试抓取操作系统事件时可能发生的错误。注意在 Mac 上,不设置辅助功能不会导致错误,只是会忽略事件。
- Key
- 键名基于设备上的物理位置。将 Option(MacOS)和 Alt(Windows、Linux)合并为 Alt;将 Windows(Windows)、Meta(Linux)、Command(MacOS)合并为 Meta。字符基于 QWERTY 布局,不要直接将其作为字符使用,因为结果会随布局变化。如需真实字符,请使用 Event.name。修饰键也会改变这些键的值。注意,Windows 上不存在 KpReturn,它严格等同于 Return。此外,数字小键盘的键在 NumLock 关闭时会被改写,等同于 PageDown 等功能键。
- Listen
Error - 尝试捕获操作系统事件时可能发生的错误。注意在 Mac 上,不设置辅助功能不会导致错误,只是会忽略事件。
Trait§
- Keyboard
State - 我们可以定义一个虚拟的 Keyboard,用于检测哪种 EventType 会触发某个字符串。目前只取当前使用的键盘布局!注意:这是布局相关的。如果你的应用需要支持布局切换,请不要使用此功能!注意:Linux 上死键机制尚未实现。注意:目前仅实现了 Shift 和死键,Windows 上的 Alt + Unicode 码点输入方式无法工作。
函数§
- display_
size - 返回主屏幕的像素尺寸。可以与 MouseMove 事件中的 x、y 一起使用。
- listen
- 监听全局事件。注意:在 MacOS 上,listen 循环必须作为主应用运行(在此之前不能 fork),并且需要开启辅助功能。
- simulate
- 发送一些事件
类型别名§
- Grab
Callback - 传给 grab 函数的回调类型。