cadmus_core/frontlight/
natural.rs1use 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
14const FRONTLIGHT_WHITE_A: &str = "lm3630a_led1b";
16const FRONTLIGHT_RED_A: &str = "lm3630a_led1a";
17const FRONTLIGHT_GREEN_A: &str = "lm3630a_ledb";
18
19const 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}