From 11c6a6ff85f8223e1f7fd68d98183048866763f7 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Mon, 20 Apr 2026 10:33:45 -0700 Subject: [PATCH] =?UTF-8?q?boot-anim:=20matrix-rain=20BG=20+=203s=E2=86=92?= =?UTF-8?q?2s=20runtime=20(boot=20into=20notepat=20faster)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two changes to the startup-fade animation (draw_startup_fade): 1. Matrix-rain background. Pseudo-CJK glyphs falling in columns — started as an homage to the "garbled-character" bug from when the firmware loaded a chain-loading EFI binary (splash.efi) directly and displayed its bytes as UTF-16 CJK. User liked the look, so kept as a proper boot aesthetic. Glyphs are random 7x9 pixel patterns drawn per-frame with per-column xorshift seeds (no CJK font needed). Bright white-green leader, fading green trail. Rain alpha is dimmed in day mode so the title stays legible; more prominent in dark mode where it gets the full matrix vibe. 2. Cut animation 180f (3s) → 120f (2s). Hold-period to accept keys dropped 60f (1s) → 40f (667ms). The 3-second hold was branding padding; the W hint is legible within 400ms. Shaves 1s off every cold boot without losing the install-during-boot window. Sub-effects scaled to the new total (subtitle fade 130→80, badges 60→40). Arpeggio boot notes at f=10/20/30/42 stay put — still within the 2s envelope, no retune needed. Co-Authored-By: Claude Opus 4.7 (1M context) --- fedac/native/src/ac-native.c | 89 +++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 11 deletions(-) diff --git a/fedac/native/src/ac-native.c b/fedac/native/src/ac-native.c index e3ca8e0ac3..1169abfb13 100644 --- a/fedac/native/src/ac-native.c +++ b/fedac/native/src/ac-native.c @@ -2441,12 +2441,31 @@ static int draw_startup_fade(ACGraph *graph, ACFramebuffer *screen, char greet_city[96]; read_cached_city(greet_city, sizeof greet_city); - // 180 frames = 3 second animation. - // W press → halt and show y/n confirmation - // Any other key → skip animation and start playing - int total_frames = 180; // 3 seconds + // 120 frames = 2 second animation (was 180/3s — shaved 1s off every boot). + // W press → halt and show y/n confirmation. + // Any other key → skip animation and start playing. + // Keys accepted after frame 40 (667ms hold so the W hint is legible). + int total_frames = 120; + int hold_frames = 40; int skip_anim = 0; int w_pressed = 0; + // Matrix-rain background state — persistent across frames. One entry + // per column; column width fixed at 10 px. + #define RAIN_MAX_COLS 128 + int rain_col_y[RAIN_MAX_COLS]; + int rain_col_speed[RAIN_MAX_COLS]; + unsigned int rain_col_seed[RAIN_MAX_COLS]; + { + unsigned int s = (unsigned int)time(NULL); + for (int c = 0; c < RAIN_MAX_COLS; c++) { + s ^= s << 13; s ^= s >> 17; s ^= s << 5; + rain_col_y[c] = -(int)(s % 400); + s ^= s << 13; s ^= s >> 17; s ^= s << 5; + rain_col_speed[c] = 2 + (int)(s % 4); + s ^= s << 13; s ^= s >> 17; s ^= s << 5; + rain_col_seed[c] = s; + } + } for (int f = 0; f < total_frames && !skip_anim && !w_pressed; f++) { double t = (double)f / (double)total_frames; @@ -2456,9 +2475,7 @@ static int draw_startup_fade(ACGraph *graph, ACFramebuffer *screen, for (int ki = 0; ki < key_fd_count; ki++) { while (read(key_fds[ki], &ev, sizeof(ev)) == sizeof(ev)) { if (ev.type == EV_KEY && ev.value == 1) { - // First 60 frames (1 second): ignore all keys - // Ensures W hint is visible before accepting input - if (f < 60) { + if (f < hold_frames) { fprintf(stderr, "[boot-anim] drained key %d at f=%d (hold period)\n", ev.code, f); continue; } @@ -2538,6 +2555,54 @@ static int draw_startup_fade(ACGraph *graph, ACFramebuffer *screen, int bg_b = start_b + (int)((target_b - start_b) * fade_t); graph_wipe(graph, (ACColor){(uint8_t)bg_r, (uint8_t)bg_g, (uint8_t)bg_b, 255}); + // Matrix-rain BG: pseudo-CJK glyphs falling in columns. Started + // as an homage to the "garbled-character" bug we hit when the + // firmware loaded a chain-loading EFI binary directly — user + // liked the look, so we're keeping it as a boot aesthetic. + // Glyphs are random 7x9 pixel patterns drawn per-frame with a + // per-column xorshift seed; no CJK font needed. Brighter leader + // + fading trail for the classic terminal-rain feel. + { + int col_w = 10; + int glyph_w = 7, glyph_h = 9; + int nc = screen->width / col_w; + if (nc > RAIN_MAX_COLS) nc = RAIN_MAX_COLS; + // Slight fade toward title, so rain doesn't fight the text. + int rain_alpha_scale = is_day ? 80 : 180; + for (int c = 0; c < nc; c++) { + rain_col_y[c] += rain_col_speed[c]; + if (rain_col_y[c] > screen->height + glyph_h * 12) { + unsigned int s = rain_col_seed[c]; + s ^= s << 13; s ^= s >> 17; s ^= s << 5; + rain_col_seed[c] = s; + rain_col_y[c] = -(int)(s % 300); + rain_col_speed[c] = 2 + (int)(s % 4); + } + int cx = c * col_w + 1; + // Trail of ~10 glyphs fading behind the leader. + for (int trail = 0; trail < 10; trail++) { + int gy = rain_col_y[c] - trail * glyph_h; + if (gy < -glyph_h || gy >= screen->height) continue; + int tb = (trail == 0) ? 220 : 180 - trail * 18; + if (tb < 15) continue; + int b = (tb * rain_alpha_scale) >> 8; + // Leader glows a bit whitish — classic matrix-rain tip. + ACColor cg = (trail == 0) + ? (ACColor){ (uint8_t)(b * 0.8), 255, (uint8_t)(b * 0.8), (uint8_t)b } + : (ACColor){ 40, (uint8_t)(b * 0.9 + 20), (uint8_t)(b * 0.4), (uint8_t)b }; + graph_ink(graph, cg); + // Random 7x9 bitmap from deterministic seed + column + trail + slow frame shimmer. + unsigned int gseed = rain_col_seed[c] ^ ((unsigned)trail * 2654435761u) ^ ((unsigned)(f / 4) * 2246822519u); + for (int row = 0; row < glyph_h; row++) { + for (int col = 0; col < glyph_w; col++) { + gseed ^= gseed << 13; gseed ^= gseed >> 17; gseed ^= gseed << 5; + if (gseed & 1) graph_plot(graph, cx + col, gy + row); + } + } + } + } + } + // Title — per-handle palette (fallback rainbow), animated pulse int alpha = (int)(255.0 * fade_t); if (alpha > 0) { @@ -2625,8 +2690,9 @@ static int draw_startup_fade(ACGraph *graph, ACFramebuffer *screen, #endif // Subtitle: "enjoy !" appears after frame 130 - if (f > 130) { - double sub_t = (double)(f - 130) / 30.0; + // Scaled for 120-frame total (was gated at 130 of 180). + if (f > 80) { + double sub_t = (double)(f - 80) / 20.0; if (sub_t > 1.0) sub_t = 1.0; int sub_alpha = (int)(180.0 * sub_t); graph_ink(graph, is_day @@ -2640,10 +2706,11 @@ static int draw_startup_fade(ACGraph *graph, ACFramebuffer *screen, } // Auth badges (bottom-left): pixel crab = Claude, pixel octocat = GitHub - if (f > 60 && alpha > 80) { + // Badges — scaled to appear earlier in the shorter animation. + if (f > 40 && alpha > 80) { int badge_x = 6; int badge_y = screen->height - 22; - double badge_t = (double)(f - 60) / 40.0; + double badge_t = (double)(f - 40) / 30.0; if (badge_t > 1.0) badge_t = 1.0; int ba = (int)(220.0 * badge_t); // badge alpha -- 2.54.0