winit/platform_impl/linux/x11/
xsettings.rs
1use 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 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 let owner = self.xcb_connection().get_selection_owner(xsettings_screen)?.reply()?;
32
33 let data: Vec<u8> =
35 self.get_property(owner.owner, atoms[_XSETTINGS_SETTINGS], atoms[_XSETTINGS_SETTINGS])?;
36
37 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
59fn read_settings(data: &[u8]) -> Result<impl Iterator<Item = Result<Setting<'_>>> + '_> {
61 let mut parser = Parser::new(data)?;
63
64 let total_settings = parser.i32()?;
66
67 let iter = iter::repeat_with(move || Setting::parse(&mut parser)).take(total_settings as usize);
69 Ok(iter)
70}
71
72struct Setting<'a> {
74 name: &'a [u8],
76
77 data: SettingData<'a>,
79}
80
81enum 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 fn parse(parser: &mut Parser<'a>) -> Result<Self> {
91 let ty: SettingType = parser.i8()?.try_into()?;
93
94 parser.advance(1)?;
96
97 let name_len = parser.i16()?;
99 let name = parser.advance(name_len as usize)?;
100 parser.pad(name.len(), 4)?;
101
102 parser.advance(4)?;
104
105 let data = match ty {
106 SettingType::Integer => {
107 SettingData::Integer(parser.i32()?)
109 },
110
111 SettingType::String => {
112 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 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
153struct Parser<'a> {
155 bytes: &'a [u8],
156 endianness: Endianness,
157}
158
159impl<'a> Parser<'a> {
160 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 bytes: bytes.get(7..).ok_or_else(|| ParserError::ran_out(7, bytes.len()))?,
172 endianness,
173 })
174 }
175
176 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 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 fn i8(&mut self) -> Result<i8> {
200 self.advance(1).map(|s| s[0] as i8)
201 }
202
203 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 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
226enum 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#[allow(dead_code)]
246#[derive(Debug)]
247pub enum ParserError {
248 NoMoreBytes { expected: NonZeroUsize, found: usize },
250
251 InvalidType(i8),
253
254 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)]
266mod 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}