uconsole_sleep/hardware/
framebuffer.rs

1//! Framebuffer detection
2
3use crate::error::Error;
4use std::path::PathBuf;
5
6#[cfg(test)]
7use std::fs;
8
9const FRAMEBUFFER_PATH: &str = "/sys/class/graphics/fb0";
10
11/// Find framebuffer device
12///
13/// # Returns
14/// - Ok(Some(PathBuf)) if framebuffer found
15/// - Ok(None) if not found
16/// - Err(Error) if error occurred
17pub fn find_framebuffer() -> Result<Option<PathBuf>, Error> {
18    let path = PathBuf::from(FRAMEBUFFER_PATH);
19
20    match path.try_exists() {
21        Ok(exists) => {
22            if exists {
23                Ok(Some(path))
24            } else {
25                Ok(None)
26            }
27        }
28        Err(e) => Err(Error::from(e)),
29    }
30}
31
32/// Get framebuffer virtual resolution
33///
34/// # Arguments
35/// * `path` - Path to the framebuffer device
36///
37/// # Returns
38/// - Ok(Some((width, height))) if found
39/// - Ok(None) if not available
40/// - Err(Error) if error occurred
41#[cfg(test)]
42fn get_virtual_resolution(path: &std::path::Path) -> Result<Option<(u32, u32)>, Error> {
43    let virtual_size_path = path.join("virtual_size");
44
45    match fs::read_to_string(&virtual_size_path) {
46        Ok(content) => {
47            let parts: Vec<&str> = content.trim().split(',').collect();
48            if let (Ok(width), Ok(height)) = (
49                parts[0].trim().parse::<u32>(),
50                parts[1].trim().parse::<u32>(),
51            ) && parts.len() == 2
52            {
53                return Ok(Some((width, height)));
54            }
55            Ok(None)
56        }
57        Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
58        Err(e) => Err(Error::from(e)),
59    }
60}
61
62/// Get framebuffer physical resolution
63///
64/// # Arguments
65/// * `path` - Path to the framebuffer device
66///
67/// # Returns
68/// - Ok(Some((width, height))) if found
69/// - Ok(None) if not available
70/// - Err(Error) if error occurred
71#[cfg(test)]
72fn get_physical_resolution(path: &std::path::Path) -> Result<Option<(u32, u32)>, Error> {
73    let phys_size_path = path.join("phys_size");
74
75    match fs::read_to_string(&phys_size_path) {
76        Ok(content) => {
77            let parts: Vec<&str> = content.trim().split(',').collect();
78            if let (Ok(width), Ok(height)) = (
79                parts[0].trim().parse::<u32>(),
80                parts[1].trim().parse::<u32>(),
81            ) && parts.len() == 2
82            {
83                return Ok(Some((width, height)));
84            }
85            Ok(None)
86        }
87        Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
88        Err(e) => Err(Error::from(e)),
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn test_framebuffer_path_constant() {
98        assert_eq!(FRAMEBUFFER_PATH, "/sys/class/graphics/fb0");
99    }
100
101    #[test]
102    fn test_find_framebuffer_returns_option() {
103        if find_framebuffer().is_ok() {}
104    }
105
106    #[test]
107    fn test_virtual_resolution_parsing_valid() {
108        let virtual_size_str = "1920,1080";
109        let parts: Vec<&str> = virtual_size_str.trim().split(',').collect();
110        assert_eq!(parts.len(), 2);
111        assert_eq!(parts[0].trim().parse::<u32>().unwrap(), 1920);
112        assert_eq!(parts[1].trim().parse::<u32>().unwrap(), 1080);
113    }
114
115    #[test]
116    fn test_physical_resolution_parsing_valid() {
117        let phys_size_str = "268,150";
118        let parts: Vec<&str> = phys_size_str.trim().split(',').collect();
119        assert_eq!(parts.len(), 2);
120        assert_eq!(parts[0].trim().parse::<u32>().unwrap(), 268);
121        assert_eq!(parts[1].trim().parse::<u32>().unwrap(), 150);
122    }
123
124    #[test]
125    fn test_resolution_parsing_with_whitespace() {
126        let size_str = "  1920 , 1080  ";
127        let parts: Vec<&str> = size_str.trim().split(',').collect();
128        assert_eq!(parts[0].trim().parse::<u32>().unwrap(), 1920);
129        assert_eq!(parts[1].trim().parse::<u32>().unwrap(), 1080);
130    }
131
132    #[test]
133    fn test_resolution_parsing_various_sizes() {
134        let sizes = vec![
135            ("640,480", (640, 480)),
136            ("1024,768", (1024, 768)),
137            ("1920,1080", (1920, 1080)),
138        ];
139
140        for (size_str, (expected_width, expected_height)) in sizes {
141            let parts: Vec<&str> = size_str.split(',').collect();
142            let width = parts[0].trim().parse::<u32>().unwrap();
143            let height = parts[1].trim().parse::<u32>().unwrap();
144            assert_eq!((width, height), (expected_width, expected_height));
145        }
146    }
147
148    #[test]
149    fn test_invalid_resolution_format() {
150        let size_str = "invalid";
151        let parts: Vec<&str> = size_str.split(',').collect();
152        assert_eq!(parts.len(), 1);
153    }
154
155    #[test]
156    fn test_get_framebuffer_resolutions_funcs() {
157        use std::fs;
158        let tmp = std::env::temp_dir().join(format!(
159            "uconsole_fb_{}",
160            std::time::SystemTime::now()
161                .duration_since(std::time::UNIX_EPOCH)
162                .unwrap()
163                .as_millis()
164        ));
165        let _ = fs::create_dir_all(&tmp);
166        fs::write(tmp.join("virtual_size"), "1920,1080\n").unwrap();
167        fs::write(tmp.join("phys_size"), "268,150\n").unwrap();
168
169        let v = get_virtual_resolution(&tmp).unwrap();
170        assert_eq!(v, Some((1920u32, 1080u32)));
171        let p = get_physical_resolution(&tmp).unwrap();
172        assert_eq!(p, Some((268u32, 150u32)));
173    }
174
175    #[test]
176    fn test_pathbuf_join_operation() {
177        let base = PathBuf::from("/sys/class/graphics/fb0");
178        let virtual_size_path = base.join("virtual_size");
179        assert!(virtual_size_path.to_string_lossy().contains("virtual_size"));
180    }
181
182    #[test]
183    fn test_resolution_edge_cases() {
184        assert_eq!("0,0".split(',').count(), 2);
185        assert_eq!(
186            ("4294967295,4294967295".split(',').collect::<Vec<_>>().len()),
187            2
188        );
189    }
190}