10 releases

0.8.0 Feb 24, 2023
0.7.3 Feb 24, 2023
0.7.2 May 8, 2021
0.7.1 Apr 25, 2021
0.1.1 Mar 11, 2021

#599 in Unix APIs

36 downloads per month

MIT license

1.5K SLoC



High level bindings to evdi, a program for managing virtual displays on linux.

Warning: This is alpha-quality software. If it breaks something maybe try rebooting your computer.

Evdi doesn't full document the invarients necessary for memory safety, so I'm not confident this library is sound yet.

See also the low-level unsafe bindings evdi-sys, and the docs for libevdi.


High-level bindings to evdi, a library for managing virtual displays on linux.

Tracing and logging

This library emits many tracing events. Logs by libevdi are converted to INFO-level tracing events.

Not thread safe

Evdi is not thread safe, and you cannot block the thread it runs for too long as your real and virtual devices will not receive events.

Alpha quality

This library is alpha quality. If your display starts behaving weirdly, rebooting may help.

I have no idea about the soundness invariants of evdi, so this library isn't safe. You have to call an unsafe function to open a device_node::DeviceNode to represent this.

Basic usage

const AWAIT_MODE_TIMEOUT: Duration = Duration::from_millis(250);
const UPDATE_BUFFER_TIMEOUT: Duration = Duration::from_millis(20);

// If get returns None you need to call DeviceNode::add with superuser permissions or setup the
// kernel module to create a device on module load.
let device = DeviceNode::get().unwrap();

// Replace this with the details of the display you want to emulate
let device_config = DeviceConfig::sample();

let unconnected_handle = device.open()?;
let mut handle = unconnected_handle.connect(&device_config);

// For simplicity don't handle the mode changing after we start
let mode = handle.events.await_mode(AWAIT_MODE_TIMEOUT).await?;

// For simplicity, we only use one buffer. You may want to use more than one buffer so that you
// can send the contents of one buffer while updating another.
let buffer_id = handle.new_buffer(&mode);

loop {
    handle.request_update(buffer_id, UPDATE_BUFFER_TIMEOUT).await?;
    let buf = handle.get_buffer(buffer_id).expect("Buffer exists");
    // Do something with the bytes
    let _bytes = buf.bytes();

Managing device nodes

Creating and removing device nodes requires superuser permissions.

I include the helper binaries evdi_device_add and evdi_device_remove_all that do nothing but call DeviceNode::add and DeviceNode::remove_all so that you can easily manage devices while testing.

For example:

> # (while in the checked out source code of this library)
> cargo build --bin evdi_device_add
> sudo target/debug/evdi_device_add

You will probably want to create your own seperate binaries that manage device nodes so that your users don't need to run your main binary with superuser permissions.

Another alternative is [configuring the kernel module] to create devices when it loads.


  • serde: Derive Serialize & Deserialize for types where it makes sense.


~192K SLoC