Skip to main content

cadmus_core/frontlight/
natural.rs

1use super::{Frontlight, LightLevel, LightLevels};
2use crate::device::{CURRENT_DEVICE, Model};
3use anyhow::Error;
4use fxhash::FxHashMap;
5use lazy_static::lazy_static;
6use std::fs::File;
7use std::fs::OpenOptions;
8use std::io::Read;
9use std::io::Write;
10use std::path::PathBuf;
11
12const FRONTLIGHT_INTERFACE: &str = "/sys/class/backlight";
13
14// Aura ONE
15const FRONTLIGHT_WHITE_A: &str = "lm3630a_led1b";
16const FRONTLIGHT_RED_A: &str = "lm3630a_led1a";
17const FRONTLIGHT_GREEN_A: &str = "lm3630a_ledb";
18
19// Aura H₂O Edition 2
20const FRONTLIGHT_WHITE_B: &str = "lm3630a_ledb";
21const FRONTLIGHT_ORANGE_B: &str = "lm3630a_leda";
22
23const FRONTLIGHT_VALUE: &str = "brightness";
24const FRONTLIGHT_MAX_VALUE: &str = "max_brightness";
25const FRONTLIGHT_POWER: &str = "bl_power";
26
27const FRONTLIGHT_POWER_ON: i16 = 31;
28const FRONTLIGHT_POWER_OFF: i16 = 0;
29
30#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
31pub enum LightColor {
32    White,
33    Red,
34    Green,
35    Orange,
36}
37
38lazy_static! {
39    pub static ref FRONTLIGHT_DIRS: FxHashMap<LightColor, &'static str> = match CURRENT_DEVICE.model
40    {
41        Model::AuraONE | Model::AuraONELimEd => {
42            [
43                (LightColor::White, FRONTLIGHT_WHITE_A),
44                (LightColor::Red, FRONTLIGHT_RED_A),
45                (LightColor::Green, FRONTLIGHT_GREEN_A),
46            ]
47            .iter()
48            .cloned()
49            .collect()
50        }
51        _ => {
52            [
53                (LightColor::White, FRONTLIGHT_WHITE_B),
54                (LightColor::Orange, FRONTLIGHT_ORANGE_B),
55            ]
56            .iter()
57            .cloned()
58            .collect()
59        }
60    };
61}
62
63pub struct NaturalFrontlight {
64    intensity: LightLevel,
65    warmth: LightLevel,
66    values: FxHashMap<LightColor, File>,
67    powers: FxHashMap<LightColor, File>,
68    maxima: FxHashMap<LightColor, i16>,
69}
70
71impl NaturalFrontlight {
72    pub fn new(intensity: LightLevel, warmth: LightLevel) -> Result<NaturalFrontlight, Error> {
73        let mut maxima = FxHashMap::default();
74        let mut values = FxHashMap::default();
75        let mut powers = FxHashMap::default();
76        let base = PathBuf::from(FRONTLIGHT_INTERFACE);
77        for (light, name) in FRONTLIGHT_DIRS.iter() {
78            let dir = base.join(name);
79            let mut buf = String::new();
80            let mut file = File::open(dir.join(FRONTLIGHT_MAX_VALUE))?;
81            file.read_to_string(&mut buf)?;
82            maxima.insert(*light, buf.trim_end().parse()?);
83            let file = OpenOptions::new()
84                .write(true)
85                .open(dir.join(FRONTLIGHT_VALUE))?;
86            values.insert(*light, file);
87            let file = OpenOptions::new()
88                .write(true)
89                .open(dir.join(FRONTLIGHT_POWER))?;
90            powers.insert(*light, file);
91        }
92        Ok(NaturalFrontlight {
93            intensity,
94            warmth,
95            maxima,
96            values,
97            powers,
98        })
99    }
100
101    fn set(&mut self, c: LightColor, percent: f32) -> Result<(), Error> {
102        let max_value = self.maxima[&c] as f32;
103        let value = (percent.clamp(0.0, 100.0) / 100.0 * max_value) as i16;
104        let mut file = &self.values[&c];
105        write!(file, "{}", value)?;
106        let mut file = &self.powers[&c];
107        let power = if value > 0 {
108            FRONTLIGHT_POWER_ON
109        } else {
110            FRONTLIGHT_POWER_OFF
111        };
112        write!(file, "{}", power)?;
113        Ok(())
114    }
115
116    fn update(&mut self, intensity: LightLevel, warmth: LightLevel) -> Result<(), Error> {
117        let i = intensity.as_fraction();
118        let w = warmth.as_fraction();
119        let white = 80.0 * i * (1.0 - w).sqrt();
120        self.set(LightColor::White, white)?;
121
122        if self.values.len() == 3 {
123            let green = 64.0 * (w * i).sqrt();
124            let red = if green == 0.0 {
125                0.0
126            } else {
127                green + 20.0 + 7.0 * (1.0 - green / 64.0) + w * 4.0
128            };
129            self.set(LightColor::Red, red)?;
130            self.set(LightColor::Green, green)?;
131        } else {
132            let orange = 95.0 * (w * i).sqrt();
133            self.set(LightColor::Orange, orange)?;
134        }
135
136        self.intensity = intensity;
137        self.warmth = warmth;
138        Ok(())
139    }
140}
141
142impl Frontlight for NaturalFrontlight {
143    fn set_intensity(&mut self, value: LightLevel) -> Result<(), Error> {
144        let warmth = self.warmth;
145        self.update(value, warmth)
146    }
147
148    fn set_warmth(&mut self, value: LightLevel) -> Result<(), Error> {
149        let intensity = self.intensity;
150        self.update(intensity, value)
151    }
152
153    fn levels(&self) -> LightLevels {
154        LightLevels {
155            intensity: self.intensity,
156            warmth: self.warmth,
157        }
158    }
159}