1use std::{
2 borrow::Cow,
3 collections::{BTreeMap, HashMap},
4 env,
5 ffi::{CStr, CString},
6};
7
8use linux_drm::{
9 modeset::{ModeProp, ObjectId, PropertyId, PropertyType},
10 result::Error,
11 Card, ClientCap, DeviceCap,
12};
13
14fn main() -> std::io::Result<()> {
15 let card_path = card_path();
16 let mut card = Card::open(card_path).map_err(map_init_err)?;
17
18 {
19 let name = card.driver_name().map_err(map_err)?;
20 let name = String::from_utf8_lossy(&name);
21 println!("Driver name: {name}");
22 }
23
24 if card
25 .get_device_cap(DeviceCap::DumbBuffer)
26 .map_err(map_err)?
27 == 0
28 {
29 return Err(std::io::Error::other(
30 "device does not support 'dumb buffers'",
31 ));
32 }
33
34 card.set_client_cap(ClientCap::UniversalPlanes, 1)
35 .map_err(map_err)?;
36 card.set_client_cap(ClientCap::Atomic, 1).map_err(map_err)?;
37
38 show_properties(&card).map_err(map_err)
39}
40
41fn card_path<'a>() -> Cow<'a, CStr> {
42 static DEFAULT_PATH: &'static CStr = c"/dev/dri/card0";
43
44 let mut args = env::args();
45 if !args.next().is_some() {
46 return Cow::Borrowed(DEFAULT_PATH);
48 }
49
50 args.next().map_or(Cow::Borrowed(DEFAULT_PATH), |s| {
51 Cow::Owned(CString::new(s).unwrap())
52 })
53}
54
55fn show_properties(card: &Card) -> Result<(), Error> {
56 let res = card.resources()?;
57
58 let mut prop_meta = HashMap::<u32, PropertyMeta>::new();
59
60 println!("");
61
62 for conn_id in res.connector_ids {
63 println!("Connector #{conn_id:?}:");
64 if let Err(e) = show_object_property_list(conn_id, &mut prop_meta, card) {
65 println!(" Error: {e:?}");
66 }
67 println!("");
68 }
69
70 for enc_id in res.encoder_ids {
71 println!("Encoder #{enc_id:?}:");
72 if let Err(e) = show_object_property_list(enc_id, &mut prop_meta, card) {
73 println!(" Error: {e:?}");
74 }
75 println!("");
76 }
77
78 for crtc_id in res.crtc_ids {
79 println!("CRTC #{crtc_id:?}:");
80 if let Err(e) = show_object_property_list(crtc_id, &mut prop_meta, card) {
81 println!(" Error: {e:?}");
82 }
83 println!("");
84 }
85
86 for fb_id in res.fb_ids {
87 println!("Framebuffer #{fb_id:?}:");
88 if let Err(e) = show_object_property_list(fb_id, &mut prop_meta, card) {
89 println!(" Error: {e:?}");
90 }
91 println!("");
92 }
93
94 for plane_id in res.plane_ids {
95 println!("Plane #{plane_id:?}:");
96 if let Err(e) = show_object_property_list(plane_id, &mut prop_meta, card) {
97 println!(" Error: {e:?}");
98 }
99 println!("");
100 }
101
102 Ok(())
103}
104
105fn show_object_property_list(
106 id: impl Into<ObjectId>,
107 prop_meta: &mut HashMap<u32, PropertyMeta>,
108 card: &Card,
109) -> Result<(), Error> {
110 let props = card.object_properties(id)?;
111 show_property_list(&props, prop_meta, card)?;
112 Ok(())
113}
114
115fn show_property_list(
116 props: &[ModeProp],
117 prop_meta: &mut HashMap<u32, PropertyMeta>,
118 card: &Card,
119) -> Result<(), Error> {
120 for prop in props {
121 let meta = property_meta(prop.prop_id, prop_meta, card)?;
122 print!(" {}: ", meta.name);
123 match meta.typ {
124 PropertyType::Enum => {
125 if let Some(name) = meta.enum_names.get(&prop.value) {
126 println!("{name}")
127 } else {
128 println!("out-of-range value {}", prop.value)
129 }
130 }
131 PropertyType::Bitmask => {
132 let v = prop.value;
133 let mut valid = 0_u64;
134 let mut printed_one = false;
135 for (bit, name) in meta.enum_names.iter() {
136 let mask = 1_u64 << *bit;
137 if (v & mask) != 0 {
138 if printed_one {
139 print!(" | ");
140 }
141 print!("{name}");
142 printed_one = true;
143 }
144 valid |= mask;
145 }
146 let invalid = v & !valid;
147 if invalid != 0 {
148 if printed_one {
149 print!(" | ");
150 }
151 print!("{invalid:#x}");
152 }
153 println!("");
154 }
155 PropertyType::Blob => {
156 println!("blob #{}", prop.value)
157 }
158 _ => println!("{}", prop.value),
159 }
160 }
161 Ok(())
162}
163
164fn property_meta<'a, 'card>(
165 prop_id: PropertyId,
166 prop_meta: &'a mut HashMap<u32, PropertyMeta>,
167 card: &Card,
168) -> Result<&'a PropertyMeta, Error> {
169 Ok(prop_meta.entry(prop_id.0).or_insert_with(|| {
170 card.property_meta(prop_id)
171 .map(|meta| {
172 let mut enum_names = BTreeMap::new();
173 for member in meta.enum_members().unwrap() {
174 enum_names.insert(member.value(), member.name().to_string());
175 }
176 PropertyMeta {
177 name: meta.name().to_string(),
178 typ: meta.property_type(),
179 immutable: meta.is_immutable(),
180 values: meta.values().unwrap(),
181 enum_names,
182 }
183 })
184 .unwrap_or(PropertyMeta {
185 name: String::from("<unknown>"),
186 typ: PropertyType::Unknown,
187 immutable: true,
188 values: Vec::new(),
189 enum_names: BTreeMap::new(),
190 })
191 }))
192}
193
194#[derive(Debug)]
195pub struct PropertyMeta {
196 pub name: String,
197 pub typ: PropertyType,
198 pub immutable: bool,
199 pub values: Vec<u64>,
200 pub enum_names: BTreeMap<u64, String>,
201}
202
203fn map_init_err(e: linux_drm::result::InitError) -> std::io::Error {
204 let e: linux_io::result::Error = e.into();
205 e.into_std_io_error()
206}
207
208fn map_err(e: linux_drm::result::Error) -> std::io::Error {
209 let e: linux_io::result::Error = e.into();
210 e.into_std_io_error()
211}