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