modesetting/
modesetting.rs1use std::{
2 borrow::Cow,
3 env,
4 ffi::{CStr, CString},
5};
6
7use linux_drm::{
8 event::{DrmEvent, GenericDrmEvent},
9 modeset::{
10 CardResources, ConnectionState, ConnectorId, ConnectorState, CrtcId, DumbBuffer,
11 DumbBufferRequest, ModeInfo, PageFlipFlags,
12 },
13 result::Error,
14 Card, DeviceCap,
15};
16
17fn main() -> std::io::Result<()> {
18 let card_path = card_path();
19 let mut card = Card::open(card_path).map_err(map_init_err)?;
20 card.become_master().map_err(map_err)?;
21
22 {
23 let name = card.driver_name().map_err(map_err)?;
24 let name = String::from_utf8_lossy(&name);
25 println!("Driver name: {name}");
26 }
27
28 if card
29 .get_device_cap(DeviceCap::DumbBuffer)
30 .map_err(map_err)?
31 == 0
32 {
33 return Err(std::io::Error::other(
34 "device does not support 'dumb buffers'",
35 ));
36 } else {
37 println!("Device supports 'dumb buffers'");
38 }
39
40 display_demo(&mut card).map_err(map_err)
41}
42
43fn card_path<'a>() -> Cow<'a, CStr> {
44 static DEFAULT_PATH: &'static CStr = c"/dev/dri/card0";
45
46 let mut args = env::args();
47 if !args.next().is_some() {
48 return Cow::Borrowed(DEFAULT_PATH);
50 }
51
52 args.next().map_or(Cow::Borrowed(DEFAULT_PATH), |s| {
53 Cow::Owned(CString::new(s).unwrap())
54 })
55}
56
57fn display_demo(card: &mut Card) -> Result<(), Error> {
58 let mut outputs = prepare_outputs(&card)?;
59 for output in &mut outputs {
60 println!("preparing output {output:#?}");
61 let conn = card.connector_state(output.conn_id)?;
62
63 let mode = &output.mode;
64 let mode_name = String::from_utf8_lossy(&mode.name);
65 println!(
66 "{:?} connector uses {mode_name} ({}x{}@{}Hz)",
67 conn.connector_type, mode.hdisplay, mode.vdisplay, mode.vrefresh,
68 );
69
70 let rows = output.db.height() as usize;
71 let pitch = output.db.pitch() as usize;
72 let data = output.db.buffer_mut();
73 for i in 0..rows {
74 if (i % 8) > 3 {
75 let row = &mut data[(i * pitch)..(i * pitch) + pitch];
76 row.fill(0xff);
77 }
78 }
79
80 println!(
81 "configuring CRTC {:?} for framebuffer {:?} and mode {mode_name} on connection {:?}",
82 output.crtc_id,
83 output.db.framebuffer_id(),
84 conn.id
85 );
86 card.set_crtc_dumb_buffer(output.crtc_id, &output.db, mode, &[output.conn_id])?;
87 card.crtc_page_flip_dumb_buffer(output.crtc_id, &output.db, PageFlipFlags::EVENT)?;
88 }
89
90 let mut evt_buf = vec![0_u8; 1024];
91 loop {
92 println!("waiting for events (send SIGINT to exit)");
93 for evt in card.read_events(&mut evt_buf)? {
94 println!("event {evt:?}");
95 match evt {
96 DrmEvent::Generic(GenericDrmEvent::FlipComplete(_)) => {
97 }
100 _ => {
101 }
103 }
104 }
105 }
106}
107
108fn prepare_outputs(card: &Card) -> Result<Vec<Output>, Error> {
109 let resources = card.resources()?;
110 let mut outputs = Vec::<Output>::new();
111
112 for id in resources.connector_ids.iter().copied() {
113 let conn = card.connector_state(id)?;
114 if conn.connection_state != ConnectionState::Connected {
115 println!("ignoring unconnected connector {id:?}");
116 continue;
117 }
118 if conn.current_encoder_id.0 == 0 {
119 println!("ignoring encoderless connector {id:?}");
120 continue;
121 }
122 if conn.modes.len() == 0 {
123 println!("ignoring modeless connector {id:?}");
124 continue;
125 }
126
127 let output = prepare_output(card, conn, &resources)?;
128 outputs.push(output);
129 }
130
131 Ok(outputs)
132}
133
134fn prepare_output(
135 card: &Card,
136 conn: ConnectorState,
137 resources: &CardResources,
138) -> Result<Output, Error> {
139 if conn.current_encoder_id.0 == 0 {
140 return Err(Error::NotSupported);
145 }
146 let _ = resources; let enc = card.encoder_state(conn.current_encoder_id)?;
149 let crtc_id = enc.current_crtc_id;
150 let crtc = card.crtc_state(crtc_id)?;
151 let mode = crtc.mode;
152 let db = card.create_dumb_buffer(DumbBufferRequest {
153 width: mode.hdisplay as u32,
154 height: mode.vdisplay as u32,
155 depth: 24,
156 bpp: 32,
157 })?;
158 Ok(Output {
159 conn_id: conn.id,
160 crtc_id,
161 mode,
162 db,
163 })
164}
165
166#[derive(Debug)]
167struct Output {
168 db: DumbBuffer,
169 mode: ModeInfo,
170 conn_id: ConnectorId,
171 crtc_id: CrtcId,
172}
173
174fn map_init_err(e: linux_drm::result::InitError) -> std::io::Error {
175 let e: linux_io::result::Error = e.into();
176 e.into_std_io_error()
177}
178
179fn map_err(e: linux_drm::result::Error) -> std::io::Error {
180 let e: linux_io::result::Error = e.into();
181 e.into_std_io_error()
182}