Add fado out effect to button presses

This commit is contained in:
2023-05-21 17:24:42 +02:00
parent 5c68b483af
commit fa3b1502d7
3 changed files with 101 additions and 57 deletions

View File

@ -1,6 +1,6 @@
use core::cmp::min; use core::cmp::min;
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Instant, Timer};
use futures::{select_biased, FutureExt}; use futures::{select_biased, FutureExt};
use crate::{util::wheel, ws2812::Rgb}; use crate::{util::wheel, ws2812::Rgb};
@ -19,18 +19,41 @@ const IDLE_BRIGHTNESS_RAMPUP: Duration = Duration::from_secs(120);
const MAX_IDLE_BRIGHTESS: f32 = 1.0; const MAX_IDLE_BRIGHTESS: f32 = 1.0;
const MIN_IDLE_BRIGHTESS: f32 = 0.05; const MIN_IDLE_BRIGHTESS: f32 = 0.05;
#[derive(Clone, Copy)]
enum LightState {
Solid(Rgb),
SolidThenFade {
color: Rgb,
solid_until: Instant,
fade_by: f32,
},
FadeBy(f32),
None,
}
#[embassy_executor::task] #[embassy_executor::task]
pub(super) async fn task(mut events: KbEvents, state: &'static State) { pub(super) async fn task(mut events: KbEvents, state: &'static State) {
let mut lights: [LightState; SWITCH_COUNT] = [LightState::None; SWITCH_COUNT];
let mut next_frame = Instant::now();
let mut idle_at = Instant::now() + UNTIL_IDLE;
loop { loop {
select_biased! { select_biased! {
event = events.recv().fuse() => handle_event(event, state).await, event = events.recv().fuse() => {
_ = Timer::after(UNTIL_IDLE).fuse() => { handle_event(event, state, &mut lights).await;
idle_at = Instant::now() + UNTIL_IDLE;
}
_ = Timer::at(next_frame).fuse() => {
tick(state, &mut lights).await;
next_frame = Instant::now() + Duration::from_millis(16);
}
_ = Timer::at(idle_at).fuse() => {
select_biased! { select_biased! {
event = events.recv().fuse() => { event = events.recv().fuse() => {
state.lights.update(|lights| { state.lights.update(|lights| {
lights.iter_mut().for_each(|rgb| *rgb = Rgb::new(0, 0, 0)); lights.iter_mut().for_each(|rgb| *rgb = Rgb::new(0, 0, 0));
}).await; }).await;
handle_event(event, state).await; handle_event(event, state, &mut lights).await;
idle_at = Instant::now() + UNTIL_IDLE;
} }
_ = idle_animation(state).fuse() => {} _ = idle_animation(state).fuse() => {}
} }
@ -39,6 +62,44 @@ pub(super) async fn task(mut events: KbEvents, state: &'static State) {
} }
} }
async fn tick(state: &'static State, lights: &mut [LightState; SWITCH_COUNT]) {
let now = Instant::now();
state
.lights
.update(|rgbs| {
for (button, light) in lights.iter_mut().enumerate() {
let Some(&led_id) = state.led_map.get(button) else { continue; };
let Some(rgb) = rgbs.get_mut(led_id) else { continue; };
match &*light {
LightState::None => {}
LightState::FadeBy(fade) => {
let [r, g, b] = rgb
.components()
.map(|c| ((c as f32) * fade.clamp(0.0, 1.0)) as u8);
*rgb = Rgb::new(r, g, b);
if *rgb == Rgb::new(0, 0, 0) {
*light = LightState::None;
}
}
&LightState::Solid(color) => *rgb = color,
&LightState::SolidThenFade {
color,
solid_until,
fade_by,
} => {
*rgb = color;
if now >= solid_until {
*light = LightState::FadeBy(fade_by);
}
}
}
}
})
.await;
}
async fn idle_animation(state: &'static State) { async fn idle_animation(state: &'static State) {
for tick in 0.. { for tick in 0.. {
const FRAMETIME: Duration = Duration::from_millis(16); const FRAMETIME: Duration = Duration::from_millis(16);
@ -56,7 +117,7 @@ async fn idle_animation(state: &'static State) {
for (n, &i) in state.led_map.iter().enumerate() { for (n, &i) in state.led_map.iter().enumerate() {
let Some(light) = lights.get_mut(i) else { continue }; let Some(light) = lights.get_mut(i) else { continue };
let (r, g, b) = wheel( let [r, g, b] = wheel(
(n as u64 * IDLE_ANIMATION_KEY_OFFSET + tick * IDLE_ANIMATION_SPEED) as u8, (n as u64 * IDLE_ANIMATION_KEY_OFFSET + tick * IDLE_ANIMATION_SPEED) as u8,
) )
.components(); .components();
@ -74,35 +135,32 @@ async fn idle_animation(state: &'static State) {
} }
} }
async fn handle_event(event: Event, state: &'static State) { async fn handle_event(
let set_button_led = |color: Rgb| { event: Event,
let led_num = state.led_map.get(event.source_button).copied(); state: &'static State,
move |leds: &mut [Rgb; SWITCH_COUNT]| { lights: &mut [LightState; SWITCH_COUNT],
if event.source != state.half { ) {
return;
}
if let Some(led) = led_num.and_then(|i| leds.get_mut(i)) {
*led = color;
}
}
};
match event.kind { match event.kind {
EventKind::PressKey(_) => { EventKind::PressKey(_) => {
state if state.half != event.source {
.lights return;
.update(set_button_led(Rgb::new(0, 150, 0))) }
.await; let Some(light) = lights.get_mut(event.source_button) else { return; };
*light = LightState::Solid(Rgb::new(0, 150, 0));
} }
EventKind::PressModifier(_) => { EventKind::PressModifier(_) => {
state if state.half != event.source {
.lights return;
.update(set_button_led(Rgb::new(0, 0, 150))) }
.await; let Some(light) = lights.get_mut(event.source_button) else { return; };
*light = LightState::Solid(Rgb::new(0, 0, 150));
} }
EventKind::ReleaseKey(_) | EventKind::ReleaseModifier(_) => { EventKind::ReleaseKey(_) | EventKind::ReleaseModifier(_) => {
state.lights.update(set_button_led(Rgb::new(0, 0, 0))).await; if state.half != event.source {
return;
}
let Some(light) = lights.get_mut(event.source_button) else { return; };
*light = LightState::FadeBy(0.85);
} }
EventKind::SetLayer(layer) => { EventKind::SetLayer(layer) => {
let layer = min(layer, state.layers.len().saturating_sub(1) as u16); let layer = min(layer, state.layers.len().saturating_sub(1) as u16);
@ -124,29 +182,15 @@ async fn handle_event(event: Event, state: &'static State) {
} }
}; };
state let solid_until = Instant::now() + Duration::from_millis(200);
.lights
.update(|leds| {
for &button in buttons_to_light_up { for &button in buttons_to_light_up {
let Some(&led_id) = state.led_map.get(button) else { continue; }; let Some(light) = lights.get_mut(button) else { continue; };
let Some(rgb) = leds.get_mut(led_id) else { continue; }; *light = LightState::SolidThenFade {
*rgb = Rgb::new(100, 0, 100); color: Rgb::new(120, 0, 120),
} solid_until,
}) fade_by: 0.85,
.await; }
}
Timer::after(Duration::from_millis(200)).await;
state
.lights
.update(|leds| {
for &button in buttons_to_light_up {
let Some(&led_id) = state.led_map.get(button) else { continue; };
let Some(rgb) = leds.get_mut(led_id) else { continue; };
*rgb = Rgb::new(0, 0, 0);
}
})
.await;
} }
} }
} }

View File

@ -106,7 +106,7 @@ async fn uart_task(uart: BufferedUart<'static, UART0>, this_half: Half, mut even
let tx_task = async { let tx_task = async {
let mut buf = [0u8; 256 + HEADER_LEN]; let mut buf = [0u8; 256 + HEADER_LEN];
let mut counter = 1; let mut counter = 1u8;
loop { loop {
// forward messages to the other keyboard half // forward messages to the other keyboard half
let event = events_rx.recv().await; let event = events_rx.recv().await;
@ -126,7 +126,7 @@ async fn uart_task(uart: BufferedUart<'static, UART0>, this_half: Half, mut even
// add a "random" value to feed the crc // add a "random" value to feed the crc
let random = counter; let random = counter;
counter += 1; counter = counter.wrapping_add(1);
let len = serialized.len() as u8; let len = serialized.len() as u8;

View File

@ -105,9 +105,9 @@ impl Rgb {
/// Get the red, green, and blue components of this Rgb. /// Get the red, green, and blue components of this Rgb.
#[inline(always)] #[inline(always)]
pub const fn components(&self) -> (u8, u8, u8) { pub const fn components(&self) -> [u8; 3] {
let [g, r, b, _] = self.0.to_be_bytes(); let [g, r, b, _] = self.0.to_be_bytes();
(r, g, b) [r, g, b]
} }
#[inline(always)] #[inline(always)]
@ -119,7 +119,7 @@ impl Rgb {
impl Debug for Rgb { impl Debug for Rgb {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (r, g, b) = self.components(); let [r, g, b] = self.components();
f.debug_tuple("Rgb").field(&r).field(&g).field(&b).finish() f.debug_tuple("Rgb").field(&r).field(&g).field(&b).finish()
} }
} }
@ -128,7 +128,7 @@ impl Div<u8> for Rgb {
type Output = Rgb; type Output = Rgb;
fn div(self, d: u8) -> Self::Output { fn div(self, d: u8) -> Self::Output {
let (r, g, b) = self.components(); let [r, g, b] = self.components();
Rgb::new(r / d, g / d, b / d) Rgb::new(r / d, g / d, b / d)
} }
} }