xrandr_parser/
connector.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright (c) 2022 Th3-S1lenc3
3
4use derive_setters::*;
5
6// IDEA: Add documentation for this module
7
8#[derive(Debug, Default, Setters, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
9#[setters(borrow_self, prefix = "set_")]
10pub struct Connector {
11    pub name: String,                        // HDMI 1
12    pub status: String,                      // (Dis)connected
13    pub primary: bool,                       // True if primary
14    pub current_resolution: Resolution,      // 1920x1080,
15    pub current_refresh_rate: String,        // 60
16    pub prefered_resolution: Resolution,     // 2560×1440
17    pub prefered_refresh_rate: String,       // 120
18    pub position: Position,                  // 0,0 or 1920,0 or ...
19    pub orientation: String,                 // normal, left, etc...
20    pub available_orientations: Vec<String>, // normal, left, etc...
21    pub physical_dimensions: Dimensions,     // 1210mm x 680mm
22    pub output_info: Vec<Output>,
23}
24
25#[derive(Debug, Default, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
26pub struct Position {
27    pub x: String, // 0 or 1920 or ...
28    pub y: String, // 0 or 1080 or ...
29}
30
31#[derive(Debug, Default, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
32pub struct Dimensions {
33    pub x: String, // 1210 or ...
34    pub y: String, // 680 or ...
35}
36
37#[derive(Debug, Default, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
38pub struct Resolution {
39    pub horizontal: String, // 1920
40    pub vertical: String,   // 1080
41}
42
43impl Resolution {
44    pub fn pretty(&self) -> String {
45        format!("{}x{}", self.horizontal, self.vertical)
46    }
47}
48
49#[derive(Debug, Default, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
50pub struct Output {
51    pub resolution: Resolution,
52    pub rates: Vec<String>,
53}
54
55impl Connector {
56    /// Create a new instance of Connector
57    pub fn new() -> Self {
58        Connector::default()
59    }
60
61    /// Getter function for `Connector.name`
62    ///
63    /// ## Example
64    ///
65    /// ```edition2021
66    /// #[allow(non_snake_case)]
67    ///
68    /// use xrandr_parser::Parser;
69    ///
70    /// fn main() -> Result<(), String> {
71    ///     let mut XrandrParser = Parser::new();
72    ///
73    ///     XrandrParser.parse()?;
74    ///
75    ///     let connector = &XrandrParser.get_connector("HDMI-1")?;
76    ///
77    ///     let name = &connector.name();
78    ///
79    ///     println!("Connector name: {}", name);
80    ///
81    /// #   assert_eq!(name, &"HDMI-1".to_string());
82    ///     Ok(())
83    /// }
84    /// ```
85    pub fn name(&self) -> String {
86        self.name.clone()
87    }
88
89    /// Getter function for `Connector.status`
90    ///
91    /// ## Example
92    ///
93    /// ```edition2021
94    /// #[allow(non_snake_case)]
95    ///
96    /// use xrandr_parser::Parser;
97    ///
98    /// fn main() -> Result<(), String> {
99    ///     let mut XrandrParser = Parser::new();
100    ///
101    ///     XrandrParser.parse()?;
102    ///
103    ///     let connector = &XrandrParser.get_connector("HDMI-1")?;
104    ///
105    ///     let status = &connector.status();
106    ///
107    ///     println!("Connector status: {}", status);
108    ///
109    /// #   assert_eq!(status, &"connected".to_string());
110    ///     Ok(())
111    /// }
112    /// ```
113    pub fn status(&self) -> String {
114        self.status.clone()
115    }
116
117    /// Getter function for `Connector.primary`
118    ///
119    /// ## Example
120    ///
121    /// ```edition2021
122    /// #[allow(non_snake_case)]
123    ///
124    /// use xrandr_parser::Parser;
125    ///
126    /// fn main() -> Result<(), String> {
127    ///     let mut XrandrParser = Parser::new();
128    ///
129    ///     XrandrParser.parse()?;
130    ///
131    ///     let connector = &XrandrParser.get_connector("HDMI-1")?;
132    ///
133    ///     let primary = &connector.primary();
134    ///
135    ///     println!("Connector primary: {}", primary);
136    ///
137    /// #   assert_eq!(primary, &true);
138    ///     Ok(())
139    /// }
140    /// ```
141    pub fn primary(&self) -> bool {
142        self.primary.clone()
143    }
144
145    /// Getter function for `Connector.current_resolution`
146    ///
147    /// ## Example
148    ///
149    /// ```edition2021
150    /// #[allow(non_snake_case)]
151    ///
152    /// use xrandr_parser::Parser;
153    /// # use xrandr_parser::connector::Resolution;
154    ///
155    /// fn main() -> Result<(), String> {
156    ///     let mut XrandrParser = Parser::new();
157    ///
158    ///     XrandrParser.parse()?;
159    ///
160    ///     let connector = &XrandrParser.get_connector("HDMI-1")?;
161    ///
162    ///     let current_resolution = &connector.current_resolution();
163    ///
164    ///     println!("Connector Current Resolution: {:#?}", current_resolution);
165    ///
166    /// #   assert_eq!(current_resolution, &Resolution {
167    /// #       horizontal: "1920".to_string(),
168    /// #       vertical: "1080".to_string(),
169    /// #   });
170    ///     Ok(())
171    /// }
172    /// ```
173    pub fn current_resolution(&self) -> Resolution {
174        self.current_resolution.clone()
175    }
176
177    /// Getter function for `Connector.current_refresh_rate`
178    ///
179    /// ## Example
180    ///
181    /// ```edition2021
182    /// #[allow(non_snake_case)]
183    ///
184    /// use xrandr_parser::Parser;
185    ///
186    /// fn main() -> Result<(), String> {
187    ///     let mut XrandrParser = Parser::new();
188    ///
189    ///     XrandrParser.parse()?;
190    ///
191    ///     let connector = &XrandrParser.get_connector("HDMI-1")?;
192    ///
193    ///     let current_refresh_rate = &connector.current_refresh_rate();
194    ///
195    ///     println!("Connector Current Refresh Rate: {}", current_refresh_rate);
196    ///
197    /// #   assert_eq!(current_refresh_rate, &"60.00".to_string());
198    ///     Ok(())
199    /// }
200    /// ```
201    pub fn current_refresh_rate(&self) -> String {
202        self.current_refresh_rate.clone()
203    }
204
205    /// Getter function for `Connector.prefered_resolution`
206    ///
207    /// ## Example
208    ///
209    /// ```edition2021
210    /// #[allow(non_snake_case)]
211    ///
212    /// use xrandr_parser::Parser;
213    /// # use xrandr_parser::connector::Resolution;
214    ///
215    /// fn main() -> Result<(), String> {
216    ///     let mut XrandrParser = Parser::new();
217    ///
218    ///     XrandrParser.parse()?;
219    ///
220    ///     let connector = &XrandrParser.get_connector("HDMI-1")?;
221    ///
222    ///     let prefered_resolution = &connector.prefered_resolution();
223    ///
224    ///     println!("Connector Prefered Resolution: {:#?}", prefered_resolution);
225    ///
226    /// #   assert_eq!(prefered_resolution, &Resolution {
227    /// #       horizontal: "1920".to_string(),
228    /// #       vertical: "1080".to_string(),
229    /// #   });
230    ///     Ok(())
231    /// }
232    /// ```
233    pub fn prefered_resolution(&self) -> Resolution {
234        self.prefered_resolution.clone()
235    }
236
237    /// Getter function for `Connector.prefered_refresh_rate`
238    ///
239    /// ## Example
240    ///
241    /// ```edition2021
242    /// #[allow(non_snake_case)]
243    ///
244    /// use xrandr_parser::Parser;
245    ///
246    /// fn main() -> Result<(), String> {
247    ///     let mut XrandrParser = Parser::new();
248    ///
249    ///     XrandrParser.parse()?;
250    ///
251    ///     let connector = &XrandrParser.get_connector("HDMI-1")?;
252    ///
253    ///     let prefered_refresh_rate = &connector.prefered_refresh_rate();
254    ///
255    ///     println!("Connector Prefered Refresh Rate: {}", prefered_refresh_rate);
256    ///
257    /// #   assert_eq!(prefered_refresh_rate, &"60.00".to_string());
258    ///     Ok(())
259    /// }
260    /// ```
261    pub fn prefered_refresh_rate(&self) -> String {
262        self.prefered_refresh_rate.clone()
263    }
264
265    /// Getter function for `Connector.primary`
266    ///
267    /// ## Example
268    ///
269    /// ```edition2021
270    /// #[allow(non_snake_case)]
271    ///
272    /// use xrandr_parser::Parser;
273    /// # use xrandr_parser::connector::Position;
274    ///
275    /// fn main() -> Result<(), String> {
276    ///     let mut XrandrParser = Parser::new();
277    ///
278    ///     XrandrParser.parse()?;
279    ///
280    ///     let connector = &XrandrParser.get_connector("HDMI-1")?;
281    ///
282    ///     let position = &connector.position();
283    ///
284    ///     println!("Connector position: {:#?}", position);
285    ///
286    /// #   assert_eq!(position, &Position {
287    /// #       x: "0".to_string(),
288    /// #       y: "0".to_string()
289    /// #   });
290    ///     Ok(())
291    /// }
292    /// ```
293    pub fn position(&self) -> Position {
294        self.position.clone()
295    }
296
297    /// Getter function for `Connector.orientation`
298    ///
299    /// ## Example
300    ///
301    /// ```edition2021
302    /// #[allow(non_snake_case)]
303    ///
304    /// use xrandr_parser::Parser;
305    ///
306    /// fn main() -> Result<(), String> {
307    ///     let mut XrandrParser = Parser::new();
308    ///
309    ///     XrandrParser.parse()?;
310    ///
311    ///     let connector = &XrandrParser.get_connector("HDMI-1")?;
312    ///
313    ///     let orientation = &connector.orientation();
314    ///
315    ///     println!("Connector orientation: {}", orientation);
316    ///
317    /// #   assert_eq!(orientation, &"normal".to_string());
318    ///     Ok(())
319    /// }
320    /// ```
321    pub fn orientation(&self) -> String {
322        self.orientation.clone()
323    }
324
325    /// Getter function for `Connector.available_orientations`
326    ///
327    /// ## Example
328    ///
329    /// ```edition2021
330    /// #[allow(non_snake_case)]
331    ///
332    /// use xrandr_parser::Parser;
333    ///
334    /// fn main() -> Result<(), String> {
335    ///     let mut XrandrParser = Parser::new();
336    ///
337    ///     XrandrParser.parse()?;
338    ///
339    ///     let connector = &XrandrParser.get_connector("HDMI-1")?;
340    ///
341    ///     let available_orientations = &connector.available_orientations();
342    ///
343    ///     println!("Connector Available Orientations: {:#?}", available_orientations);
344    ///
345    /// #   assert_eq!(available_orientations, &vec![
346    /// #       "normal".to_string(),
347    /// #       "left".to_string(),
348    /// #       "inverted".to_string(),
349    /// #       "right".to_string(),
350    /// #       "x axis".to_string(),
351    /// #       "y axis".to_string(),
352    /// #   ]);
353    ///     Ok(())
354    /// }
355    /// ```
356    pub fn available_orientations(&self) -> Vec<String> {
357        self.available_orientations.clone()
358    }
359
360    /// Getter function for `Connector.physical_dimensions`
361    ///
362    /// ## Example
363    ///
364    /// ```edition2021
365    /// #[allow(non_snake_case)]
366    ///
367    /// use xrandr_parser::Parser;
368    /// # use xrandr_parser::connector::Dimensions;
369    ///
370    /// fn main() -> Result<(), String> {
371    ///     let mut XrandrParser = Parser::new();
372    ///
373    ///     XrandrParser.parse()?;
374    ///
375    ///     let connector = &XrandrParser.get_connector("HDMI-1")?;
376    ///
377    ///     let physical_dimensions = &connector.physical_dimensions();
378    ///
379    ///     println!("Connector Physical Dimensions: {:#?}", physical_dimensions);
380    ///
381    /// #   assert_eq!(physical_dimensions, &Dimensions {
382    /// #       x: "1210".to_string(),
383    /// #       y: "680".to_string(),
384    /// #   });
385    ///     Ok(())
386    /// }
387    /// ```
388    pub fn physical_dimensions(&self) -> Dimensions {
389        self.physical_dimensions.clone()
390    }
391
392    /// Getter function for `Connector.output_info`
393    ///
394    /// ## Example
395    ///
396    /// ```edition2021
397    /// #[allow(non_snake_case)]
398    ///
399    /// use xrandr_parser::Parser;
400    /// # use xrandr_parser::connector::{Output, Resolution};
401    ///
402    /// fn main() -> Result<(), String> {
403    ///     let mut XrandrParser = Parser::new();
404    ///
405    ///     XrandrParser.parse()?;
406    ///
407    ///     let connector = &XrandrParser.get_connector("HDMI-1")?;
408    ///
409    ///     let output_info = &connector.output_info();
410    ///
411    ///     println!("Connector Output Info: {:#?}", output_info);
412    ///
413    /// #   assert_eq!(output_info, &vec![
414    /// #       Output {
415    /// #           resolution: Resolution {
416    /// #               horizontal: "1920".to_string(),
417    /// #               vertical: "1080".to_string(),
418    /// #           },
419    /// #           rates: vec![
420    /// #               "60.00".to_string(),
421    /// #           ].to_vec(),
422    /// #       }
423    /// #   ]);
424    ///     Ok(())
425    /// }
426    /// ```
427    pub fn output_info(&self) -> Vec<Output> {
428        self.output_info.clone()
429    }
430
431    /// Get available resolutions for a specific connector in `struct` form. If the resolution
432    /// is not found it returns an empty `Vec<Resolution>` not an error.
433    ///
434    /// ## Example
435    ///
436    /// ```edition2021
437    /// #[allow(non_snake_case)]
438    ///
439    /// use xrandr_parser::Parser;
440    /// # use xrandr_parser::connector::*;
441    ///
442    /// fn main() -> Result<(), String> {
443    ///     let mut XrandrParser = Parser::new();
444    ///
445    ///     XrandrParser.parse()?;
446    ///
447    ///     let connector = &XrandrParser.get_connector("HDMI-1")?;
448    ///
449    ///     let available_resolutions = &connector.available_resolutions()?;
450    ///
451    ///     println!("Available Resolutions: {:#?}", available_resolutions);
452    ///
453    /// # assert_eq!(available_resolutions, &[
454    /// #     Resolution {
455    /// #         horizontal: "1920".to_string(),
456    /// #         vertical: "1080".to_string(),
457    /// #     }
458    /// # ].to_vec());
459    ///     Ok(())
460    /// }
461    /// ```
462    // TODO: Condense these two functions into a single generic function.
463    // IDEA: Add error if `Vec<Resolution>` is empty
464    pub fn available_resolutions(&self) -> Result<Vec<Resolution>, String> {
465        let outputs: Vec<Output> = self.output_info();
466
467        let resolutions: Vec<Resolution> = outputs.into_iter().map(|o| o.resolution).collect();
468
469        Ok(resolutions)
470    }
471
472    /// Get available resolutions for a specific connector in `String` form. If the resolution
473    /// is not found it returns an empty `Vec<String>` not an error.
474    ///
475    /// ```edition2021
476    /// #[allow(non_snake_case)]
477    ///
478    /// use xrandr_parser::Parser;
479    ///
480    /// fn main() -> Result<(), String> {
481    ///     let mut XrandrParser = Parser::new();
482    ///
483    ///     XrandrParser.parse()?;
484    ///
485    ///     let connector = &XrandrParser.get_connector("HDMI-1")?;
486    ///
487    ///     let available_resolutions = &connector.available_resolutions_pretty()?;
488    ///
489    ///     println!("Available Resolutions: {:#?}", available_resolutions);
490    ///
491    /// # assert_eq!(available_resolutions, &[
492    /// #     "1920x1080".to_string(),
493    /// # ].to_vec());
494    ///     Ok(())
495    /// }
496    /// ```
497    // IDEA: Add error if `Vec<String>` is empty
498    pub fn available_resolutions_pretty(&self) -> Result<Vec<String>, String> {
499        let outputs: Vec<Output> = self.output_info();
500
501        let resolutions: Vec<String> = outputs.into_iter().map(|o| o.resolution.pretty()).collect();
502
503        Ok(resolutions)
504    }
505
506    /// Get available refresh rates for a specific connector at a given resolution. If the resolution
507    /// is not found it returns an empty `Vec<String>` not an error.
508    ///
509    /// ## Example
510    ///
511    /// ```edition2021
512    /// #[allow(non_snake_case)]
513    ///
514    /// use xrandr_parser::Parser;
515    ///
516    /// fn main() -> Result<(), String> {
517    ///     let mut XrandrParser = Parser::new();
518    ///
519    ///     XrandrParser.parse()?;
520    ///
521    ///     let connector = &XrandrParser.get_connector("HDMI-1")?;
522    ///
523    ///     let available_refresh_rates = &connector.available_refresh_rates("1920x1080")?;
524    ///
525    ///     println!("Available Refresh Rates: {:#?}", available_refresh_rates);
526    ///
527    /// # assert_eq!(available_refresh_rates, &[
528    /// #     "60.00".to_string(),
529    /// # ].to_vec());
530    ///     Ok(())
531    /// }
532    /// ```
533    // IDEA: Add error if `Vec<String>` is empty
534    // TODO: Rewrite so target_resolution in `Resolution` struct
535    pub fn available_refresh_rates(&self, target_resolution: &str) -> Result<Vec<String>, String> {
536        let mut outputs: Vec<Output> = self.output_info();
537
538        outputs.retain(|o| o.resolution.pretty() == target_resolution);
539
540        Ok(outputs.into_iter().map(|o| o.rates).flatten().collect())
541    }
542}