Crate rdev[][src]

Simple library to listen and send events to keyboard and mouse on MacOS, Windows and Linux (x11).

You can also check out Enigo which is another crate which helped me write this one.

This crate is so far a pet project for me to understand the rust ecosystem.

Listening to global events

use rdev::{listen, Event};

// This will block.
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 => (),
    }
}

Sending some events

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);
        }
    }
    // Let ths OS catchup (at least 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,
});

Main structs

Event

In order to detect what a user types, we need to plug to the OS level management of keyboard state (modifiers like shift, ctrl, but also dead keys if they exist).

EventType corresponds to a physical event, corresponding to QWERTY layout Event corresponds to an actual event that was received and Event.name reflects what key was interpreted by the OS at that time, it will respect the layout.

/// When events arrive from the system we can add some information
/// time is when the event was received.
#[derive(Debug)]
pub struct Event {
    pub time: SystemTime,
    pub name: Option<String>,
    pub event_type: EventType,
}

Be careful, Event::name, might be None, but also String::from(“”), and might contain not displayable unicode characters. We send exactly what the OS sends us so do some sanity checking before using it. Caveat: Dead keys don’t function yet on Linux

EventType

In order to manage different OS, the current EventType choices is a mix&match to account for all possible events. There is a safe mechanism to detect events no matter what, which are the Unknown() variant of the enum which will contain some OS specific value. Also not that not all keys are mapped to an OS code, so simulate might fail if you try to send an unmapped key. Sending Unknown() variants will always work (the OS might still reject it).

/// In order to manage different OS, the current EventType choices is a mix&match
/// to account for all possible events.
#[derive(Debug)]
pub enum EventType {
    /// The keys correspond to a standard qwerty layout, they don't correspond
    /// To the actual letter a user would use, that requires some layout logic to be added.
    KeyPress(Key),
    KeyRelease(Key),
    /// Some mouse will have more than 3 buttons, these are not defined, and different OS will
    /// give different Unknown code.
    ButtonPress(Button),
    ButtonRelease(Button),
    /// Values in pixels
    MouseMove {
        x: f64,
        y: f64,
    },
    /// Note: On Linux, there is no actual delta the actual values are ignored for delta_x
    /// and we only look at the sign of delta_y to simulate wheelup or wheeldown.
    Wheel {
        delta_x: i64,
        delta_y: i64,
    },
}

OS Specificities

For now the code only works for Linux (X11), MacOS and Windows. On MacOS, the listen loop needs to be the primary app (no fork before) and needs to have accessibility settings enabled (Terminal added in System Preferences > Security & Privacy > Privacy > Accessibility).

Getting the main screen size

use rdev::{display_size};

let (w, h) = display_size().unwrap();
assert!(w > 0);
assert!(h > 0);

Keyboard state

We can define a dummy Keyboard, that we will use to detect what kind of EventType trigger some String. We get the currently used layout for now ! Caveat : This is layout dependent. If your app needs to support layout switching don’t use this ! Caveat: On Linux, the dead keys mechanism is not implemented. Caveat: Only shift and dead keys are implemented, Alt+unicode code on windows won’t work.

use rdev::{Keyboard, EventType, Key, KeyboardState};

let mut keyboard = Keyboard::new().unwrap();
let string = keyboard.add(&EventType::KeyPress(Key::KeyS));
// string == Some("s")

Grabbing global events. (Requires unstable_grab feature)

In the callback, returning None ignores the event and returning the event let’s it pass. There is no modification of the event possible here. Caveat: On MacOS, you require the grab loop needs to be the primary app (no fork before) and need to have accessibility settings enabled. Not implemented on Linux, you will always receive an error.

Serialization

Serialization and deserialization. (Requires serialize feature).

Structs

Event

When events arrive from the OS they get some additional information added from EventType, which is the time when this event was received, and the name Option which contains what characters should be emmitted from that event. This relies on the OS layout and keyboard state machinery. Caveat: Dead keys don’t function on Linux(X11) yet. You will receive None for a dead key, and the raw letter instead of accentuated letter.

Keyboard
SimulateError

Marking an error when we tried to simulate and event

Enums

Button

Standard mouse buttons Some mice have more than 3 buttons. These are not defined, and different OSs will give different Button::Unknown values.

DisplayError

Errors that occur when trying to get display size.

EventType

In order to manage different OSs, the current EventType choices are a mix and match to account for all possible events.

GrabError

Errors that occur when trying to grab OS events. Be careful on Mac, not setting accessibility does not cause an error it justs ignores events.

Key

Key names based on physical location on the device Merge Option(MacOS) and Alt(Windows, Linux) into Alt Merge Windows (Windows), Meta(Linux), Command(MacOS) into Meta Characters based on Qwerty layout, don’t use this for characters as it WILL depend on the layout. Use Event.name instead. Key modifiers gives those keys a different value too. Careful, on Windows KpReturn does not exist, it’ s strictly equivalent to Return, also Keypad keys get modified if NumLock is Off and ARE pagedown and so on.

ListenError

Errors that occur when trying to capture OS events. Be careful on Mac, not setting accessibility does not cause an error it justs ignores events.

Traits

KeyboardState

We can define a dummy Keyboard, that we will use to detect what kind of EventType trigger some String. We get the currently used layout for now ! Caveat : This is layout dependent. If your app needs to support layout switching don’t use this ! Caveat: On Linux, the dead keys mechanism is not implemented. Caveat: Only shift and dead keys are implemented, Alt+unicode code on windows won’t work.

Functions

display_size

Returns the size in pixels of the main screen. This is useful to use with x, y from MouseMove Event.

listen

Listening to global events. Caveat: On MacOS, you require the listen loop needs to be the primary app (no fork before) and need to have accessibility settings enabled.

simulate

Sending some events

Type Definitions

GrabCallback

Callback type to send to grab function.