cadmus_core/frontlight/
natural.rs

1use super::{Frontlight, LightLevels};
2use crate::device::{Model, CURRENT_DEVICE};
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: f32,
65    warmth: f32,
66    values: FxHashMap<LightColor, File>,
67    powers: FxHashMap<LightColor, File>,
68    maxima: FxHashMap<LightColor, i16>,
69}
70
71impl NaturalFrontlight {
72    pub fn new(intensity: f32, warmth: f32) -> 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) {
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).unwrap();
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).unwrap();
113    }
114
115    fn update(&mut self, intensity: f32, warmth: f32) {
116        let i = intensity / 100.0;
117        let w = warmth / 100.0;
118        let white = 80.0 * i * (1.0 - w).sqrt();
119        self.set(LightColor::White, white);
120
121        if self.values.len() == 3 {
122            let green = 64.0 * (w * i).sqrt();
123            let red = if green == 0.0 {
124                0.0
125            } else {
126                green + 20.0 + 7.0 * (1.0 - green / 64.0) + w * 4.0
127            };
128            self.set(LightColor::Red, red);
129            self.set(LightColor::Green, green);
130        } else {
131            let orange = 95.0 * (w * i).sqrt();
132            self.set(LightColor::Orange, orange);
133        }
134
135        self.intensity = intensity;
136        self.warmth = warmth;
137    }
138}
139
140impl Frontlight for NaturalFrontlight {
141    fn set_intensity(&mut self, value: f32) {
142        let warmth = self.warmth;
143        self.update(value, warmth);
144    }
145
146    fn set_warmth(&mut self, value: f32) {
147        let intensity = self.intensity;
148        self.update(intensity, value);
149    }
150
151    fn levels(&self) -> LightLevels {
152        LightLevels {
153            intensity: self.intensity,
154            warmth: self.warmth,
155        }
156    }
157}