Additional graph widget options

There are the new styles:

bottom (fill the graph to the bottom of widget-square)
top (fill the graph to the top of the widget-square
line (just print a line representing the values)

E.g when there are multiple 'bottom'-style graphs, it will print the larger
part on top of the smaller.  When two values are the same, it will (actually)
just print it with one color (something to improve maybe).

bottom-style overdraws top-style, and line-style overdraws top and bottom style
(= gets drawn at the end)

An example configuration:

    graph gr_cpu
    {
      data { scale = false max = 100 fg = "#669966" style = bottom} # total
      data { scale = false max = 100 fg = "#cc9966" style = bottom} # user
      data { scale = false max = 100 fg = "#ffffff" style = bottom} # nice-processes
      width = 50
      height = "0.80"
      bg = "#000000"
      bordercolor = "#669966"
    }

With the 'line' style, there is a bug (draws sometimes over the rectangle).
I checked the values and didn't find any value what actually should do that.
So I have no idea why that is... needs a recheck, because it's not really nice..
Happens especially when scale=true and after a rescaling takes place.

Signed-off-by: Julien Danjou <julien@danjou.info>
This commit is contained in:
marco candrian 2008-01-25 21:39:08 +01:00 committed by Julien Danjou
parent 03743e1f0b
commit c73e0bd72e
4 changed files with 289 additions and 95 deletions

View File

@ -160,39 +160,79 @@ draw_rectangle(DrawCtx *ctx, Area geometry, Bool filled, XColor color)
cairo_surface_destroy(surface);
}
/* draws a graph; it takes the line-lengths from 'h' (w = size of h)
* It cycles backwards through it, beginning at position h_index, until
* h_index is reached again (wrapped around). */
/* draw_graph functions */
void
draw_graph_init(DrawCtx *ctx, cairo_surface_t **pp_surface, cairo_t **pp_cr)
{
*pp_surface = cairo_xlib_surface_create(ctx->display, ctx->drawable, ctx->visual, ctx->width, ctx->height);
*pp_cr = cairo_create (*pp_surface);
cairo_set_antialias(*pp_cr, CAIRO_ANTIALIAS_NONE);
cairo_set_line_width(*pp_cr, 1.0);
}
void
draw_graph(DrawCtx *ctx, int x, int y, int w, int *h, int h_index, XColor color)
draw_graph_end(cairo_surface_t *surface, cairo_t *cr)
{
cairo_destroy(cr);
cairo_surface_destroy(surface);
}
/* draws a graph; it takes the line-lengths from *from and *to. It cycles
* backwards through those arrays (what have the lenght of w), beginning at
* position cur_index, until h_index is reached again (wrapped around). */
void
draw_graph(cairo_t *cr, int x, int y, int w, int *from, int *to, int cur_index, XColor color)
{
cairo_surface_t *surface;
cairo_t *cr;
int i;
surface = cairo_xlib_surface_create(ctx->display, ctx->drawable, ctx->visual, ctx->width, ctx->height);
cr = cairo_create (surface);
cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
cairo_set_line_width(cr, 1.0);
cairo_set_source_rgb(cr, color.red / 65535.0, color.green / 65535.0, color.blue / 65535.0);
i = -1;
while(++i < w)
{
cairo_move_to(cr, x, y);
cairo_line_to(cr, x, y - h[h_index]);
cairo_move_to(cr, x, y - from[cur_index]);
cairo_line_to(cr, x, y - to[cur_index]);
x++;
if (--h_index < 0)
h_index = w - 1;
if (--cur_index < 0)
cur_index = w - 1;
}
cairo_stroke(cr);
}
void
draw_graph_line(cairo_t *cr, int x, int y, int w, int *to, int cur_index, XColor color)
{
int i;
int flag = 0; /* used to prevent drawing a line from 0 to 0 values */
cairo_set_source_rgb(cr, color.red / 65535.0, color.green / 65535.0, color.blue / 65535.0);
cairo_destroy(cr);
cairo_surface_destroy(surface);
/* XXX x - 1 (on the border actually), for whatever reason... */
cairo_move_to(cr, x - 1, y - to[cur_index]);
for (i = 0; i < w; i++)
{
if (to[cur_index] >= 1)
{
cairo_line_to(cr, x, y - to[cur_index]);
flag = 1;
}
else
{
if(flag) /* only draw from values > 0 to 0-values */
{
cairo_line_to(cr, x, y);
flag = 0;
}
else
cairo_move_to(cr, x, y);
}
if (--cur_index < 0) /* cycles around the index */
cur_index = w - 1;
x++;
}
cairo_stroke(cr);
}
void

