leenfetch_core/modules/linux/desktop/
resolution.rs

1use std::{env, fs};
2
3#[cfg(feature = "x11")]
4mod x11 {
5    use libc::{c_char, c_int, c_ulong};
6    use std::ptr;
7
8    #[repr(C)]
9    struct XRRScreenConfiguration {
10        _private: [u8; 0],
11    }
12
13    #[repr(C)]
14    pub struct Display {
15        _private: [u8; 0],
16    }
17
18    type Window = c_ulong;
19
20    #[link(name = "X11")]
21    #[link(name = "Xrandr")]
22    unsafe extern "C" {
23        fn XOpenDisplay(display_name: *const c_char) -> *mut Display;
24        fn XCloseDisplay(display: *mut Display);
25        fn XDefaultScreen(display: *mut Display) -> c_int;
26        fn XDisplayWidth(display: *mut Display, screen_number: c_int) -> c_int;
27        fn XDisplayHeight(display: *mut Display, screen_number: c_int) -> c_int;
28
29        fn XRRGetScreenInfo(display: *mut Display, window: Window) -> *mut XRRScreenConfiguration;
30        fn XRRFreeScreenConfigInfo(config: *mut XRRScreenConfiguration);
31        fn XRRConfigCurrentRate(config: *mut XRRScreenConfiguration) -> i16;
32        fn XRootWindow(display: *mut Display, screen_number: c_int) -> Window;
33    }
34
35    pub fn try_x11(refresh_rate: bool) -> Option<String> {
36        unsafe {
37            let display = XOpenDisplay(ptr::null());
38            if display.is_null() {
39                return None;
40            }
41
42            let screen = XDefaultScreen(display);
43            let width = XDisplayWidth(display, screen);
44            let height = XDisplayHeight(display, screen);
45            let mut result = format!("{}x{}", width, height);
46
47            if refresh_rate {
48                let root = XRootWindow(display, screen);
49                let config = XRRGetScreenInfo(display, root);
50                if !config.is_null() {
51                    let rate = XRRConfigCurrentRate(config);
52                    result = format!("{} @ {}Hz", result, rate);
53                    XRRFreeScreenConfigInfo(config);
54                }
55            }
56
57            XCloseDisplay(display);
58            Some(result)
59        }
60    }
61}
62
63#[cfg(not(feature = "x11"))]
64mod x11 {
65    pub fn try_x11(_refresh_rate: bool) -> Option<String> {
66        None
67    }
68}
69
70use x11::try_x11;
71
72pub fn get_resolution(refresh_rate: bool) -> Option<String> {
73    // X11 path
74    if env::var("DISPLAY").is_ok() {
75        if let Some(res) = try_x11(refresh_rate) {
76            return Some(res);
77        }
78    }
79
80    // DRM/KMS path
81    if let Some(res) = try_drm(refresh_rate) {
82        return Some(res);
83    }
84
85    // Framebuffer path
86    if let Some(res) = try_fb() {
87        return Some(res);
88    }
89
90    // Wayland path (unsupported without compositor protocol)
91    if env::var("WAYLAND_DISPLAY").is_ok() {
92        return Some("Wayland: resolution unavailable (restricted by compositor)".to_string());
93    }
94
95    None
96}
97
98fn try_drm(refresh_rate: bool) -> Option<String> {
99    let entries = fs::read_dir("/sys/class/drm/").ok()?;
100
101    for entry in entries.flatten() {
102        let path = entry.path();
103        if path.file_name()?.to_str()?.contains("card") && path.join("status").exists() {
104            let status = fs::read_to_string(path.join("status")).ok()?;
105            if status.trim() != "connected" {
106                continue;
107            }
108
109            let mode_path = path.join("modes");
110            if mode_path.exists() {
111                let mode = fs::read_to_string(mode_path)
112                    .ok()?
113                    .lines()
114                    .next()?
115                    .to_string();
116                return Some(if refresh_rate {
117                    format!("{} @ 60Hz", mode) // Fallback rate assumption
118                } else {
119                    mode
120                });
121            }
122        }
123    }
124
125    None
126}
127
128fn try_fb() -> Option<String> {
129    let contents = fs::read_to_string("/sys/class/graphics/fb0/virtual_size").ok()?;
130    let (w, h) = contents.trim().split_once(',')?;
131    Some(format!("{}x{}", w.trim(), h.trim()))
132}