/* retoor */ #include #include #include #include #include "display.h" static void format_bytes(unsigned long bytes, char *buf, size_t len) { const char *units[] = {"B", "K", "M", "G", "T"}; int unit = 0; double size = (double)bytes; while (size >= 1024.0 && unit < 4) { size /= 1024.0; unit++; } if (unit == 0) snprintf(buf, len, "%5luB", bytes); else snprintf(buf, len, "%5.1f%s", size, units[unit]); } static void format_rate(double rate, char *buf, size_t len) { const char *units[] = {"B/s", "K/s", "M/s", "G/s"}; int unit = 0; while (rate >= 1024.0 && unit < 3) { rate /= 1024.0; unit++; } snprintf(buf, len, "%7.1f%s", rate, units[unit]); } static void draw_bar(Terminal *term, double pct, int w) { if (pct < 0) pct = 0; if (pct > 100) pct = 100; int f = (int)(pct * w / 100); terminal_buffer_append(term, "["); for (int i = 0; i < w; i++) { if (i < f) { if (pct >= 90) terminal_buffer_append(term, COLOR_RED "|" COLOR_RESET); else if (pct >= 70) terminal_buffer_append(term, COLOR_YELLOW "|" COLOR_RESET); else terminal_buffer_append(term, COLOR_GREEN "|" COLOR_RESET); } else { terminal_buffer_append(term, " "); } } terminal_buffer_append(term, "]"); } void display_header(Terminal *term, SystemInfo *sys) { char buf[128]; time_t now = time(NULL); struct tm *t = localtime(&now); char ts[20]; strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S", t); terminal_move_cursor(term, 1, 1); terminal_buffer_append(term, COLOR_BOLD_WHITE); terminal_buffer_append(term, sys->hostname); terminal_buffer_append(term, COLOR_RESET " " COLOR_CYAN); terminal_buffer_append(term, sys->kernel); terminal_buffer_append(term, COLOR_RESET " " COLOR_YELLOW); terminal_buffer_append(term, sys->arch); terminal_buffer_append(term, COLOR_RESET " "); snprintf(buf, sizeof(buf), "up %s%dd %02d:%02d%s ", COLOR_GREEN, sys->uptime_days, sys->uptime_hours, sys->uptime_minutes, COLOR_RESET); terminal_buffer_append(term, buf); snprintf(buf, sizeof(buf), "load %s%.2f %.2f %.2f%s", COLOR_BOLD_WHITE, sys->load_1, sys->load_5, sys->load_15, COLOR_RESET); terminal_buffer_append(term, buf); int time_col = term->cols - 19; if (time_col > 0) { terminal_move_cursor(term, 1, time_col); terminal_buffer_append(term, COLOR_BOLD_CYAN); terminal_buffer_append(term, ts); terminal_buffer_append(term, COLOR_RESET); } } void display_tabs(Terminal *term, int current_tab) { const char *tabs[] = {"CPU", "Memory", "Network", "Disk", "Process"}; char buf[32]; terminal_move_cursor(term, 2, 1); for (int i = 0; i < 5; i++) { if (i == current_tab) { snprintf(buf, sizeof(buf), " %s[%d]%s%s ", COLOR_BOLD_WHITE, i + 1, tabs[i], COLOR_RESET); } else { snprintf(buf, sizeof(buf), " %s[%d]%s%s ", COLOR_CYAN, i + 1, tabs[i], COLOR_RESET); } terminal_buffer_append(term, buf); } terminal_move_cursor(term, 3, 1); for (int i = 0; i < term->cols - 1; i++) { terminal_buffer_append(term, "─"); } } void display_footer(Terminal *term, double refresh_rate) { char buf[128]; terminal_get_size(term); terminal_move_cursor(term, term->rows, 1); snprintf(buf, sizeof(buf), "%s◄ ► Navigate tabs | 1-5 Jump | Refresh: %.1fs | q quit%s", COLOR_CYAN, refresh_rate, COLOR_RESET); terminal_buffer_append(term, buf); } void display_cpu_detail(Terminal *term, CpuInfo *cpu) { char buf[128]; int row = 4; int bar_w = term->cols - 20; if (bar_w > 60) bar_w = 60; if (bar_w < 20) bar_w = 20; for (int i = 0; i < cpu->num_cores && row < term->rows - 2; i++) { terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sCPU%-3d%s ", COLOR_CYAN, i, COLOR_RESET); terminal_buffer_append(term, buf); draw_bar(term, cpu->cores[i].usage_percent, bar_w); snprintf(buf, sizeof(buf), " %6.1f%%", cpu->cores[i].usage_percent); terminal_buffer_append(term, buf); } terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sAVG %s ", COLOR_BOLD_CYAN, COLOR_RESET); terminal_buffer_append(term, buf); draw_bar(term, cpu->total.usage_percent, bar_w); snprintf(buf, sizeof(buf), " %6.1f%%", cpu->total.usage_percent); terminal_buffer_append(term, buf); row++; terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sCores:%s %d", COLOR_CYAN, COLOR_RESET, cpu->num_cores); terminal_buffer_append(term, buf); if (cpu->temperature > 0) { terminal_move_cursor(term, row++, 1); const char *tc = cpu->temperature >= 80 ? COLOR_RED : cpu->temperature >= 60 ? COLOR_YELLOW : COLOR_GREEN; snprintf(buf, sizeof(buf), "%sTemperature:%s %s%.1f°C%s", COLOR_CYAN, COLOR_RESET, tc, cpu->temperature, COLOR_RESET); terminal_buffer_append(term, buf); } if (cpu->frequency_mhz > 0) { terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sFrequency:%s %.0f MHz (%.0f - %.0f)", COLOR_CYAN, COLOR_RESET, cpu->frequency_mhz, cpu->min_freq_mhz, cpu->max_freq_mhz); terminal_buffer_append(term, buf); } terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sCtx Switch:%s %.0f/s", COLOR_CYAN, COLOR_RESET, cpu->context_switches_per_sec); terminal_buffer_append(term, buf); terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sInterrupts:%s %.0f/s", COLOR_CYAN, COLOR_RESET, cpu->interrupts_per_sec); terminal_buffer_append(term, buf); } void display_memory_detail(Terminal *term, MemoryInfo *mem, ProcessList *procs) { char buf[256], tmp[16], tmp2[16]; int row = 4; int bar_w = term->cols - 25; if (bar_w > 50) bar_w = 50; if (bar_w < 20) bar_w = 20; terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sTasks:%s %d total, %s%d running%s, %d sleeping, %s%d stopped%s, %s%d zombie%s", COLOR_CYAN, COLOR_RESET, procs->total, COLOR_GREEN, procs->running, COLOR_RESET, procs->sleeping, COLOR_YELLOW, procs->stopped, COLOR_RESET, COLOR_RED, procs->zombie, COLOR_RESET); terminal_buffer_append(term, buf); row++; format_bytes(mem->used * 1024, tmp, sizeof(tmp)); format_bytes(mem->total * 1024, tmp2, sizeof(tmp2)); terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sRAM %s ", COLOR_CYAN, COLOR_RESET); terminal_buffer_append(term, buf); draw_bar(term, mem->used_percent, bar_w); snprintf(buf, sizeof(buf), " %5.1f%% %s / %s", mem->used_percent, tmp, tmp2); terminal_buffer_append(term, buf); format_bytes(mem->swap_used * 1024, tmp, sizeof(tmp)); format_bytes(mem->swap_total * 1024, tmp2, sizeof(tmp2)); terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sSwap %s ", COLOR_CYAN, COLOR_RESET); terminal_buffer_append(term, buf); draw_bar(term, mem->swap_percent, bar_w); snprintf(buf, sizeof(buf), " %5.1f%% %s / %s", mem->swap_percent, tmp, tmp2); terminal_buffer_append(term, buf); row++; format_bytes(mem->total * 1024, tmp, sizeof(tmp)); terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sTotal: %s %s", COLOR_CYAN, COLOR_RESET, tmp); terminal_buffer_append(term, buf); format_bytes(mem->used * 1024, tmp, sizeof(tmp)); terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sUsed: %s %s (%.1f%%)", COLOR_CYAN, COLOR_RESET, tmp, mem->used_percent); terminal_buffer_append(term, buf); format_bytes(mem->available * 1024, tmp, sizeof(tmp)); terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sAvailable: %s %s", COLOR_CYAN, COLOR_RESET, tmp); terminal_buffer_append(term, buf); format_bytes(mem->buffers * 1024, tmp, sizeof(tmp)); terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sBuffers: %s %s", COLOR_CYAN, COLOR_RESET, tmp); terminal_buffer_append(term, buf); format_bytes(mem->cached * 1024, tmp, sizeof(tmp)); terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sCached: %s %s", COLOR_CYAN, COLOR_RESET, tmp); terminal_buffer_append(term, buf); row++; format_bytes(mem->swap_total * 1024, tmp, sizeof(tmp)); terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sSwap Total:%s %s", COLOR_CYAN, COLOR_RESET, tmp); terminal_buffer_append(term, buf); format_bytes(mem->swap_used * 1024, tmp, sizeof(tmp)); terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sSwap Used: %s %s (%.1f%%)", COLOR_CYAN, COLOR_RESET, tmp, mem->swap_percent); terminal_buffer_append(term, buf); row++; terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sPage In: %s %.0f/s", COLOR_CYAN, COLOR_RESET, mem->page_in_per_sec); terminal_buffer_append(term, buf); terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sPage Out: %s %.0f/s", COLOR_CYAN, COLOR_RESET, mem->page_out_per_sec); terminal_buffer_append(term, buf); } void display_network_detail(Terminal *term, NetworkInfo *net) { char buf[256], rx[16], tx[16], rxb[16], txb[16]; int row = 4; terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%s%-15s %-16s %-3s %-12s %-12s %-10s %-10s%s", COLOR_BOLD_WHITE, "INTERFACE", "IP ADDRESS", "UP", "RX RATE", "TX RATE", "RX TOTAL", "TX TOTAL", COLOR_RESET); terminal_buffer_append(term, buf); for (int i = 0; i < net->count && row < term->rows - 2; i++) { NetworkInterface *n = &net->interfaces[i]; format_rate(n->rx_rate, rx, sizeof(rx)); format_rate(n->tx_rate, tx, sizeof(tx)); format_bytes(n->rx_bytes, rxb, sizeof(rxb)); format_bytes(n->tx_bytes, txb, sizeof(txb)); terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%s%-15s%s %-16s %s%-3s%s %s▼%s%-11s %s▲%s%-11s %-10s %-10s", COLOR_CYAN, n->name, COLOR_RESET, n->ip[0] ? n->ip : "-", n->up ? COLOR_GREEN : COLOR_RED, n->up ? "Yes" : "No", COLOR_RESET, COLOR_GREEN, COLOR_RESET, rx, COLOR_RED, COLOR_RESET, tx, rxb, txb); terminal_buffer_append(term, buf); } } void display_disk_detail(Terminal *term, DiskInfo *disk, FilesystemInfo *fs) { char buf[256], rd[16], wr[16]; int row = 4; int bar_w = 25; terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%s%-12s %-14s %-14s %-12s%s", COLOR_BOLD_WHITE, "DEVICE", "READ", "WRITE", "IOPS (R/W)", COLOR_RESET); terminal_buffer_append(term, buf); for (int i = 0; i < disk->count && row < term->rows / 2; i++) { DiskStats *d = &disk->disks[i]; format_rate(d->read_bytes_per_sec, rd, sizeof(rd)); format_rate(d->write_bytes_per_sec, wr, sizeof(wr)); terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%s%-12s%s %s▼%s %-12s %s▲%s %-12s %5.0f / %5.0f", COLOR_CYAN, d->name, COLOR_RESET, COLOR_GREEN, COLOR_RESET, rd, COLOR_RED, COLOR_RESET, wr, d->reads_per_sec, d->writes_per_sec); terminal_buffer_append(term, buf); } row++; terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%s%-20s %-22s %7s %9s %9s %-8s%s", COLOR_BOLD_WHITE, "MOUNT", "USAGE", "PERCENT", "USED", "TOTAL", "TYPE", COLOR_RESET); terminal_buffer_append(term, buf); for (int i = 0; i < fs->count && row < term->rows - 2; i++) { FilesystemStats *f = &fs->filesystems[i]; char used[16], total[16]; format_bytes(f->used * 1024, used, sizeof(used)); format_bytes(f->total * 1024, total, sizeof(total)); const char *c = f->used_percent >= 90 ? COLOR_RED : f->used_percent >= 70 ? COLOR_YELLOW : COLOR_GREEN; terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%s%-20s%s ", COLOR_CYAN, f->mount_point, COLOR_RESET); terminal_buffer_append(term, buf); draw_bar(term, f->used_percent, bar_w); snprintf(buf, sizeof(buf), " %s%5.1f%%%s %9s %9s %-8s", c, f->used_percent, COLOR_RESET, used, total, f->fs_type); terminal_buffer_append(term, buf); } } void display_process_detail(Terminal *term, ProcessList *procs) { char buf[256]; int row = 4; terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%sTasks:%s %d total, %s%d running%s, %d sleeping, %s%d stopped%s, %s%d zombie%s", COLOR_CYAN, COLOR_RESET, procs->total, COLOR_GREEN, procs->running, COLOR_RESET, procs->sleeping, COLOR_YELLOW, procs->stopped, COLOR_RESET, COLOR_RED, procs->zombie, COLOR_RESET); terminal_buffer_append(term, buf); row++; int cmd_w = term->cols - 68; if (cmd_w < 10) cmd_w = 10; if (cmd_w > 40) cmd_w = 40; terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%s%6s %6s %-8s S %5s %5s %7s %7s %3s %3s %-*s%s", COLOR_BOLD_WHITE, "PID", "PPID", "USER", "CPU%", "MEM%", "RSS", "VIRT", "THR", "NI", cmd_w, "COMMAND", COLOR_RESET); terminal_buffer_append(term, buf); int max_procs = term->rows - row - 2; for (int i = 0; i < procs->count && i < max_procs; i++) { ProcessInfo *p = &procs->processes[i]; char rss[16], virt[16]; format_bytes(p->rss, rss, sizeof(rss)); format_bytes(p->vsize, virt, sizeof(virt)); const char *sc = p->state == 'R' ? COLOR_GREEN : p->state == 'D' || p->state == 'Z' ? COLOR_RED : p->state == 'T' ? COLOR_YELLOW : COLOR_RESET; const char *cpuc = p->cpu_percent >= 50 ? COLOR_RED : p->cpu_percent >= 20 ? COLOR_YELLOW : COLOR_RESET; const char *memc = p->mem_percent >= 10 ? COLOR_RED : p->mem_percent >= 5 ? COLOR_YELLOW : COLOR_RESET; terminal_move_cursor(term, row++, 1); snprintf(buf, sizeof(buf), "%6d %6d %-8.8s %s%c%s %s%5.1f%s %s%5.1f%s %7s %7s %3d %3d %-*.*s", p->pid, p->ppid, p->user, sc, p->state, COLOR_RESET, cpuc, p->cpu_percent, COLOR_RESET, memc, p->mem_percent, COLOR_RESET, rss, virt, p->threads, p->nice, cmd_w, cmd_w, p->cmd); terminal_buffer_append(term, buf); } }