cpuwidget: Fancier persistent graphs
This commit is contained in:
parent
90cef82161
commit
bd32606e5e
230
apps/cpuwidget.c
230
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();
|
||||
|
Loading…
Reference in New Issue
Block a user