cpuwidget: Fancier persistent graphs

This commit is contained in:
K. Lange 2022-04-24 19:40:10 -04:00
parent 90cef82161
commit bd32606e5e

View File

@ -69,7 +69,6 @@ uint32_t hsv_to_rgb(float h, float s, float v) {
static int should_exit = 0;
static clock_t last_redraw = 0;
static int cpu_count = 1;
int cpus[32] = {0};
static void get_cpu_info(int cpus[]) {
FILE * f = fopen("/proc/idle","r");
@ -100,52 +99,57 @@ static uint32_t if_colors[32];
#define EASE_WIDTH 8
static void shift_graph(gfx_context_t * ctx) {
for (int y = 0; y < ctx->height; ++y) {
for (int x = 0; x < ctx->width - 1; ++x) {
GFX(ctx,x,y) = GFX(ctx,x+1,y);
}
for (int w = 1; w < 2; ++w) {
GFX(ctx,ctx->width-w,y) = rgb(0xF8,0xF8,0xF8);
}
}
}
extern struct TT_Contour * tt_contour_start(float x, float y);
extern struct TT_Shape * tt_contour_finish(struct TT_Contour * in);
extern struct TT_Contour * tt_contour_line_to(struct TT_Contour * shape, float x, float y);
extern void tt_path_paint(gfx_context_t * ctx, const struct TT_Shape * shape, uint32_t color);
extern void tt_contour_stroke_bounded(gfx_context_t * ctx, struct TT_Contour * in, uint32_t color, float width,
int x_0, int y_0, int w, int h);
extern struct TT_Shape * tt_contour_stroke_shape(struct TT_Contour * in, float width);
static void graph_between(gfx_context_t * ctx, size_t old, size_t new, size_t scale, uint32_t color, int direction) {
static float factor[EASE_WIDTH] = {0.0};
if (factor[0] == 0.0) {
for (int i = 0; i < EASE_WIDTH; ++i) {
factor[i] = (cos(M_PI * ((float)i / (float)(EASE_WIDTH-1))) + 1.0) / 2.0;
static void plot_graph(gfx_context_t * ctx, size_t scale, long samples[100], uint32_t color, float shift) {
float unit_width = (float)ctx->width / 99.0;
float factor[EASE_WIDTH];
for (int k = 0; k < EASE_WIDTH; ++k) {
factor[k] = (cos(M_PI * ((float)k / (float)(EASE_WIDTH-1))) + 1.0) / 2.0;
}
struct TT_Contour * contour = NULL;
size_t first = 1;
for (int j = 1; j < 100; ++j) {
if (samples[j-1] == -1) {
first++;
continue;
}
float start = (float)ctx->width * (float)(j - 1) / 99.0 + shift;
size_t old = samples[j-1];
size_t new = samples[j];
if (old > scale) old = scale;
if (new > scale) new = scale;
float nsamples[EASE_WIDTH];
for (int k = 0; k < EASE_WIDTH; ++k) {
float value = old * factor[k] + new * (1.0 - factor[k]);
nsamples[k] = ((scale - value) * ((float)ctx->height - 1) / (float)scale);
}
if (!contour) {
contour = tt_contour_start(start, nsamples[0]);
}
for (int k = 1; k < EASE_WIDTH; ++k) {
contour = tt_contour_line_to(contour, start + unit_width * ((float)k / (float)(EASE_WIDTH-1)), nsamples[k]);
}
}
if (old > scale) old = scale;
if (new > scale) new = scale;
if (!contour) return;
static float samples[EASE_WIDTH];
for (int i = 0; i < EASE_WIDTH; ++i) {
size_t value = old * factor[i] + new * (1.0 - factor[i]);
samples[i] = (direction == 0) ? (value * (ctx->height - 1) / (float)scale) : ((scale - value) * (ctx->height - 1) / (float)scale);
}
struct TT_Shape * stroke = tt_contour_stroke_shape(contour, 0.5);
tt_path_paint(ctx, stroke, color);
free(stroke);
/* Main line */
struct TT_Contour * contour = tt_contour_start(ctx->width - EASE_WIDTH, samples[0]);
for (int i = 1; i < EASE_WIDTH; ++i) {
contour = tt_contour_line_to(contour, ctx->width - EASE_WIDTH + i, samples[i]);
}
/* Now slow stroke, but only within these bounds */
tt_contour_stroke_bounded(ctx, contour, color, 0.5, ctx->width - EASE_WIDTH, 0, EASE_WIDTH - 1, ctx->height);
/* Now finish the lower part of the graph */
contour = tt_contour_line_to(contour, ctx->width - 1, ctx->height);
contour = tt_contour_line_to(contour, ctx->width - EASE_WIDTH, ctx->height);
contour = tt_contour_line_to(contour, ctx->width + shift, ctx->height);
contour = tt_contour_line_to(contour, (float)ctx->width * (float)(first - 1) / 99.0 + shift, ctx->height);
struct TT_Shape * shape = tt_contour_finish(contour);
@ -155,13 +159,38 @@ static void graph_between(gfx_context_t * ctx, size_t old, size_t new, size_t sc
free(contour);
}
static long cpu_samples[32][100];
static void draw_lines(gfx_context_t * ctx) {
float unit_width = (float)ctx->width / 99.0;
for (int i = 1; i < 10; i++) {
struct TT_Contour * line = tt_contour_start((int)(unit_width * 10.0 * i) + 0.5, 0);
line = tt_contour_line_to(line, (int)(unit_width * 10.0 * i) + 0.5, ctx->height);
struct TT_Shape * shape = tt_contour_stroke_shape(line, 0.5);
free(line);
tt_path_paint(ctx, shape, rgb(150,150,150));
free(shape);
}
}
static void draw_cpu_graphs(gfx_context_t * ctx, float shift) {
draw_fill(ctx, rgb(0xF8,0xF8,0xF8));
draw_lines(ctx);
for (int i = 0; i < cpu_count; ++i) {
plot_graph(ctx, 1000, cpu_samples[i], colors[i], shift);
}
}
static void next_cpu(gfx_context_t * ctx) {
int cpus_new[32];
get_cpu_info(cpus_new);
for (int i = 0; i < cpu_count; ++i) {
graph_between(ctx, cpus[i], cpus_new[i], 1000, colors[i], 0);
memmove(&cpu_samples[i][0], &cpu_samples[i][1], 99 * sizeof(long));
cpu_samples[i][99] = 1000-cpus_new[i];
}
memcpy(cpus, cpus_new, sizeof(cpus_new));
draw_cpu_graphs(ctx, 0.0);
}
static void get_mem_info(int * total, int * used) {
@ -188,6 +217,14 @@ static void get_mem_info(int * total, int * used) {
fclose(f);
}
static long mem_samples[100];
static long mem_total;
static void draw_mem_graphs(gfx_context_t * ctx, float shift) {
draw_fill(ctx, rgb(0xF8,0xF8,0xF8));
draw_lines(ctx);
plot_graph(ctx, mem_total, mem_samples, rgb(250,110,240), shift);
}
static void next_mem(gfx_context_t * ctx) {
static int total = 0;
static int old_use = 0;
@ -199,7 +236,10 @@ static void next_mem(gfx_context_t * ctx) {
return;
}
graph_between(ctx, old_use, mem_use, total, rgb(250,110,240), 1);
memmove(&mem_samples[0], &mem_samples[1], 99 * sizeof(long));
mem_total = total;
mem_samples[99] = mem_use;
draw_mem_graphs(ctx, 0.0);
old_use = mem_use;
}
@ -253,13 +293,25 @@ static void refresh_interfaces(size_t ifs[32]) {
closedir(d);
}
static long net_samples[32][100];
static size_t net_scale = 300 * 1024;
static void redraw_net_scale(void);
static int if_count = -1;
static void draw_net_graphs(gfx_context_t * ctx, float shift) {
draw_fill(ctx, rgb(0xF8,0xF8,0xF8));
draw_lines(ctx);
for (int i = 0; i < if_count; ++i) {
plot_graph(ctx, net_scale, net_samples[i], if_colors[i], shift);
}
}
static void next_net(gfx_context_t * ctx) {
static size_t old_ifs[32];
static size_t old_use[32];
static clock_t ticks_last = 0;
size_t new_ifs[32];
size_t new_use[32];
if (!ticks_last) {
ticks_last = times(NULL);
@ -270,26 +322,41 @@ static void next_net(gfx_context_t * ctx) {
clock_t ticks_now = times(NULL);
refresh_interfaces(new_ifs);
long max = 0;
for (int i = 0; i < if_count; ++i) {
for (int j = 0; j < 99; ++j) {
net_samples[i][j] = net_samples[i][j+1];
if (net_samples[i][j] != -1) {
if (net_samples[i][j] > max) {
max = net_samples[i][j];
}
}
}
/* Kilobits... */
new_use[i] = (new_ifs[i] - old_ifs[i]) * 8 / 1024;
new_use[i] *= CLOCKS_PER_SEC;
new_use[i] /= (ticks_now - ticks_last);
/* Relative to 300mbps... */
graph_between(ctx, old_use[i], new_use[i], 300 * 1024, if_colors[i], 1);
size_t use = (new_ifs[i] - old_ifs[i]) * 8 / 1024;
use *= CLOCKS_PER_SEC;
use /= (ticks_now - ticks_last);
net_samples[i][99] = use;
if ((long)use > max) {
max = use;
}
}
size_t scale = max ? max : (300 * 1024);
if (scale != net_scale) {
net_scale = scale;
redraw_net_scale();
}
draw_net_graphs(ctx, 0.0);
memcpy(old_ifs, new_ifs, sizeof(new_ifs));
memcpy(old_use, new_use, sizeof(new_ifs));
ticks_last = ticks_now;
}
static void demarcate(gfx_context_t * ctx) {
for (int y = 0; y < ctx->height; ++y) {
GFX(ctx,ctx->width - 1,y) = rgb(127,127,127);
}
}
static char * ellipsify(char * input, int font_size, struct TT_Font * font, int max_width, int * out_width) {
int len = strlen(input);
char * out = malloc(len + 4);
@ -357,21 +424,15 @@ static void draw_legend_net(void) {
}
}
static int poll_tick = 0;
static void redraw_graphs(void) {
float shift = -(float)(poll_tick + 1) / (float)(EASE_WIDTH-1) * ctx_cpu->width / 100.0;
draw_cpu_graphs(ctx_cpu, shift);
draw_mem_graphs(ctx_mem, shift);
draw_net_graphs(ctx_net, shift);
}
static void refresh(clock_t ticks) {
static int poll_tick = 0;
static clock_t last_line = 0;
/* Shift graphs */
shift_graph(ctx_cpu);
shift_graph(ctx_mem);
shift_graph(ctx_net);
if (ticks > last_line + CLOCKS_PER_SEC * 10) {
demarcate(ctx_cpu);
demarcate(ctx_mem);
demarcate(ctx_net);
last_line = ticks;
}
if (poll_tick == (EASE_WIDTH-2)) {
next_cpu(ctx_cpu);
@ -379,6 +440,7 @@ static void refresh(clock_t ticks) {
next_net(ctx_net);
poll_tick = 0;
} else {
redraw_graphs();
poll_tick++;
}
@ -388,6 +450,18 @@ static void refresh(clock_t ticks) {
last_redraw = ticks;
}
static void redraw_net_scale(void) {
struct decor_bounds bounds;
decor_get_bounds(wina, &bounds);
tt_set_size(tt_thin, 10);
char net_max[100];
snprintf(net_max, 100, "%0.2fmbps", (double)net_scale / 1024.0);
int swidth = tt_string_width(tt_thin, net_max) + 2;
draw_rectangle(ctx_base, bounds.left_width + width - swidth, bounds.top_height + 2 * (top_pad + bottom_pad + graph_height), swidth, 20, rgb(204,204,204));
tt_draw_string(ctx_base, tt_thin, bounds.left_width + width - swidth, bounds.top_height + 2 * (top_pad + bottom_pad + graph_height) + 17, net_max, rgb(0,0,0));
}
static void initial_stuff(void) {
struct decor_bounds bounds;
decor_get_bounds(wina, &bounds);
@ -411,7 +485,12 @@ static void initial_stuff(void) {
tt_set_size(tt_thin, 10);
tt_draw_string(ctx_base, tt_thin, bounds.left_width + width - 30, bounds.top_height + 17, "100%", rgb(0,0,0));
tt_draw_string(ctx_base, tt_thin, bounds.left_width + width - 30, bounds.top_height + (top_pad + bottom_pad + graph_height) + 17, "100%", rgb(0,0,0));
tt_draw_string(ctx_base, tt_thin, bounds.left_width + width - 50, bounds.top_height + 2 * (top_pad + bottom_pad + graph_height) + 17, "300mbps", rgb(0,0,0));
char net_max[100];
snprintf(net_max, 100, "%0.2fmbps", (double)net_scale / 1024.0);
int swidth = tt_string_width(tt_thin, net_max) + 2;
tt_draw_string(ctx_base, tt_thin, bounds.left_width + width - swidth, bounds.top_height + 2 * (top_pad + bottom_pad + graph_height) + 17, net_max, rgb(0,0,0));
tt_draw_string(ctx_base, tt_thin, bounds.left_width + width - 25, bounds.top_height + top_pad + graph_height + 13, "0%", rgb(0,0,0));
tt_draw_string(ctx_base, tt_thin, bounds.left_width + width - 25, bounds.top_height + 2 * (top_pad + graph_height) + bottom_pad + 13, "0%", rgb(0,0,0));
tt_draw_string(ctx_base, tt_thin, bounds.left_width + width - 40, bounds.top_height + 3 * (top_pad + graph_height) + 2 * bottom_pad + 13, "0mbps", rgb(0,0,0));
@ -442,6 +521,8 @@ void resize_finish(int w, int h) {
height = h - bounds.top_height - bounds.bottom_height;
initial_stuff();
redraw_graphs();
flip(ctx_base);
yutani_window_resize_done(yctx, wina);
@ -463,6 +544,9 @@ int main (int argc, char ** argv) {
srand(time(NULL));
for (int i = 0; i < cpu_count; ++i) {
colors[i] = hsv_to_rgb((float)i / (float)cpu_count * 6.24, 0.9, 0.9);
for (int j = 0; j < 100; ++j) {
cpu_samples[i][j] = -1;
}
}
init_decorations();
@ -478,10 +562,16 @@ int main (int argc, char ** argv) {
tt_thin = tt_font_from_shm("sans-serif");
tt_bold = tt_font_from_shm("sans-serif.bold");
get_cpu_info(cpus);
if_count = count_interfaces();
for (int i = 0; i < if_count; ++i) {
if_colors[i] = hsv_to_rgb((float)i / (float)(if_count)* 6.24 + 0.2, 0.9, 0.9);
for (int j = 0; j < 100; ++j) {
net_samples[i][j] = -1;
}
}
for (int i = 0; i < 100; ++i) {
mem_samples[i] = -1;
}
initial_stuff();