properties/
properties.rs

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        // skip the executable name
47        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}