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}