feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
+74
@@ -0,0 +1,74 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use proc_macro2::{TokenStream, TokenTree};
|
||||
use quote::{ToTokens, TokenStreamExt};
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::{braced, bracketed, token, AttrStyle, Attribute, Signature, Token, Visibility};
|
||||
|
||||
pub fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
|
||||
tokens.extend(error.into_compile_error());
|
||||
tokens
|
||||
}
|
||||
|
||||
pub fn error<A: ToTokens, T: Display>(s: &mut TokenStream, obj: A, msg: T) {
|
||||
s.extend(syn::Error::new_spanned(obj.into_token_stream(), msg).into_compile_error())
|
||||
}
|
||||
|
||||
/// Function signature and body.
|
||||
///
|
||||
/// Same as `syn`'s `ItemFn` except we keep the body as a TokenStream instead of
|
||||
/// parsing it. This makes the macro not error if there's a syntax error in the body,
|
||||
/// which helps IDE autocomplete work better.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ItemFn {
|
||||
pub attrs: Vec<Attribute>,
|
||||
pub vis: Visibility,
|
||||
pub sig: Signature,
|
||||
pub brace_token: token::Brace,
|
||||
pub body: TokenStream,
|
||||
}
|
||||
|
||||
impl Parse for ItemFn {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let mut attrs = input.call(Attribute::parse_outer)?;
|
||||
let vis: Visibility = input.parse()?;
|
||||
let sig: Signature = input.parse()?;
|
||||
|
||||
let content;
|
||||
let brace_token = braced!(content in input);
|
||||
while content.peek(Token![#]) && content.peek2(Token![!]) {
|
||||
let content2;
|
||||
attrs.push(Attribute {
|
||||
pound_token: content.parse()?,
|
||||
style: AttrStyle::Inner(content.parse()?),
|
||||
bracket_token: bracketed!(content2 in content),
|
||||
meta: content2.parse()?,
|
||||
});
|
||||
}
|
||||
|
||||
let mut body = Vec::new();
|
||||
while !content.is_empty() {
|
||||
body.push(content.parse::<TokenTree>()?);
|
||||
}
|
||||
let body = body.into_iter().collect();
|
||||
|
||||
Ok(ItemFn {
|
||||
attrs,
|
||||
vis,
|
||||
sig,
|
||||
brace_token,
|
||||
body,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ItemFn {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
tokens.append_all(self.attrs.iter().filter(|a| matches!(a.style, AttrStyle::Outer)));
|
||||
self.vis.to_tokens(tokens);
|
||||
self.sig.to_tokens(tokens);
|
||||
self.brace_token.surround(tokens, |tokens| {
|
||||
tokens.append_all(self.body.clone());
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user