libmonitor/mccs/capabilities/
mod.rs

1//! MCCS compliant displays will report their supported capabilities in a string
2//! This crate parses the capability string into structured data.
3
4mod entries;
5mod parsers;
6
7use crate::ddc::ci::DdcOpcode;
8
9use self::{entries::ValueParser, parsers::Cap};
10use super::{features::VcpCapability, DisplayTechnology, Protocol};
11
12use {
13    crate::mccs::{UnknownData, UnknownTag, Version},
14    nom::Finish,
15    std::{fmt, io, str},
16};
17
18/// Parsed display capabilities string.
19#[derive(Debug, Default, Clone)]
20pub struct Capabilities {
21    /// It's not very clear what this field is for.
22    pub protocol: Option<Protocol>,
23    /// The display panel technology.
24    pub ty: Option<DisplayTechnology>,
25    /// The monitor model identifier.
26    pub model: Option<String>,
27    /// List of supported DDCCI commands.
28    pub commands: Vec<DdcOpcode>,
29    /// A value of `1` seems to indicate that the monitor has passed Microsoft's
30    /// Windows Hardware Quality Labs testing.
31    pub ms_whql: Option<u8>,
32    /// Monitor Command Control Set version code.
33    pub mccs_version: Option<Version>,
34    /// Virtual Control Panel feature code descriptors.
35    pub vcp_features: Vec<VcpCapability>,
36    /// Additional unrecognized data from the capability string.
37    pub unknown_tags: Vec<UnknownTag>,
38}
39
40/// Parses a MCCS capability string.
41pub fn parse_capabilities<C: AsRef<[u8]>>(capability_string: C) -> io::Result<Capabilities> {
42    let capability_string = capability_string.as_ref();
43    let entries = Value::parse_capabilities(capability_string);
44
45    let mut caps = Capabilities::default();
46    for cap in Cap::parse_entries(entries) {
47        match cap? {
48            Cap::Protocol(protocol) => caps.protocol = Some(protocol.into()),
49            Cap::Type(ty) => caps.ty = Some(ty.into()),
50            Cap::Model(model) => caps.model = Some(model.into()),
51            Cap::Commands(ref cmds) => caps.commands = cmds.clone(),
52            Cap::Whql(whql) => caps.ms_whql = Some(whql),
53            Cap::MccsVersion(major, minor) => caps.mccs_version = Some(Version::new(major, minor)),
54            Cap::Vcp(vcp) => {
55                for v in vcp {
56                    caps.vcp_features.push(v);
57                }
58            }
59            Cap::Unknown(value) => caps.unknown_tags.push(UnknownTag {
60                name: value.tag().into(),
61                data: match value {
62                    Value::String { value, .. } => match str::from_utf8(value) {
63                        Ok(value) => UnknownData::String(value.into()),
64                        Err(..) => UnknownData::StringBytes(value.into()),
65                    },
66                    Value::Binary { data, .. } => UnknownData::Binary(data.into()),
67                },
68            }),
69        }
70    }
71
72    Ok(caps)
73}
74
75/// An entry from a capability string
76#[derive(Copy, Clone, Debug, PartialEq, Eq)]
77pub enum Value<'i> {
78    /// A normal string
79    String {
80        /// The value name
81        tag: &'i str,
82        /// String contents
83        value: &'i [u8],
84    },
85    /// Raw binary data
86    Binary {
87        /// The value name
88        tag: &'i str,
89        /// Data contents
90        data: &'i [u8],
91    },
92}
93
94impl<'i> Value<'i> {
95    /// Create a new iterator over the values in a capability string
96    pub fn parse_capabilities(capability_string: &'i [u8]) -> ValueParser<'i> {
97        ValueParser::new(capability_string)
98    }
99
100    /// Parse a single capability string entry
101    pub fn parse(data: &'i str) -> io::Result<Self> {
102        Self::parse_bytes(data.as_bytes())
103    }
104
105    /// Parse a single capability string entry
106    pub fn parse_bytes(data: &'i [u8]) -> io::Result<Self> {
107        Self::parse_nom(data, None)
108            .finish()
109            .map(|(_, v)| v)
110            .map_err(map_err)
111    }
112
113    /// The value name
114    pub fn tag(&self) -> &'i str {
115        match *self {
116            Value::String { tag, .. } => tag,
117            Value::Binary { tag, .. } => tag,
118        }
119    }
120}
121
122impl From<Value<'_>> for UnknownTag {
123    fn from(v: Value) -> Self {
124        UnknownTag {
125            name: v.tag().into(),
126            data: match v {
127                Value::Binary { data, .. } => UnknownData::Binary(data.into()),
128                Value::String { value, .. } => match str::from_utf8(value) {
129                    Ok(value) => UnknownData::String(value.into()),
130                    Err(_) => UnknownData::StringBytes(value.into()),
131                },
132            },
133        }
134    }
135}
136
137impl<'a> From<&'a UnknownTag> for Value<'a> {
138    fn from(v: &'a UnknownTag) -> Self {
139        let tag = &v.name;
140        match &v.data {
141            UnknownData::Binary(data) => Value::Binary { tag, data },
142            UnknownData::StringBytes(value) => Value::String { tag, value },
143            UnknownData::String(value) => Value::String {
144                tag,
145                value: value.as_bytes(),
146            },
147        }
148    }
149}
150
151impl<'i> fmt::Display for Value<'i> {
152    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
153        match self {
154            Value::String { tag, value } => write!(f, "{tag}({})", value.escape_ascii()),
155            Value::Binary { tag, data } => {
156                write!(f, "{tag} bin({}({}))", data.len(), data.escape_ascii())
157            }
158        }
159    }
160}
161
162pub(crate) type OResult<'i, O> = Result<O, nom::error::Error<&'i [u8]>>;
163pub(crate) type OResultI<'i, O> = Result<O, nom::Err<nom::error::Error<&'i [u8]>>>;
164
165pub(crate) fn map_err(e: nom::error::Error<&[u8]>) -> io::Error {
166    use nom::error::{Error, ErrorKind};
167
168    io::Error::new(
169        match e.code {
170            ErrorKind::Eof | ErrorKind::Complete => io::ErrorKind::UnexpectedEof,
171            _ => io::ErrorKind::InvalidData,
172        },
173        Error {
174            input: e.input.escape_ascii().to_string(),
175            code: e.code,
176        },
177    )
178}
179
180pub(crate) fn trim_spaces<I, O, E, P>(parser: P) -> impl FnMut(I) -> nom::IResult<I, O, E>
181where
182    P: nom::Parser<I, O, E>,
183    E: nom::error::ParseError<I>,
184    I: Clone + nom::InputTakeAtPosition,
185    <I as nom::InputTakeAtPosition>::Item: nom::AsChar + Clone,
186{
187    use nom::{character::complete::space0, sequence::delimited};
188
189    delimited(space0, parser, space0)
190}
191
192pub(crate) fn bracketed<I, O, E, P>(parser: P) -> impl FnMut(I) -> nom::IResult<I, O, E>
193where
194    P: nom::Parser<I, O, E>,
195    E: nom::error::ParseError<I>,
196    I: Clone + nom::Slice<std::ops::RangeFrom<usize>> + nom::InputIter,
197    <I as nom::InputIter>::Item: nom::AsChar,
198{
199    use nom::{character::complete::char, sequence::delimited};
200
201    delimited(char('('), parser, char(')'))
202}