winit/platform_impl/linux/x11/
xsettings.rs

1//! Parser for the xsettings data format.
2//!
3//! Some of this code is referenced from [here].
4//!
5//! [here]: https://github.com/derat/xsettingsd
6
7use std::iter;
8use std::num::NonZeroUsize;
9
10use x11rb::protocol::xproto::{self, ConnectionExt};
11
12use super::atoms::*;
13use super::XConnection;
14
15type Result<T> = core::result::Result<T, ParserError>;
16
17const DPI_NAME: &[u8] = b"Xft/DPI";
18const DPI_MULTIPLIER: f64 = 1024.0;
19const LITTLE_ENDIAN: u8 = b'l';
20const BIG_ENDIAN: u8 = b'B';
21
22impl XConnection {
23    /// Get the DPI from XSettings.
24    pub(crate) fn xsettings_dpi(
25        &self,
26        xsettings_screen: xproto::Atom,
27    ) -> core::result::Result<Option<f64>, super::X11Error> {
28        let atoms = self.atoms();
29
30        // Get the current owner of the screen's settings.
31        let owner = self.xcb_connection().get_selection_owner(xsettings_screen)?.reply()?;
32
33        // Read the _XSETTINGS_SETTINGS property.
34        let data: Vec<u8> =
35            self.get_property(owner.owner, atoms[_XSETTINGS_SETTINGS], atoms[_XSETTINGS_SETTINGS])?;
36
37        // Parse the property.
38        let dpi_setting = read_settings(&data)?
39            .find(|res| res.as_ref().map_or(true, |s| s.name == DPI_NAME))
40            .transpose()?;
41        if let Some(dpi_setting) = dpi_setting {
42            let base_dpi = match dpi_setting.data {
43                SettingData::Integer(dpi) => dpi as f64,
44                SettingData::String(_) => {
45                    return Err(ParserError::BadType(SettingType::String).into())
46                },
47                SettingData::Color(_) => {
48                    return Err(ParserError::BadType(SettingType::Color).into())
49                },
50            };
51
52            Ok(Some(base_dpi / DPI_MULTIPLIER))
53        } else {
54            Ok(None)
55        }
56    }
57}
58
59/// Read over the settings in the block of data.
60fn read_settings(data: &[u8]) -> Result<impl Iterator<Item = Result<Setting<'_>>> + '_> {
61    // Create a parser. This automatically parses the first 8 bytes for metadata.
62    let mut parser = Parser::new(data)?;
63
64    // Read the total number of settings.
65    let total_settings = parser.i32()?;
66
67    // Iterate over the settings.
68    let iter = iter::repeat_with(move || Setting::parse(&mut parser)).take(total_settings as usize);
69    Ok(iter)
70}
71
72/// A setting in the settings list.
73struct Setting<'a> {
74    /// The name of the setting.
75    name: &'a [u8],
76
77    /// The data contained in the setting.
78    data: SettingData<'a>,
79}
80
81/// The data contained in a setting.
82enum SettingData<'a> {
83    Integer(i32),
84    String(#[allow(dead_code)] &'a [u8]),
85    Color(#[allow(dead_code)] [i16; 4]),
86}
87
88impl<'a> Setting<'a> {
89    /// Parse a new `SettingData`.
90    fn parse(parser: &mut Parser<'a>) -> Result<Self> {
91        // Read the type.
92        let ty: SettingType = parser.i8()?.try_into()?;
93
94        // Read another byte of padding.
95        parser.advance(1)?;
96
97        // Read the name of the setting.
98        let name_len = parser.i16()?;
99        let name = parser.advance(name_len as usize)?;
100        parser.pad(name.len(), 4)?;
101
102        // Ignore the serial number.
103        parser.advance(4)?;
104
105        let data = match ty {
106            SettingType::Integer => {
107                // Read a 32-bit integer.
108                SettingData::Integer(parser.i32()?)
109            },
110
111            SettingType::String => {
112                // Read the data.
113                let data_len = parser.i32()?;
114                let data = parser.advance(data_len as usize)?;
115                parser.pad(data.len(), 4)?;
116
117                SettingData::String(data)
118            },
119
120            SettingType::Color => {
121                // Read i16's of color.
122                let (red, blue, green, alpha) =
123                    (parser.i16()?, parser.i16()?, parser.i16()?, parser.i16()?);
124
125                SettingData::Color([red, blue, green, alpha])
126            },
127        };
128
129        Ok(Setting { name, data })
130    }
131}
132
133#[derive(Debug)]
134pub enum SettingType {
135    Integer = 0,
136    String = 1,
137    Color = 2,
138}
139
140impl TryFrom<i8> for SettingType {
141    type Error = ParserError;
142
143    fn try_from(value: i8) -> Result<Self> {
144        Ok(match value {
145            0 => Self::Integer,
146            1 => Self::String,
147            2 => Self::Color,
148            x => return Err(ParserError::InvalidType(x)),
149        })
150    }
151}
152
153/// Parser for the incoming byte stream.
154struct Parser<'a> {
155    bytes: &'a [u8],
156    endianness: Endianness,
157}
158
159impl<'a> Parser<'a> {
160    /// Create a new parser.
161    fn new(bytes: &'a [u8]) -> Result<Self> {
162        let (endianness, bytes) = bytes.split_first().ok_or_else(|| ParserError::ran_out(1, 0))?;
163        let endianness = match *endianness {
164            BIG_ENDIAN => Endianness::Big,
165            LITTLE_ENDIAN => Endianness::Little,
166            _ => Endianness::native(),
167        };
168
169        Ok(Self {
170            // Ignore three bytes of padding and the four-byte serial.
171            bytes: bytes.get(7..).ok_or_else(|| ParserError::ran_out(7, bytes.len()))?,
172            endianness,
173        })
174    }
175
176    /// Get a slice of bytes.
177    fn advance(&mut self, n: usize) -> Result<&'a [u8]> {
178        if n == 0 {
179            return Ok(&[]);
180        }
181
182        if n > self.bytes.len() {
183            Err(ParserError::ran_out(n, self.bytes.len()))
184        } else {
185            let (part, rem) = self.bytes.split_at(n);
186            self.bytes = rem;
187            Ok(part)
188        }
189    }
190
191    /// Skip some padding.
192    fn pad(&mut self, size: usize, pad: usize) -> Result<()> {
193        let advance = (pad - (size % pad)) % pad;
194        self.advance(advance)?;
195        Ok(())
196    }
197
198    /// Get a single byte.
199    fn i8(&mut self) -> Result<i8> {
200        self.advance(1).map(|s| s[0] as i8)
201    }
202
203    /// Get two bytes.
204    fn i16(&mut self) -> Result<i16> {
205        self.advance(2).map(|s| {
206            let bytes: &[u8; 2] = s.try_into().unwrap();
207            match self.endianness {
208                Endianness::Big => i16::from_be_bytes(*bytes),
209                Endianness::Little => i16::from_le_bytes(*bytes),
210            }
211        })
212    }
213
214    /// Get four bytes.
215    fn i32(&mut self) -> Result<i32> {
216        self.advance(4).map(|s| {
217            let bytes: &[u8; 4] = s.try_into().unwrap();
218            match self.endianness {
219                Endianness::Big => i32::from_be_bytes(*bytes),
220                Endianness::Little => i32::from_le_bytes(*bytes),
221            }
222        })
223    }
224}
225
226/// Endianness of the incoming data.
227enum Endianness {
228    Little,
229    Big,
230}
231
232impl Endianness {
233    #[cfg(target_endian = "little")]
234    fn native() -> Self {
235        Endianness::Little
236    }
237
238    #[cfg(target_endian = "big")]
239    fn native() -> Self {
240        Endianness::Big
241    }
242}
243
244/// Parser errors.
245#[allow(dead_code)]
246#[derive(Debug)]
247pub enum ParserError {
248    /// Ran out of bytes.
249    NoMoreBytes { expected: NonZeroUsize, found: usize },
250
251    /// Invalid type.
252    InvalidType(i8),
253
254    /// Bad setting type.
255    BadType(SettingType),
256}
257
258impl ParserError {
259    fn ran_out(expected: usize, found: usize) -> ParserError {
260        let expected = NonZeroUsize::new(expected).unwrap();
261        Self::NoMoreBytes { expected, found }
262    }
263}
264
265#[cfg(test)]
266/// Tests for the XSETTINGS parser.
267mod tests {
268    use super::*;
269
270    const XSETTINGS: &str = include_str!("tests/xsettings.dat");
271
272    #[test]
273    fn empty() {
274        let err = match read_settings(&[]) {
275            Ok(_) => panic!(),
276            Err(err) => err,
277        };
278        match err {
279            ParserError::NoMoreBytes { expected, found } => {
280                assert_eq!(expected.get(), 1);
281                assert_eq!(found, 0);
282            },
283
284            _ => panic!(),
285        }
286    }
287
288    #[test]
289    fn parse_xsettings() {
290        let data = XSETTINGS
291            .trim()
292            .split(',')
293            .map(|tok| {
294                let val = tok.strip_prefix("0x").unwrap();
295                u8::from_str_radix(val, 16).unwrap()
296            })
297            .collect::<Vec<_>>();
298
299        let settings = read_settings(&data).unwrap().collect::<Result<Vec<_>>>().unwrap();
300
301        let dpi = settings.iter().find(|s| s.name == b"Xft/DPI").unwrap();
302        assert_int(&dpi.data, 96 * 1024);
303        let hinting = settings.iter().find(|s| s.name == b"Xft/Hinting").unwrap();
304        assert_int(&hinting.data, 1);
305
306        let rgba = settings.iter().find(|s| s.name == b"Xft/RGBA").unwrap();
307        assert_string(&rgba.data, "rgb");
308        let lcd = settings.iter().find(|s| s.name == b"Xft/Lcdfilter").unwrap();
309        assert_string(&lcd.data, "lcddefault");
310    }
311
312    fn assert_string(dat: &SettingData<'_>, s: &str) {
313        match dat {
314            SettingData::String(left) => assert_eq!(*left, s.as_bytes()),
315            _ => panic!("invalid data type"),
316        }
317    }
318
319    fn assert_int(dat: &SettingData<'_>, i: i32) {
320        match dat {
321            SettingData::Integer(left) => assert_eq!(*left, i),
322            _ => panic!("invalid data type"),
323        }
324    }
325}