1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
//!
//! # drm-rs
//!
//! drm-rs
//!
//! This library is a safe interface to the Direct Rendering Manager API found on various operating systems.
//!
//! This library is currently a work in progress.
//!
//! ## Usage
//!
//! The user is expected to implement their own functionality for opening and
//! accessing the file descriptor of the device. Here we create a small wrapper
//! around `File` and implement `AsRawFd`, `drm::Device`, and
//! `drm::control::Device`:
//!
//! ```rust,ignore
//! extern crate drm;
//!
//! use std::fs::{OpenOptions, File};
//! use std::os::unix::io::{AsRawFd, RawFd};
//!
//! use drm::Device as BasicDevice;
//! use drm::control::Device as ControlDevice;
//!
//! // The drm crate does not provide a method of opening the device.
//! // It is expected to be implemented by the user.
//! struct Card(File);
//!
//! // Required to implement drm::Device
//! impl AsRawFd for Card {
//!     fn as_raw_fd(&self) -> RawFd { self.0.as_raw_fd() }
//! }
//!
//! // Required to implement drm::control::Device
//! impl BasicDevice for Card { }
//!
//! // Allows modesetting functionality to be performed.
//! impl ControlDevice for Card { }
//!
//! ```
//!
//! Assuming the program used the above wrapper, the user now opens the card:
//!
//! ```rust,ignore
//! // Open the device (usually located at /dev/dri/*) with rw access.
//! let mut options = OpenOptions::new();
//! options.read(true);
//! options.write(true);
//! let file = options.open("/dev/dri/card0");
//! let card = Card(file);
//! ```
//!
//! Now we can check out what resources are available:
//!
//! ```rust,ignore
//! // Get a set of all modesetting resource handles (excluding planes):
//! let res_handles = card.resource_handles().unwrap();
//!
//! // Print all connector information
//! for &con in res_handles.connectors() {
//!     let info = card.resource_info(con).unwrap();
//!
//!     println!("{:#?}")
//! }
//!
//! // Print all CRTC information
//! for &crtc in res_handles.crtcs() {
//!     let info = card.resource_info(crtc).unwrap();
//!
//!     println!("{:#?}")
//! }
//! ```
//!
//! You'll also want to find a suitable mode:
//!
//! ```rust,ignore
//! // Assuming we found a good connector and loaded the info into `connector_info`
//! let &mode = connector_info.modes().iter(); // Search until you find one you want.
//! ```
//!
//! Once you find a suitable connector and CRTC, it's time to create a framebuffer.
//! Here we use a simple dumbbuffer as the backend:'
//!
//! ```rust,ignore
//! // Create a DB of size 1920x1080
//! let db = dumbbuffer::DumbBuffer::create_from_device(&card, (1920, 1080), 32)
//!     .expect("Could not create dumb buffer");
//!
//! // Map it and grey it out.
//! let mut map = db.map(&card).expect("Could not map dumbbuffer");
//! for mut b in map.as_mut() {
//!     *b = 128; // Grey
//! }
//!
//! let fb_info = framebuffer::Info::create_from_buffer(&card, &db)
//! let fb_handle = fb_info.handle();
//! ```
//!
//! Now we can apply the framebuffer onto the CRTC's internal plane, and connect it
//! to a connector with the proper mode:
//!
//! ```rust,ignore
//! // Assuming `crtc` is a crtc handle and `con` is a connector handle
//! crtc.set_on_device(&card, fb_handle, &[con], (0, 0), Some(mode))
//!     .expect("Could not set Crtc");
//! ```
//!
//! The contents of the dumb buffer will now appear onto the screen.

#![warn(missing_docs)]

extern crate drm_sys;

#[macro_use]
extern crate nix;

#[macro_use]
extern crate derive_more;

#[macro_use]
extern crate error_chain;

#[macro_use]
pub mod ffi;
pub mod result;

pub mod control;
pub mod buffer;

use std::os::unix::io::AsRawFd;
use result::Result;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, From, Into)]
/// A token unique to the process that determines who opened the device.
///
/// This token can be sent to another process that acts as the DRM Master and
/// then authenticated to give extra privileges.
pub struct AuthToken(u32);

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// Capabilities that the process understands.
///
/// These can be used to tell the DRM device what capabilities the process can
/// use.
pub enum ClientCapability {
    /// Stereoscopic 3D Support
    Stereo3D = ffi::DRM_CLIENT_CAP_STEREO_3D as isize,
    /// Universal plane access and api
    UniversalPlanes = ffi::DRM_CLIENT_CAP_UNIVERSAL_PLANES as isize,
    /// Atomic modesetting support
    Atomic = ffi::DRM_CLIENT_CAP_ATOMIC as isize,
}

/// A trait for all DRM devices.
pub trait Device: AsRawFd {
    /// Generates and returns a magic token unique to the current process.
    ///
    /// This token can be used to authenticate with the DRM Master.
    fn get_auth_token(&self) -> Result<AuthToken> {
        let token = {
            let mut raw: ffi::drm_auth_t = Default::default();
            unsafe { ffi::ioctl_get_magic(self.as_raw_fd(), &mut raw)? };
            raw.magic
        };

        Ok(AuthToken(token))
    }

    /// Tells the DRM device whether we understand or do not understand a
    /// particular capability.
    ///
    /// Some features, such as atomic modesetting, require informing the device
    /// that the process can use such features before it will expose them.
    fn set_client_cap(&self, cap: ClientCapability, set: bool) -> Result<()> {
        let mut raw = {
            let mut raw: ffi::drm_set_client_cap = Default::default();
            raw.capability = cap as u64;
            raw.value = set as u64;
            raw
        };

        unsafe { ffi::ioctl_set_client_cap(self.as_raw_fd(), &mut raw)? };

        Ok(())
    }

    /// Attempts to acquire the DRM Master lock.
    fn set_master(&self) -> Result<()> {
        unsafe { ffi::ioctl_set_master(self.as_raw_fd())? };

        Ok(())
    }

    /// Attempts to release the DRM Master lock.
    fn drop_master(&self) -> Result<()> {
        unsafe { ffi::ioctl_drop_master(self.as_raw_fd())? };

        Ok(())
    }
}

#[allow(non_camel_case_types)]
/// Signed point
pub type iPoint = (i32, i32);
#[allow(non_camel_case_types)]
/// Unsigned point
pub type uPoint = (u32, u32);
/// Dimensions (width, height)
pub type Dimensions = (u32, u32);
#[allow(non_camel_case_types)]
/// Rectangle with a signed upper left corner
pub type iRect = (iPoint, Dimensions);
#[allow(non_camel_case_types)]
/// Rectangle with an unsigned upper left corner
pub type uRect = (uPoint, Dimensions);