refactor: split 832-line main.rs into focused modules
config (constants), usb (driver + tasks), led (NeoPixel anims), mouse (cursor anims + HID), kbd (F13 wake + HID), chart (statechart + Ctx/Ev + JigglyActions). main.rs is now just Irqs + USB builder + chart.run(). No behavior change — comments, deadlines, and tunables preserved verbatim. Builds clean on default and --features embassy,defmt; clippy -D warnings passes.
This commit is contained in:
+163
@@ -0,0 +1,163 @@
|
||||
//! Mouse HID: report send + the family of cursor animations
|
||||
//! (wake-shake, spinner, eased spirals).
|
||||
|
||||
use core::f32::consts::PI;
|
||||
|
||||
use embassy_rp::clocks::RoscRng;
|
||||
use embassy_time::{Duration as EDuration, Timer, with_timeout};
|
||||
use embassy_usb::class::hid::HidWriter;
|
||||
use libm::{cosf, powf, roundf, sinf};
|
||||
use usbd_hid::descriptor::MouseReport;
|
||||
|
||||
use crate::chart::Ev;
|
||||
use crate::config::{
|
||||
ANIM_FRAME, EASE_POW, FINAL_SPIRAL_FRAMES, FINAL_SPIRAL_RADIUS_START, FINAL_SPIRAL_TURNS,
|
||||
RUN_CIRCLES, RUN_FRAMES_PER_CIRCLE, RUN_RADIUS, SPIRAL_RADIUS_END, WAKE_AMPLITUDE,
|
||||
WAKE_FRAMES_PER_HALF, WAKE_JITTER, WAKE_OSCILLATIONS, WARN5_FRAMES, WARN5_RADIUS_START,
|
||||
WARN5_TURNS, WARN10_FRAMES, WARN10_RADIUS_START, WARN10_TURNS,
|
||||
};
|
||||
use crate::usb::UsbDriver;
|
||||
|
||||
pub(crate) type MouseHid = HidWriter<'static, UsbDriver, 5>;
|
||||
|
||||
// Frantic horizontal mouse shake. Plain oneshot helper — the action method
|
||||
// that calls this races it against MOUSE_WAKE_DEADLINE.
|
||||
pub(crate) async fn wake_with_mouse(mouse: &mut MouseHid) {
|
||||
let period_frames = (WAKE_FRAMES_PER_HALF * 2) as f32;
|
||||
let total_frames = WAKE_OSCILLATIONS * WAKE_FRAMES_PER_HALF * 2;
|
||||
let mut prev_x: f32 = 0.0;
|
||||
let mut prev_y: f32 = 0.0;
|
||||
let mut acc_x: f32 = 0.0;
|
||||
let mut acc_y: f32 = 0.0;
|
||||
for f in 0..total_frames {
|
||||
let phase = (f as f32) * 2.0 * PI / period_frames;
|
||||
let next_x = WAKE_AMPLITUDE * sinf(phase);
|
||||
let jitter = ((RoscRng::next_u8() as f32) / 255.0 - 0.5) * 2.0 * WAKE_JITTER;
|
||||
let next_y = jitter;
|
||||
let (dx, dy, used_x, used_y) = step_delta(prev_x, prev_y, next_x, next_y, acc_x, acc_y);
|
||||
acc_x = used_x;
|
||||
acc_y = used_y;
|
||||
send_mouse(mouse, dx, dy).await;
|
||||
prev_x = next_x;
|
||||
prev_y = next_y;
|
||||
Timer::after(ANIM_FRAME).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn animate_spinner(mouse: &mut MouseHid) -> Ev {
|
||||
let total_frames = RUN_CIRCLES * RUN_FRAMES_PER_CIRCLE;
|
||||
let period_frames = RUN_FRAMES_PER_CIRCLE as f32;
|
||||
let mut prev_x: f32 = 0.0;
|
||||
let mut prev_y: f32 = 0.0;
|
||||
let mut acc_x: f32 = 0.0;
|
||||
let mut acc_y: f32 = 0.0;
|
||||
for f in 0..total_frames {
|
||||
let angle = (f as f32) * 2.0 * PI / period_frames;
|
||||
let next_x = RUN_RADIUS * sinf(angle);
|
||||
let next_y = RUN_RADIUS * (1.0 - cosf(angle));
|
||||
let (dx, dy, used_x, used_y) = step_delta(prev_x, prev_y, next_x, next_y, acc_x, acc_y);
|
||||
acc_x = used_x;
|
||||
acc_y = used_y;
|
||||
send_mouse(mouse, dx, dy).await;
|
||||
prev_x = next_x;
|
||||
prev_y = next_y;
|
||||
Timer::after(ANIM_FRAME).await;
|
||||
}
|
||||
Ev::SpinDone
|
||||
}
|
||||
|
||||
async fn animate_spiral(
|
||||
mouse: &mut MouseHid,
|
||||
radius_start: f32,
|
||||
radius_end: f32,
|
||||
turns: f32,
|
||||
frames: u32,
|
||||
) {
|
||||
let mut prev_x: f32 = 0.0;
|
||||
let mut prev_y: f32 = 0.0;
|
||||
let mut acc_x: f32 = 0.0;
|
||||
let mut acc_y: f32 = 0.0;
|
||||
for f in 0..frames {
|
||||
let t = (f as f32) / (frames as f32);
|
||||
let u = powf(t, EASE_POW);
|
||||
let angle = u * 2.0 * PI * turns;
|
||||
let radius = radius_start + (radius_end - radius_start) * u;
|
||||
// Subtract starting offset so the spiral begins at the cursor's entry point.
|
||||
let next_x = radius * cosf(angle) - radius_start;
|
||||
let next_y = radius * sinf(angle);
|
||||
let (dx, dy, used_x, used_y) = step_delta(prev_x, prev_y, next_x, next_y, acc_x, acc_y);
|
||||
acc_x = used_x;
|
||||
acc_y = used_y;
|
||||
send_mouse(mouse, dx, dy).await;
|
||||
prev_x = next_x;
|
||||
prev_y = next_y;
|
||||
Timer::after(ANIM_FRAME).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn animate_final_spiral(mouse: &mut MouseHid) -> Ev {
|
||||
animate_spiral(
|
||||
mouse,
|
||||
FINAL_SPIRAL_RADIUS_START,
|
||||
SPIRAL_RADIUS_END,
|
||||
FINAL_SPIRAL_TURNS,
|
||||
FINAL_SPIRAL_FRAMES,
|
||||
)
|
||||
.await;
|
||||
Ev::SpiralDone
|
||||
}
|
||||
|
||||
pub(crate) async fn animate_warning_5(mouse: &mut MouseHid) -> Ev {
|
||||
animate_spiral(
|
||||
mouse,
|
||||
WARN5_RADIUS_START,
|
||||
SPIRAL_RADIUS_END,
|
||||
WARN5_TURNS,
|
||||
WARN5_FRAMES,
|
||||
)
|
||||
.await;
|
||||
Ev::WarnDone
|
||||
}
|
||||
|
||||
pub(crate) async fn animate_warning_10(mouse: &mut MouseHid) -> Ev {
|
||||
animate_spiral(
|
||||
mouse,
|
||||
WARN10_RADIUS_START,
|
||||
SPIRAL_RADIUS_END,
|
||||
WARN10_TURNS,
|
||||
WARN10_FRAMES,
|
||||
)
|
||||
.await;
|
||||
Ev::WarnDone
|
||||
}
|
||||
|
||||
// Translate continuous (next_x, next_y) target into clamped i8 deltas while
|
||||
// carrying sub-pixel residue forward, so a 90-frame circle of radius 50 doesn't
|
||||
// lose ~half its motion to truncation. Returns (dx, dy, residual_x, residual_y).
|
||||
fn step_delta(
|
||||
prev_x: f32,
|
||||
prev_y: f32,
|
||||
next_x: f32,
|
||||
next_y: f32,
|
||||
acc_x: f32,
|
||||
acc_y: f32,
|
||||
) -> (i8, i8, f32, f32) {
|
||||
let want_x = (next_x - prev_x) + acc_x;
|
||||
let want_y = (next_y - prev_y) + acc_y;
|
||||
let dx = roundf(want_x.clamp(-127.0, 127.0)) as i8;
|
||||
let dy = roundf(want_y.clamp(-127.0, 127.0)) as i8;
|
||||
(dx, dy, want_x - dx as f32, want_y - dy as f32)
|
||||
}
|
||||
|
||||
// ── HID primitive ──────────────────────────────────────────────────
|
||||
|
||||
pub(crate) async fn send_mouse(mouse: &mut MouseHid, x: i8, y: i8) {
|
||||
let report = MouseReport {
|
||||
buttons: 0,
|
||||
x,
|
||||
y,
|
||||
wheel: 0,
|
||||
pan: 0,
|
||||
};
|
||||
let _ = with_timeout(EDuration::from_secs(3), mouse.write_serialize(&report)).await;
|
||||
}
|
||||
Reference in New Issue
Block a user