diff --git a/systats.py b/systats.py index dff7b64..794ef51 100755 --- a/systats.py +++ b/systats.py @@ -1245,121 +1245,265 @@ def intro_splash(stdscr): """Animated boot splash inspired by Spectre/Pulse.""" curses.curs_set(0) stdscr.nodelay(False) - stdscr.timeout(30) + stdscr.timeout(20) init_colors() h, w = stdscr.getmaxyx() if h < 15 or w < 60: - return # skip intro on tiny terminals + return - # Gather system info for boot lines + # Gather system info cores = psutil.cpu_count() + cores_phys = psutil.cpu_count(logical=False) + mem = psutil.virtual_memory() + mem_str = f"{mem.total / (1024**3):.0f}GB" gpu_str = "NVIDIA" if NVIDIA_AVAILABLE else ("AMD" if os.path.exists(AMD_GPU_PATH) else "NONE") + hostname = os.uname().nodename try: temps_count = sum(len(v) for v in psutil.sensors_temperatures().values()) temps_str = f"{temps_count} sensors" except Exception: temps_str = "N/A" + try: + parts = len(psutil.disk_partitions(all=False)) + disk_str = f"{parts} volumes" + except Exception: + disk_str = "N/A" - glitch_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%&*!?<>/\\|{}[]~^" + glitch_chars = "@#$%&*!?<>/\\|{}[]~^=+:;0123456789" - # Phase 1: Boot lines - boot_start = time.time() - shown_lines = [] - - for line_tpl, delay in BOOT_LINES: - line = line_tpl.format(ver=VERSION, cores=cores, gpu=gpu_str, temps=temps_str) - target_time = boot_start + delay - while time.time() < target_time: - stdscr.getch() # drain input - time.sleep(0.02) - - shown_lines.append(line) + # ── Phase 1: Matrix rain background ── + rain_cols = [_random.randint(-h, 0) for _ in range(w)] + for frame in range(25): stdscr.erase() - start_y = (h - len(BOOT_LINES) - len(LOGO_ART) - 4) // 2 - for i, sl in enumerate(shown_lines): - col = C_LOW if "OK" in sl or "READY" in sl or "ONLINE" in sl or "detected" in sl else C_DIM - if "Electric Entropy" in sl: - col = C_ACCENT - safe_addstr(stdscr, start_y + i, (w - len(sl)) // 2, sl, - curses.color_pair(col)) + for col in range(w): + rain_cols[col] += 1 + if rain_cols[col] > h + 5: + rain_cols[col] = _random.randint(-5, 0) + for row_off in range(4): + ry = rain_cols[col] - row_off + if 0 <= ry < h: + ch = _random.choice(glitch_chars) + brightness = C_LOW if row_off == 0 else C_DIM + if row_off == 0: + brightness = C_LOW + try: + stdscr.addch(ry, col, ch, + curses.color_pair(brightness) | (curses.A_BOLD if row_off == 0 else curses.A_DIM)) + except curses.error: + pass stdscr.refresh() + stdscr.getch() + time.sleep(0.04) - time.sleep(0.3) + # ── Phase 2: Boot sequence with typing effect ── + boot_lines = [ + (f"> SYSTATS v{VERSION} // Electric Entropy Lab", C_ACCENT), + (f"> host: {hostname}", C_DIM), + (f"> scanning CPU... {cores_phys}C/{cores}T detected", C_LOW), + (f"> memory probe... {mem_str} OK", C_LOW), + (f"> GPU subsystem... {gpu_str}", C_LOW), + (f"> thermal sensors... {temps_str}", C_LOW), + (f"> disk volumes... {disk_str}", C_LOW), + (f"> network interfaces... ONLINE", C_LOW), + (f"> process table... READY", C_LOW), + ("> all systems nominal", C_TITLE), + ] - # Phase 2: Decrypt logo animation - logo_start_y = (h - len(LOGO_ART)) // 2 - # Find max logo width - max_logo_w = max(len(l) for l in LOGO_ART) + start_y = (h - len(boot_lines)) // 2 - 3 + typed_lines = [] - # Initialize with random chars for non-space positions - logo_state = [] - for line in LOGO_ART: - row = [] - for ch in line: - if ch == ' ' or ch == '\u2500': - row.append(ch) - else: - row.append(_random.choice(glitch_chars)) - logo_state.append(row) + for line_text, line_col in boot_lines: + typed_lines.append((line_text, line_col)) - # Decrypt columns left to right - total_cols = max_logo_w - decrypt_steps = 20 # frames for full decrypt - chars_per_step = max(1, total_cols // decrypt_steps) + # Type this line character by character + for char_idx in range(len(line_text) + 1): + stdscr.erase() - revealed = [set() for _ in LOGO_ART] - - for step in range(decrypt_steps + 5): - stdscr.erase() - - # Reveal columns up to this step - reveal_up_to = min(step * chars_per_step, total_cols) - - for row_idx, line in enumerate(LOGO_ART): - lx = (w - max_logo_w) // 2 - for col_idx, ch in enumerate(line): - if col_idx < reveal_up_to: - revealed[row_idx].add(col_idx) - - display = [] - for col_idx in range(len(line)): - if col_idx in revealed[row_idx]: - display.append(line[col_idx]) - elif line[col_idx] == ' ': - display.append(' ') - else: - display.append(_random.choice(glitch_chars)) - - text = "".join(display) - # Color: revealed chars in cyan, glitch in dim - for col_idx, ch in enumerate(text): - if col_idx >= w - lx - 1: - break - if ch == ' ': - continue - if col_idx in revealed[row_idx]: - attr = curses.color_pair(C_TITLE) | curses.A_BOLD - else: - attr = curses.color_pair(C_DIM) | curses.A_DIM + # Fading rain in background + for col in range(0, w, 3): + ry = _random.randint(0, h - 1) + ch = _random.choice(glitch_chars) try: - stdscr.addch(logo_start_y + row_idx, lx + col_idx, ch, attr) + stdscr.addch(ry, col, ch, curses.color_pair(C_DIM) | curses.A_DIM) except curses.error: pass - # Version + brand below logo - if step > decrypt_steps - 3: - ver_str = f"v{VERSION} // Electric Entropy Lab" - safe_addstr(stdscr, logo_start_y + len(LOGO_ART) + 1, (w - len(ver_str)) // 2, - ver_str, curses.color_pair(C_ACCENT) | curses.A_BOLD) + # Draw all completed lines + for i, (lt, lc) in enumerate(typed_lines[:-1]): + safe_addstr(stdscr, start_y + i, 2, lt, + curses.color_pair(lc)) + # Draw current line being typed + current_y = start_y + len(typed_lines) - 1 + partial = line_text[:char_idx] + safe_addstr(stdscr, current_y, 2, partial, curses.color_pair(line_col)) + + # Blinking cursor + if char_idx < len(line_text): + cursor_x = 2 + char_idx + if cursor_x < w: + try: + stdscr.addch(current_y, cursor_x, "\u2588", + curses.color_pair(C_TITLE) | curses.A_BOLD) + except curses.error: + pass + + stdscr.refresh() + stdscr.getch() + + # Variable typing speed + if line_text[char_idx - 1:char_idx] in ".:": + time.sleep(0.08) + elif _random.random() < 0.15: + time.sleep(0.06) + else: + time.sleep(0.015) + + time.sleep(0.15) + + time.sleep(0.4) + + # ── Phase 3: Glitch transition ── + for frame in range(6): + stdscr.erase() + for row in range(h): + offset = _random.randint(-3, 3) + for col in range(w): + try: + stdscr.addch(row, col, _random.choice(glitch_chars), + curses.color_pair(_random.choice([C_TITLE, C_LOW, C_DIM])) | curses.A_DIM) + except curses.error: + pass stdscr.refresh() - stdscr.getch() # drain - time.sleep(0.05) + time.sleep(0.04) - time.sleep(0.5) + # ── Phase 4: Logo decrypt ── + max_logo_w = max(len(l) for l in LOGO_ART) + logo_y = (h - len(LOGO_ART)) // 2 - 1 + logo_x = (w - max_logo_w) // 2 - # Phase 3: Flash and clear + # Build target grid and scrambled state + target = [] + scrambled = [] + for line in LOGO_ART: + t_row = list(line.ljust(max_logo_w)) + s_row = [] + for ch in t_row: + if ch == ' ': + s_row.append(' ') + else: + s_row.append(_random.choice(glitch_chars)) + target.append(t_row) + scrambled.append(s_row) + + # Reveal each character with multiple scramble frames before settling + total_chars = max_logo_w + reveal_order = list(range(total_chars)) + _random.shuffle(reveal_order) + + # Group into waves + wave_size = max(1, total_chars // 12) + waves = [reveal_order[i:i + wave_size] for i in range(0, len(reveal_order), wave_size)] + + revealed_cols = set() + + for wave_idx, wave in enumerate(waves): + # Several scramble frames per wave + for scramble_frame in range(4): + stdscr.erase() + + for row_idx in range(len(LOGO_ART)): + for col_idx in range(max_logo_w): + if logo_x + col_idx >= w - 1: + break + if col_idx in revealed_cols: + ch = target[row_idx][col_idx] + if ch != ' ': + try: + stdscr.addch(logo_y + row_idx, logo_x + col_idx, ch, + curses.color_pair(C_TITLE) | curses.A_BOLD) + except curses.error: + pass + elif col_idx in wave and scramble_frame >= 2: + ch = target[row_idx][col_idx] + if ch != ' ': + try: + stdscr.addch(logo_y + row_idx, logo_x + col_idx, ch, + curses.color_pair(C_TITLE) | curses.A_BOLD) + except curses.error: + pass + elif target[row_idx][col_idx] != ' ': + ch = _random.choice(glitch_chars) + try: + stdscr.addch(logo_y + row_idx, logo_x + col_idx, ch, + curses.color_pair(C_LOW if col_idx in wave else C_DIM) | curses.A_DIM) + except curses.error: + pass + + # Show version once past halfway + if wave_idx > len(waves) // 2: + ver_str = f"v{VERSION}" + brand_str = "Electric Entropy Lab" + safe_addstr(stdscr, logo_y + len(LOGO_ART) + 1, (w - len(ver_str)) // 2, + ver_str, curses.color_pair(C_ACCENT) | curses.A_BOLD) + safe_addstr(stdscr, logo_y + len(LOGO_ART) + 2, (w - len(brand_str)) // 2, + brand_str, curses.color_pair(C_DIM)) + + stdscr.refresh() + stdscr.getch() + time.sleep(0.035) + + revealed_cols.update(wave) + + # Final clean frame + stdscr.erase() + for row_idx, line in enumerate(LOGO_ART): + safe_addstr(stdscr, logo_y + row_idx, logo_x, line, + curses.color_pair(C_TITLE) | curses.A_BOLD) + ver_str = f"v{VERSION}" + brand_str = "Electric Entropy Lab" + lang_str = f"[{current_lang.upper()}]" + safe_addstr(stdscr, logo_y + len(LOGO_ART) + 1, (w - len(ver_str)) // 2, + ver_str, curses.color_pair(C_ACCENT) | curses.A_BOLD) + safe_addstr(stdscr, logo_y + len(LOGO_ART) + 2, (w - len(brand_str)) // 2, + brand_str, curses.color_pair(C_DIM)) + safe_addstr(stdscr, logo_y + len(LOGO_ART) + 3, (w - len(lang_str)) // 2, + lang_str, curses.color_pair(C_DIM)) + + # Scanning line effect + for scan_y in range(h): + try: + for sx in range(w): + stdscr.addch(scan_y, sx, ' ', curses.color_pair(C_HEADER)) + except curses.error: + pass + stdscr.refresh() + time.sleep(0.008) + + # Restore the line + if logo_y <= scan_y < logo_y + len(LOGO_ART): + row_idx = scan_y - logo_y + safe_addstr(stdscr, scan_y, logo_x, LOGO_ART[row_idx], + curses.color_pair(C_TITLE) | curses.A_BOLD) + elif scan_y == logo_y + len(LOGO_ART) + 1: + safe_addstr(stdscr, scan_y, (w - len(ver_str)) // 2, ver_str, + curses.color_pair(C_ACCENT) | curses.A_BOLD) + elif scan_y == logo_y + len(LOGO_ART) + 2: + safe_addstr(stdscr, scan_y, (w - len(brand_str)) // 2, brand_str, + curses.color_pair(C_DIM)) + elif scan_y == logo_y + len(LOGO_ART) + 3: + safe_addstr(stdscr, scan_y, (w - len(lang_str)) // 2, lang_str, + curses.color_pair(C_DIM)) + else: + for sx in range(w): + try: + stdscr.addch(scan_y, sx, ' ') + except curses.error: + pass + stdscr.refresh() + + time.sleep(0.6) stdscr.erase() stdscr.refresh() time.sleep(0.1)