|
/* retoor <retoor@molodetz.nl> */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#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);
|
|
}
|
|
}
|