View File

@ -22,6 +22,7 @@
#ifndef AWESOME_DRAW_H
#define AWESOME_DRAW_H
#include <cairo.h>
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>
@ -56,7 +57,12 @@ typedef struct
DrawCtx *draw_get_context(Display *, int, int, int, Drawable);
void draw_text(DrawCtx *, Area, Alignment, int, XftFont *, const char *, XColor fg, XColor bg);
void draw_rectangle(DrawCtx *, Area, Bool, XColor);
void draw_graph(DrawCtx *, int, int, int, int *, int, XColor);
void draw_graph_init(DrawCtx *, cairo_surface_t **, cairo_t **);
void draw_graph(cairo_t *, int, int, int, int *, int *, int, XColor);
void draw_graph_end(cairo_surface_t *, cairo_t *);
void draw_graph_line(cairo_t *, int, int, int, int *, int, XColor);
void draw_circle(DrawCtx *, int, int, int, Bool, XColor);
void draw_image(DrawCtx *, int, int, int, const char *);
void draw_image_from_argb_data(DrawCtx *, int, int, int, int, int, unsigned char *);

View File

@ -539,19 +539,25 @@ config_parse(const char *confpatharg)
CFG_BOOL((char *) "show_all", cfg_false, CFGF_NONE),
CFG_END()
};
static cfg_opt_t widget_graph_data_opts[] =
{
CFG_FLOAT((char *) "max", 100.0f, CFGF_NONE),
CFG_BOOL((char *) "scale", cfg_false, CFGF_NONE),
CFG_STR((char *) "fg", (char *) NULL, CFGF_NONE),
CFG_STR((char *) "style", (char *) "bottom", CFGF_NONE),
CFG_END()
};
static cfg_opt_t widget_graph_opts[] =
{
CFG_INT((char *) "x", 0xffffffff, CFGF_NONE),
CFG_INT((char *) "y", 0xffffffff, CFGF_NONE),
CFG_SEC((char *) "mouse", mouse_generic_opts, CFGF_MULTI),
CFG_SEC((char *) "data", widget_graph_data_opts, CFGF_MULTI),
CFG_INT((char *) "width", 100, CFGF_NONE),
CFG_INT((char *) "padding_left", 0, CFGF_NONE),
CFG_FLOAT((char *) "height", 0.67, CFGF_NONE),
CFG_STR((char *) "fg", (char *) NULL, CFGF_NONE),
CFG_STR((char *) "bg", (char *) NULL, CFGF_NONE),
CFG_STR((char *) "bordercolor", (char *) NULL, CFGF_NONE),
CFG_BOOL((char *) "scale", cfg_false, CFGF_NONE),
CFG_FLOAT((char *) "max", 100.0f, CFGF_NONE),
CFG_END()
};
static cfg_opt_t widget_progressbar_bar_opts[] =

View File

