pub mod drivers;
mod renderers;
#[cfg(feature = "oled_async")]
pub use display_interface_i2c;
use embassy_futures::select::{Either, Either3, select, select3};
use embassy_time::{Duration, Instant, Ticker, Timer};
use embedded_graphics::prelude::*;
#[cfg(feature = "lcd_async")]
pub use lcd_async;
#[cfg(feature = "oled_async")]
pub use oled_async;
pub use renderers::{LogoRenderer, OledRenderer};
use rmk_macro::processor;
#[cfg(feature = "_ble")]
use rmk_types::ble::BleStatus;
use rmk_types::modifier::ModifierCombination;
#[cfg(feature = "ssd1306")]
pub use ssd1306;
use crate::core_traits::Runnable;
#[cfg(feature = "_ble")]
use crate::event::ConnectionStatusChangeEvent;
#[cfg(all(feature = "split", feature = "_ble"))]
use crate::event::PeripheralBatteryEvent;
use crate::event::{
BatteryStatusEvent, KeyboardEvent, LayerChangeEvent, LedIndicatorEvent, ModifierEvent, SleepStateEvent,
WpmUpdateEvent,
};
#[cfg(feature = "split")]
use crate::event::{CentralConnectedEvent, PeripheralConnectedEvent};
use crate::processor::Processor;
pub struct RenderContext {
pub layer: u8,
pub wpm: u16,
pub caps_lock: bool,
pub num_lock: bool,
pub battery: BatteryStatusEvent,
pub sleeping: bool,
#[cfg(feature = "_ble")]
pub ble_status: BleStatus,
#[cfg(feature = "split")]
pub central_connected: bool,
#[cfg(feature = "split")]
pub peripherals_connected: [bool; crate::SPLIT_PERIPHERALS_NUM],
#[cfg(all(feature = "split", feature = "_ble"))]
pub peripheral_batteries: [BatteryStatusEvent; crate::SPLIT_PERIPHERALS_NUM],
pub modifiers: ModifierCombination,
pub key_pressed: bool,
pub key_press_latch: bool,
}
impl Default for RenderContext {
fn default() -> Self {
Self {
layer: 0,
wpm: 0,
caps_lock: false,
num_lock: false,
battery: BatteryStatusEvent(rmk_types::battery::BatteryStatus::Unavailable),
sleeping: false,
#[cfg(feature = "_ble")]
ble_status: BleStatus::default(),
#[cfg(feature = "split")]
central_connected: false,
#[cfg(feature = "split")]
peripherals_connected: [false; crate::SPLIT_PERIPHERALS_NUM],
#[cfg(all(feature = "split", feature = "_ble"))]
peripheral_batteries: [BatteryStatusEvent(rmk_types::battery::BatteryStatus::Unavailable);
crate::SPLIT_PERIPHERALS_NUM],
modifiers: ModifierCombination::new(),
key_pressed: false,
key_press_latch: false,
}
}
}
pub trait DisplayDriver: DrawTarget {
fn init(&mut self) -> impl core::future::Future<Output = ()>;
fn flush(&mut self) -> impl core::future::Future<Output = ()>;
}
pub trait DisplayRenderer<C: PixelColor> {
fn render<D: DrawTarget<Color = C>>(&mut self, ctx: &RenderContext, display: &mut D);
}
#[processor(subscribe = [KeyboardEvent, LayerChangeEvent, WpmUpdateEvent, LedIndicatorEvent, ModifierEvent, BatteryStatusEvent, SleepStateEvent])]
#[cfg_attr(feature = "_ble", processor(subscribe = [ConnectionStatusChangeEvent]))]
#[cfg_attr(feature = "split", processor(subscribe = [PeripheralConnectedEvent, CentralConnectedEvent]))]
#[cfg_attr(all(feature = "split", feature = "_ble"), processor(subscribe = [PeripheralBatteryEvent]))]
#[::rmk::macros::runnable_generated]
pub struct DisplayProcessor<D, R = LogoRenderer>
where
D: DisplayDriver,
R: DisplayRenderer<D::Color>,
{
display: D,
renderer: R,
ctx: RenderContext,
initialized: bool,
last_render: Instant,
pending_render: bool,
min_render_interval: Duration,
render_interval: Option<Duration>,
}
impl<D> DisplayProcessor<D, LogoRenderer>
where
D: DisplayDriver,
LogoRenderer: DisplayRenderer<D::Color>,
{
pub fn new(display: D) -> Self {
Self::with_renderer(display, LogoRenderer)
}
}
impl<D, R> DisplayProcessor<D, R>
where
D: DisplayDriver,
R: DisplayRenderer<D::Color>,
{
pub fn with_renderer(display: D, renderer: R) -> Self {
Self {
display,
renderer,
ctx: RenderContext::default(),
initialized: false,
last_render: Instant::from_ticks(0),
pending_render: false,
min_render_interval: Duration::from_millis(33),
render_interval: None,
}
}
pub fn with_render_interval(mut self, interval: Duration) -> Self {
self.render_interval = Some(interval);
self
}
pub fn with_min_render_interval(mut self, interval: Duration) -> Self {
self.min_render_interval = interval;
self
}
async fn poll(&mut self) {
self.pending_render = true;
self.render().await;
}
fn next_render_wait(&self) -> Option<Duration> {
if self.pending_render {
Some(
self.min_render_interval
.checked_sub(self.last_render.elapsed())
.unwrap_or(Duration::MIN),
)
} else {
None
}
}
async fn render(&mut self) {
let now = Instant::now();
if now.duration_since(self.last_render) < self.min_render_interval {
self.pending_render = true;
return;
}
if !self.initialized {
self.display.init().await;
self.initialized = true;
}
self.renderer.render(&self.ctx, &mut self.display);
self.ctx.key_press_latch = false;
self.display.flush().await;
self.pending_render = false;
self.last_render = Instant::now();
}
async fn on_layer_change_event(&mut self, event: LayerChangeEvent) {
self.ctx.layer = event.0;
self.render().await;
}
async fn on_wpm_update_event(&mut self, event: WpmUpdateEvent) {
self.ctx.wpm = event.0;
}
async fn on_led_indicator_event(&mut self, event: LedIndicatorEvent) {
self.ctx.caps_lock = event.0.caps_lock();
self.ctx.num_lock = event.0.num_lock();
self.render().await;
}
async fn on_battery_status_event(&mut self, event: BatteryStatusEvent) {
self.ctx.battery = event;
self.render().await;
}
async fn on_keyboard_event(&mut self, event: KeyboardEvent) {
self.ctx.key_pressed = event.pressed;
if event.pressed {
self.ctx.key_press_latch = true;
}
self.render().await;
}
async fn on_modifier_event(&mut self, event: ModifierEvent) {
self.ctx.modifiers = event.modifier;
self.render().await;
}
async fn on_sleep_state_event(&mut self, event: SleepStateEvent) {
self.ctx.sleeping = event.0;
self.render().await;
}
#[cfg(feature = "_ble")]
async fn on_connection_status_change_event(&mut self, event: ConnectionStatusChangeEvent) {
self.ctx.ble_status = event.0.ble;
self.render().await;
}
#[cfg(feature = "split")]
async fn on_peripheral_connected_event(&mut self, event: PeripheralConnectedEvent) {
if let Some(slot) = self.ctx.peripherals_connected.get_mut(event.id) {
*slot = event.connected;
}
self.render().await;
}
#[cfg(feature = "split")]
async fn on_central_connected_event(&mut self, event: CentralConnectedEvent) {
self.ctx.central_connected = event.connected;
self.render().await;
}
#[cfg(all(feature = "split", feature = "_ble"))]
async fn on_peripheral_battery_event(&mut self, event: PeripheralBatteryEvent) {
if let Some(slot) = self.ctx.peripheral_batteries.get_mut(event.id) {
*slot = event.state;
}
self.render().await;
}
}
impl<D, R> Runnable for DisplayProcessor<D, R>
where
D: DisplayDriver,
R: DisplayRenderer<D::Color>,
{
async fn run(&mut self) -> ! {
use crate::event::EventSubscriber;
let mut sub = <Self as Processor>::subscriber();
#[cfg(feature = "_ble")]
{
self.ctx.ble_status = crate::state::current_ble_status();
}
self.pending_render = true;
self.render().await;
let mut ticker = self.render_interval.map(Ticker::every);
loop {
if !self.ctx.sleeping {
match (ticker.as_mut(), self.next_render_wait()) {
(Some(ticker), Some(wait)) => {
match select3(ticker.next(), Timer::after(wait), sub.next_event()).await {
Either3::First(_) => self.poll().await,
Either3::Second(_) => self.render().await,
Either3::Third(event) => self.process(event).await,
}
}
(Some(ticker), None) => match select(ticker.next(), sub.next_event()).await {
Either::First(_) => self.poll().await,
Either::Second(event) => self.process(event).await,
},
(None, Some(wait)) => match select(Timer::after(wait), sub.next_event()).await {
Either::First(_) => self.render().await,
Either::Second(event) => self.process(event).await,
},
(None, None) => {
let event = sub.next_event().await;
self.process(event).await;
}
}
} else {
let was_sleeping = self.ctx.sleeping;
let event = sub.next_event().await;
self.process(event).await;
if was_sleeping
&& !self.ctx.sleeping
&& let Some(ticker) = ticker.as_mut()
{
ticker.reset();
}
}
}
}
}