thinkpad_backlight_api/
lib.rs

1use std::io::{Read, Write};
2
3const BRIGHTNESS_PATH: &str = "/sys/class/leds/tpacpi::kbd_backlight/brightness";
4
5#[derive(Clone, Debug)]
6pub enum Error {
7    PermissionDenied,
8    FileNotFound(String),
9    IoError(String),
10    InvalidLevel(String),
11}
12
13#[derive(Clone, Debug)]
14pub enum LightLevel {
15    Off = 0,
16    Medium = 1,
17    Full = 2,
18}
19
20impl LightLevel {
21    pub fn from_u8(level: u8) -> Result<Self, Error> {
22        match level {
23            0 => Ok(Self::Off),
24            1 => Ok(Self::Medium),
25            2 => Ok(Self::Full),
26            _ => Err(Error::InvalidLevel(format!(
27                "Unexpected level: {}",
28                level
29            ))),
30        }
31    }
32
33    pub fn as_u8(self) -> u8 {
34        self as u8
35    }
36}
37
38pub struct KeyboardBacklight;
39
40impl KeyboardBacklight {
41    fn ensure_root() -> Result<(), Error> {
42        if unsafe { libc::getuid() } != 0 {
43            return Err(Error::PermissionDenied);
44        }
45        Ok(())
46    }
47
48    pub fn set(level: LightLevel) -> Result<(), Error> {
49        Self::ensure_root()?;
50
51        let mut file = std::fs::OpenOptions::new()
52            .write(true)
53            .open(BRIGHTNESS_PATH)
54            .map_err(|e| Error::FileNotFound(format!("Cannot open file: {}", e)))?;
55
56        file.write_all(level.as_u8().to_string().as_bytes())
57            .map_err(|e| Error::IoError(format!("Failed to set brightness level: {}", e)))?;
58
59        Ok(())
60    }
61
62    pub fn get() -> Result<LightLevel, Error> {
63        Self::ensure_root()?;
64
65        let mut file = std::fs::OpenOptions::new()
66            .read(true)
67            .open(BRIGHTNESS_PATH)
68            .map_err(|e| Error::FileNotFound(format!("Cannot open file: {}", e)))?;
69
70        let mut content = String::new();
71        file.read_to_string(&mut content)
72            .map_err(|e| Error::IoError(format!("Failed to get brightness level: {}", e)))?;
73
74        let level: u8 = content
75            .trim()
76            .parse()
77            .map_err(|_| Error::InvalidLevel(format!("Invalid brightness value: {}", content)))?;
78
79        LightLevel::from_u8(level)
80    }
81}