跳到主要内容

rdev 包

搜索

Crate rdev 

Source
展开描述

用于在 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 下工作。 为了使其生效,运行 listengrab 循环的进程需要以 root 用户身份运行(不推荐), 或者以属于 input 组的用户身份运行(推荐)。 注意:在某些发行版上,用于 evdev 访问的组名叫做 plugdev;在某些系统上,这两个组可能同时存在。 如有疑问,请将你的用户同时添加到这两个组(如果它们都存在)。

§序列化

如果使用 serialize 特性安装本库,那么 listengrab 函数返回的事件数据可以使用 Serde 进行序列化和反序列化。

结构体§

Event
当事件从操作系统到达时,会根据 EventType 添加一些附加信息:事件被接收的时间,以及一个 name Option,其中包含该事件应产生的字符。这些信息依赖于操作系统的布局和键盘状态机制。注意:Linux(X11)上死键目前还无法工作——遇到死键时会收到 None,并返回原始字母而非带音标的字母。
Keyboard
SimulateError
表示我们尝试模拟一个事件时发生的错误

枚举§

Button
标准鼠标按钮。有些鼠标有超过 3 个按钮。这些按钮未定义,不同的操作系统会给出不同的 Button::Unknown values.
DisplayError
获取显示器尺寸时可能发生的错误。
EventType
为了兼容不同的操作系统,当前的 EventType 选项是混合搭配的,以涵盖所有可能的事件。
GrabError
尝试抓取操作系统事件时可能发生的错误。注意在 Mac 上,不设置辅助功能不会导致错误,只是会忽略事件。
Key
键名基于设备上的物理位置。将 Option(MacOS)和 Alt(Windows、Linux)合并为 Alt;将 Windows(Windows)、Meta(Linux)、Command(MacOS)合并为 Meta。字符基于 QWERTY 布局,不要直接将其作为字符使用,因为结果会随布局变化。如需真实字符,请使用 Event.name。修饰键也会改变这些键的值。注意,Windows 上不存在 KpReturn,它严格等同于 Return。此外,数字小键盘的键在 NumLock 关闭时会被改写,等同于 PageDown 等功能键。
ListenError
尝试捕获操作系统事件时可能发生的错误。注意在 Mac 上,不设置辅助功能不会导致错误,只是会忽略事件。

Trait§

KeyboardState
我们可以定义一个虚拟的 Keyboard,用于检测哪种 EventType 会触发某个字符串。目前只取当前使用的键盘布局!注意:这是布局相关的。如果你的应用需要支持布局切换,请不要使用此功能!注意:Linux 上死键机制尚未实现。注意:目前仅实现了 Shift 和死键,Windows 上的 Alt + Unicode 码点输入方式无法工作。

函数§

display_size
返回主屏幕的像素尺寸。可以与 MouseMove 事件中的 x、y 一起使用。
listen
监听全局事件。注意:在 MacOS 上,listen 循环必须作为主应用运行(在此之前不能 fork),并且需要开启辅助功能。
simulate
发送一些事件

类型别名§

GrabCallback
传给 grab 函数的回调类型。