1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! Backlight functionality
use smart_leds::{
hsv::{
hsv2rgb,
Hsv,
},
RGB8,
};
use crate::{
key::PhysKey,
serial::Msg,
};
use futures::prelude::*;
/// Trait representing an effect to display on the backlight.
pub trait BacklightEffect {
/// Update the internal state of the effect based on a key press or release
fn on_key_event(&mut self, key: PhysKey);
/**
Update the internal state of the effect when a backlight 'tick' occurs
The length of a tick depends on the frequency of the timer controlling the
backlight system
*/
fn on_tick(&mut self);
/**
Returns the colour of the LED at the specified position based on the
current state of the effect
*/
fn get_hsv(&self, position: (u8, u8)) -> Option<Hsv>;
/**
Triggered when a "Backlight effect change" message is received.
In general, this should not do anything. It is only used for effects
which are unions of other effects
*/
fn on_backlight_change(&mut self) {}
}
/// Mapping between LED indices and a physical location on the keyboard
pub trait BacklightMap {
/// Return the position of the specified LED
fn translate(&self, led: usize) -> Option<(u8, u8)>;
}
/// Trait for a struct that can drive an RGB led strip
pub trait RgbDriver {
/// Prepare to set the colour of the specified LED
fn prepare_color(&mut self, index: usize, color: RGB8);
/// Transmit all the configured colours
fn transmit(&mut self);
}
/// A struct for controlling the keyboard backlight.
pub struct BacklightManager<T> {
driver: T,
led_count: usize,
}
impl<T> BacklightManager<T>
where
T: RgbDriver,
{
/// Initialise the RGB manager
pub fn new(driver: T, led_count: usize) -> Self {
Self { driver, led_count }
}
/**
Runs the RGB manager.
Consumes keyboard messages from `msg_rx` which are used to update the
internal state of effects.
Every tick of `timer` updates the effect, then sends the resulting colors
to the leds.
*/
pub async fn run<S, Ti>(
&mut self,
msg_rx: S,
timer: Ti,
led_map: impl BacklightMap,
mut effect: impl BacklightEffect,
) where
S: Stream<Item = Msg> + Unpin,
Ti: Stream + Unpin,
{
// Combine the timer and message future streams
let mut event = stream::select(timer.map(|_| Event::AnimationTick), msg_rx.map(Event::Msg));
loop {
match event.next().await {
Some(Event::AnimationTick) => {
effect.on_tick();
for i in 0..self.led_count {
let hsv = led_map.translate(i).and_then(|pos| effect.get_hsv(pos));
let color = match hsv {
Some(hsv) => hsv2rgb(hsv),
None => RGB8 { r: 0, g: 0, b: 0 },
};
self.driver.prepare_color(i, color);
}
self.driver.transmit();
}
Some(Event::Msg(Msg::KeyEvent(e))) => {
effect.on_key_event(e);
}
Some(_) => {}
None => unreachable!(),
}
}
}
}
/// Compound type for events from multiple backlight related futures.
enum Event {
AnimationTick,
Msg(Msg),
}