From bd32606e5e6036c989c4b33cb7e8b2a2372384dd Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Sun, 24 Apr 2022 19:40:10 -0400 Subject: [PATCH] cpuwidget: Fancier persistent graphs --- apps/cpuwidget.c | 230 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 160 insertions(+), 70 deletions(-) diff --git a/apps/cpuwidget.c b/apps/cpuwidget.c index ee1bfe62..6dde120b 100644 --- a/apps/cpuwidget.c +++ b/apps/cpuwidget.c @@ -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();