@ -20,6 +20,8 @@
*
*/
#include <cairo.h>
#include "common/draw.h"
#include "widget.h"
#include "xutil.h"
#include "screen.h"
@ -29,20 +31,40 @@ extern AwesomeConf globalconf;
typedef struct
{
float max; /* Represents a full graph */
int width; /* Width of the widget */
int padding_left; /* Left padding */
float height; /* Height 0-1, where 1 is height of statusbar */
XColor fg; /* Foreground color */
XColor bg; /* Background color */
XColor bordercolor; /* Border color */
int *lines; /* Keeps the calculated values (line-length); */
int lines_index; /* Pointer to current value */
int lines_size; /* Size of lines-array (also innerbox-lenght) */
int box_height; /* Height of the innerbox */
float *line_values; /* Actual values */
float current_max; /* Curent maximum value */
int line_max_index; /* Index of the current maximum value */
/* general layout */
float *max; /* Represents a full graph */
int width; /* Width of the widget */
float height; /* Height of graph (0-1, where 1 is height of statusbar) */
int box_height; /* Height of the innerbox in pixels */
int padding_left; /* Left padding */
int size; /* Size of lines-array (also innerbox-lenght) */
XColor bg; /* Background color */
XColor bordercolor; /* Border color */
/* markers... */
int index; /* Index of current (new) value */
int *max_index; /* Index of the actual maximum value */
float *current_max; /* Pointer to current maximum value itself */
/* all data is stored here */
int data_items; /* Number of data-input items */
int **lines; /* Keeps the calculated values (line-length); */
float **values; /* Actual values */
/* additional data + a pointer to **lines accordingly */
int **fillbottom; /* datatypes holder (same as some **lines pointer) */
int fillbottom_total; /* total of them */
XColor *fillbottom_color; /* color of them */
int **filltop; /* datatypes holder */
int filltop_total; /* total of them */
XColor *filltop_color; /* color of them */
int **drawline; /* datatypes holder */
int drawline_total; /* total of them */
XColor *drawline_color; /* color of them */
int *draw_from; /* Preparation/tmp array for draw_graph(); */
int *draw_to; /* Preparation/tmp array for draw_graph(); */
} Data;
static int
@ -50,10 +72,13 @@ graph_draw(Widget *widget, DrawCtx *ctx, int offset,
int used __attribute__ ((unused)))
{
int margin_top, left_offset;
int z, y, x, tmp;
Data *d = widget->data;
Area rectangle;
cairo_surface_t *surface;
cairo_t *cr;
if(d->width < 1 || !(d->max > 0))
if(d->width < 1 || !d->data_items)
return 0;
if(!widget->user_supplied_x)
@ -72,7 +97,7 @@ graph_draw(Widget *widget, DrawCtx *ctx, int offset,
rectangle.x = left_offset;
rectangle.y = margin_top;
rectangle.width = d->lines_size + 2;
rectangle.width = d->size + 2;
rectangle.height = d->box_height + 2;
draw_rectangle(ctx, rectangle, False, d->bordercolor);
@ -82,13 +107,62 @@ graph_draw(Widget *widget, DrawCtx *ctx, int offset,
rectangle.height -= 2;
draw_rectangle(ctx, rectangle, True, d->bg);
if(d->lines[d->lines_index] < 0)
d->lines[d->lines_index] = 0;
draw_graph_init(ctx, &surface, &cr); /* setup drawing surface etc */
draw_graph(ctx,
left_offset + 2, margin_top + d->box_height + 1,
d->lines_size, d->lines, d->lines_index,
d->fg);
/* draw style = top */
for(z = 0; z < d->filltop_total; z++)
{
for(y = 0; y < d->size; y++)
{
for(tmp = 0, x = 0; x < d->filltop_total; x++) /* find largest smaller value */
{
if (x == z)
continue;
if(d->filltop[x][y] > tmp && d->filltop[x][y] < d->filltop[z][y])
tmp = d->filltop[x][y];
}
d->draw_from[y] = d->box_height - tmp;
d->draw_to[y] = d->box_height - d->filltop[z][y];
}
draw_graph(cr,
left_offset + 2, margin_top + d->box_height + 1,
d->size, d->draw_from, d->draw_to, d->index,
d->filltop_color[z]);
}
/* draw style = bottom */
for(z = 0; z < d->fillbottom_total; z++)
{
for(y = 0; y < d->size; y++)
{
for(tmp = 0, x = 0; x < d->fillbottom_total; x++) /* find largest smaller value */
{
if (x == z)
continue;
if(d->fillbottom[x][y] > tmp && d->fillbottom[x][y] < d->fillbottom[z][y])
tmp = d->fillbottom[x][y];
}
d->draw_from[y] = tmp;
}
draw_graph(cr,
left_offset + 2, margin_top + d->box_height + 1,
d->size, d->draw_from, d->fillbottom[z], d->index,
d->fillbottom_color[z]);
}
/* draw style = line */
for(z = 0; z < d->drawline_total; z++)
{
draw_graph_line(cr,
left_offset + 2, margin_top + d->box_height + 1,
d->size, d->drawline[z], d->index,
d->drawline_color[z]);
}
draw_graph_end(surface, cr);
widget->area.width = d->width;
widget->area.height = widget->statusbar->height;
@ -99,53 +173,61 @@ static void
graph_tell(Widget *widget, char *command)
{
Data *d = widget->data;
int i;
float value;
int i, z;
float *value;
char *tok;
if(!command || d->width < 1 || !(d->max > 0))
if(!command || d->width < 1 || !(d->data_items > 0))
return;
if(++d->lines_index >= d->lines_size) /* cycle inside the array */
d->lines_index = 0;
value = p_new(float, d->data_items);
value = MAX(atof(command), 0); /* TODO: may allow min-option and values */
for (i = 0, tok = strtok(command, ","); tok && i < d->data_items; tok = strtok(NULL, ","), i++)
value[i] = MAX(atof(tok), 0);
if(d->line_values) /* scale option is true */
if(++d->index >= d->size) /* cycle inside the arrays (all-in-one) */
d->index = 0;
/* add according values and to-draw-line-lenghts to the according data_items */
for(z = 0; z < d->data_items; z++)
{
d->line_values[d->lines_index] = value;
if(value > d->current_max) /* a new maximum value found */
if(d->values[z]) /* scale option is true */
{
d->line_max_index = d->lines_index;
d->current_max = value;
d->values[z][d->index] = value[z];
if(value[z] > d->current_max[z]) /* a new maximum value found */
{
d->max_index[z] = d->index;
d->current_max[z] = value[z];
/* recalculate */
for (i = 0; i < d->size; i++)
d->lines[z][i] = (int) (d->values[z][i] * (d->box_height) / d->current_max[z] + 0.5);
}
else if(d->max_index[z] == d->index) /* old max_index reached, re-check/generate */
{
/* find the new max */
for (i = 0; i < d->size; i++)
if (d->values[z][i] > d->values[z][d->max_index[z]])
d->max_index[z] = i;
d->current_max[z] = MAX(d->values[z][d->max_index[z]], d->max[z]);
/* recalculate */
for (i = 0; i < d->size; i++)
d->lines[z][i] = (int) (d->values[z][i] * d->box_height / d->current_max[z] + 0.5);
}
else
d->lines[z][d->index] = (int) (value[z] * d->box_height / d->current_max[z] + 0.5);
/* recalculate */
for (i = 0; i < d->lines_size; i++)
d->lines[i] = (int) (d->line_values[i] * (d->box_height) / d->current_max + 0.5);
}
else if(d->line_max_index == d->lines_index) /* old max_index reached, re-check/generate */
else /* scale option is false - limit to d->box_height */
{
/* find the new max */
for (i = 0; i < d->lines_size; i++)
if (d->line_values[i] > d->line_values[d->line_max_index])
d->line_max_index = i;
d->current_max = MAX(d->line_values[d->line_max_index], d->max);
/* recalculate */
for (i = 0; i < d->lines_size; i++)
d->lines[i] = (int) (d->line_values[i] * d->box_height / d->current_max + 0.5);
if (value[z] < d->current_max[z])
d->lines[z][d->index] = (int) (value[z] * d->box_height / d->current_max[z] + 0.5);
else
d->lines[z][d->index] = d->box_height;
}
else
d->lines[d->lines_index] = (int) (value * d->box_height / d->current_max + 0.5);
}
else /* scale option is false */
{
if (value < d->current_max)
d->lines[d->lines_index] = (int) (value * d->box_height / d->current_max + 0.5);
else
d->lines[d->lines_index] = d->box_height;
}
}
@ -154,8 +236,12 @@ graph_new(Statusbar *statusbar, cfg_t *config)
{
Widget *w;
Data *d;
cfg_t *cfg;
char *color;
int phys_screen = get_phys_screen(statusbar->screen);
int i;
char *type;
XColor tmp_color;
w = p_new(Widget, 1);
widget_common_new(w, statusbar, config);
@ -167,30 +253,86 @@ graph_new(Statusbar *statusbar, cfg_t *config)
d->width = cfg_getint(config, "width");
d->height = cfg_getfloat(config, "height");
d->padding_left = cfg_getint(config, "padding_left");
d->lines_size = d->width - d->padding_left - 2;
d->size = d->width - d->padding_left - 2;
if(d->lines_size < 1)
if(d->size < 1)
{
warn("graph widget needs: (width - padding_left) >= 3\n");
return w;
}
/* prevent: division by zero */
d->current_max = d->max = cfg_getfloat(config, "max");
if(!(d->max > 0))
if(!(d->data_items = cfg_size(config, "data")))
{
warn("graph widget needs a 'max' value greater than zero\n");
warn("graph widget needs at least one data section\n");
return w;
}
d->lines = p_new(int, d->lines_size);
d->draw_from = p_new(int, d->size);
d->draw_to = p_new(int, d->size);
if (cfg_getbool(config, "scale"))
d->line_values = p_new(float, d->lines_size);
d->fillbottom = p_new(int *, d->size);
d->filltop = p_new(int *, d->size);
d->drawline = p_new(int *, d->size);
if((color = cfg_getstr(config, "fg")))
d->fg = initxcolor(globalconf.display, phys_screen, color);
else
d->fg = globalconf.screens[statusbar->screen].colors_normal[ColFG];
d->values = p_new(float *, d->data_items);
d->lines = p_new(int *, d->data_items);
d->filltop_color = p_new(XColor, d->data_items);
d->fillbottom_color = p_new(XColor, d->data_items);
d->drawline_color = p_new(XColor, d->data_items);
d->max_index = p_new(int, d->data_items);
d->current_max = p_new(float, d->data_items);
d->max = p_new(float, d->data_items);
for(i = 0; i < d->data_items; i++)
{
cfg = cfg_getnsec(config, "data", i);
if((color = cfg_getstr(cfg, "fg")))
tmp_color = initxcolor(globalconf.display, phys_screen, color);
else
tmp_color = globalconf.screens[statusbar->screen].colors_normal[ColFG];
if (cfg_getbool(cfg, "scale"))
d->values[i] = p_new(float, d->size); /* not null -> scale = true */
/* prevent: division by zero */
d->current_max[i] = d->max[i] = cfg_getfloat(cfg, "max");
if(!(d->max[i] > 0))
{
warn("all graph widget needs a 'max' value greater than zero\n");
d->data_items = 0;
return w;
}
d->lines[i] = p_new(int, d->size);
/* filter each style-typ into it's own array (for easy looping later)*/
if ((type = cfg_getstr(cfg, "style")))
{
if(!strncmp(type, "bottom", sizeof("bottom")))
{
d->fillbottom[d->fillbottom_total] = d->lines[i];
d->fillbottom_color[d->fillbottom_total] = tmp_color;
d->fillbottom_total++;
}
else if (!strncmp(type, "top", sizeof("top")))
{
d->filltop[d->filltop_total] = d->lines[i];
d->filltop_color[d->filltop_total] = tmp_color;
d->filltop_total++;
}
else if (!strncmp(type, "line", sizeof("line")))
{
d->drawline[d->drawline_total] = d->lines[i];
d->drawline_color[d->drawline_total] = tmp_color;
d->drawline_total++;
}
}
}
if((color = cfg_getstr(config, "bg")))
d->bg = initxcolor(globalconf.display, phys_screen, color);
@ -200,7 +342,7 @@ graph_new(Statusbar *statusbar, cfg_t *config)
if((color = cfg_getstr(config, "bordercolor")))
d->bordercolor = initxcolor(globalconf.display, phys_screen, color);
else
d->bordercolor = d->fg;
d->bordercolor = tmp_color;
return w;
}