Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Smithay/drm-rs/llms.txt

Use this file to discover all available pages before exploring further.

This quickstart walks you through everything you need to go from a blank Rust project to a working program that opens a DRM device, queries the loaded GPU driver, and lists every connected display along with its supported video modes. By the end you will have a solid foundation for building Wayland compositors, display managers, kiosk applications, or any other software that needs direct access to Linux graphics hardware.
1
Add drm-rs to Cargo.toml
2
Open your project’s Cargo.toml and add drm-rs under [dependencies]:
3
[dependencies]
drm = "0.15"
4
That single line is all you need for most use cases. The drm crate re-exports everything required for device access and KMS modesetting. Run cargo build to download and compile the crate before moving on.
5
Implement the Card Wrapper
6
drm-rs does not open device files for you. Instead, you provide any type that implements the standard AsFd trait. The simplest approach is a thin newtype around std::fs::File:
7
use std::fs::File;
use std::os::unix::io::{AsFd, BorrowedFd};

pub struct Card(File);

impl AsFd for Card {
    fn as_fd(&self) -> BorrowedFd<'_> {
        self.0.as_fd()
    }
}

impl Card {
    pub fn open(path: &str) -> Self {
        let mut options = std::fs::OpenOptions::new();
        options.read(true);
        options.write(true);
        Card(options.open(path).unwrap())
    }
}
8
The Card struct wraps a File that points to a DRM device node (e.g. /dev/dri/card0). Both read and write access are required — the kernel rejects ioctls on read-only file descriptors.
9
Implement drm::Device
10
With AsFd in place, implementing drm::Device requires nothing more than an empty impl block:
11
impl drm::Device for Card {}
12
That single line gives your Card type access to driver queries, master-lock management, vblank waits, and capability inspection.
13
Query the Driver
14
You can now open the device and ask the kernel which driver is managing it:
15
fn main() {
    let gpu = Card::open("/dev/dri/card0");

    let driver = gpu.get_driver().unwrap();
    println!("Driver name:    {:?}", driver.name());
    println!("Driver date:    {:?}", driver.date());
    println!("Driver desc:    {:?}", driver.description());
    println!("Driver version: {:?}", driver.version);
}
16
Running this on a typical desktop with an Intel GPU might print something like:
17
Driver name:    "i915"
Driver date:    "20080730"
Driver desc:    "Intel Graphics"
Driver version: (1, 6, 0)
18
List Connectors and Display Modes
19
To query display hardware you need the KMS trait. Add another empty impl block:
20
impl drm::control::Device for Card {}
21
Now call resource_handles() to get a snapshot of all modesetting objects, then iterate over the connectors and print their state and available modes:
22
use drm::control::Device as ControlDevice;

fn main() {
    let card = Card::open("/dev/dri/card0");

    let resources = card.resource_handles().unwrap();

    for &connector_handle in resources.connectors() {
        let info = card.get_connector(connector_handle, false).unwrap();

        println!(
            "Connector {:?}-{}: {:?}",
            info.interface(),
            info.interface_id(),
            info.state()
        );

        if info.state() == drm::control::connector::State::Connected {
            println!("  Modes:");
            for mode in info.modes() {
                println!("    {:?}", mode);
            }
        }
    }
}
23
get_connector returns an Info struct describing the physical connector (HDMI, DisplayPort, VGA, etc.), its connection state, and the list of Mode descriptors reported by the attached monitor’s EDID data.
Your process needs read and write access to /dev/dri/card0. On most Linux distributions the device is owned by the video group — add your user to that group with sudo usermod -aG video $USER and log out/in. Alternatively, run the binary with sudo during development. Without the correct permissions, open() will panic and ioctl calls will return EACCES.

Full Working Example

Putting all the steps together into a single self-contained file:
use std::fs::File;
use std::os::unix::io::{AsFd, BorrowedFd};

// --- Card wrapper -----------------------------------------------------------

pub struct Card(File);

impl AsFd for Card {
    fn as_fd(&self) -> BorrowedFd<'_> {
        self.0.as_fd()
    }
}

impl Card {
    pub fn open(path: &str) -> Self {
        let mut options = std::fs::OpenOptions::new();
        options.read(true);
        options.write(true);
        Card(options.open(path).unwrap())
    }
}

// --- Trait implementations --------------------------------------------------

impl drm::Device for Card {}
impl drm::control::Device for Card {}

// --- Main -------------------------------------------------------------------

fn main() {
    let card = Card::open("/dev/dri/card0");

    // Query the GPU driver
    let driver = card.get_driver().unwrap();
    println!("Driver: {:?} (version {:?})", driver.name(), driver.version);

    // List connectors and their modes
    let resources = card.resource_handles().unwrap();
    for &handle in resources.connectors() {
        let info = card.get_connector(handle, false).unwrap();
        println!(
            "\nConnector {:?}-{}: {:?}",
            info.interface(),
            info.interface_id(),
            info.state()
        );

        if info.state() == drm::control::connector::State::Connected {
            for mode in info.modes() {
                println!(
                    "  {}x{}@{} Hz",
                    mode.size().0,
                    mode.size().1,
                    mode.vrefresh()
                );
            }
        }
    }
}
Save this as src/main.rs in a project that has drm = "0.15" in its Cargo.toml, then run:
cargo run
You should see your GPU driver information followed by a list of every display connector and its supported resolutions.

Build docs developers (and LLMs) love