? CHANGELOG ? gstripchart.diff Index: Makefile.am =================================================================== RCS file: /cvs/gnome/gnome-utils/gstripchart/Makefile.am,v retrieving revision 1.2 diff -u -r1.2 Makefile.am --- Makefile.am 1998/09/15 08:17:15 1.2 +++ Makefile.am 1998/11/29 23:55:17 @@ -11,7 +11,7 @@ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ -DLOCALEDIR=\"$(datadir)/locale\" \ -I$(includedir) \ - $(GNOME_INCLUDEDIR) + $(GNOME_INCLUDEDIR) $(LIBGTOP_INCS) bin_PROGRAMS = \ gstripchart @@ -22,7 +22,10 @@ gstripchart_LDADD = \ $(GNOME_LIBDIR) \ $(GNOMEUI_LIBS) \ - $(INTLLIBS) + $(INTLLIBS) \ + $(LIBGTOP_LIBS) \ + $(LIBGTOP_EXTRA_LIBS) + EXTRA_DIST = \ gstripchart.desktop \ Index: TODO =================================================================== RCS file: /cvs/gnome/gnome-utils/gstripchart/TODO,v retrieving revision 1.1 diff -u -r1.1 TODO --- TODO 1998/07/13 17:22:01 1.1 +++ TODO 1998/11/29 23:55:17 @@ -1,6 +1,10 @@ -JUST PLAIN BUGS +BUGS TO BE SQUASHED +The code to process a command option error leaves trailing cruft after +the command name, and includes all the gnome-internal options but +omits the long-named varients of the gstripchart options. + Resizing only works when enlarging the size of the window from its default value. When shrinking the window, stuff on the RHS gets squeezed out of the window. This is driving me nuts. It's got to be @@ -8,70 +12,48 @@ a race condition such that the chart doesn't get redrawn for a few seconds after making a sudden, large increase in the size of the window. -When a menu is included, it gets laid over the top of the chart -window. It would be better to pack it into the enclosing window so -that ploted values don't get hidden beneith the menu bar. This, -unfortunately, would leave a large blank area exposed when the menu bar -is torn off. I see no good solution to this quandry. - The help browser doesn't jump to the name tags listed in the topics.dat file. Instead, the help file always loads the help page starting at the top of the file. As far as I can tell, this doesn't work for any other apps either, so I'll assume that the problem lies outside of gstripchart. - -GNOME-SPECIFIC ITEMS - -Add session management code. This will require support for a ---geometry option. +FEATURES TO BE ADDED -CONFIGURATION-SPECIFIC ITEMS +Add session management code. Also, figure out how to account for WM +decorations when dealing with a geometry string that specify a +position relative to the lower or right sides. Use notebook pages for configuration. Have a right-click pop up a configuration notebook. This should probably be written using one of the GUI config tools, such as Linuxconf or COAS. +Add auto-ranging. Having to manually specify the range for a value is +too awkward. -APPLICATION-SPECIFIC ITEMS - -Add a low-pass filter to the position of the slider arrows, so that -they don't jump around so erratically. Maybe do the same for the -chart window. - -Add an option to allow the slider window to be placed on the LHS -instead of the RHS of the frame. - -Add support for named parameters using the Gtop library. This will be -easier to configure and more portable than reading directly from /proc. - -Add a way to read parameter values from a pipe, such as stdin. This -will allow the output of programs to be plotted. - -Add an auto-ranging capability, for things like load which don't have -any well defined upper bound. Perhaps better yet: add named functions -to the eval code. This would permit plotting something like -log(load). Perhaps this should be worked in elsewhere, so that value -displays could be kept in sync. - Add an option to include legends along the sides of the display. The vertical legend could include the name and last value of each parameter. This could get crowded in a hurry. -Allow a few global setup parameters to be defined in the configuration -file. The polling interval, background color, and geometry parameters -come to mind, but there are probably others as well. +Allow values to be plotted using a logarithmic basis rather than a +linear one. This would be especially handy for values such as load +which is normally very low, but which can jump way up at times. Don't draw a line if there's no value available. For example, it should be possible to distinguish between a 'net connection down' condition and a 'no net traffic' condition. This will require some type of conditional test in the config setup. +Allow the background color and geometry parameters to be defined on +the command line and in the config file. + Catch a SIGHUP and re-read the config file. This would be handier than stopping and restarting on each change to the configuration file. -Allow fields to be split on delimtiers other than whitespace. +Allow fields to be split on delimtiers other than whitespace and +colon, then eliminate the colon from the seperator list and add it to +the net I/O items. Use regular expressions instead of strstr for field matching and strtok for splitting? This would be more versatile, but would also Index: gstripchart.c =================================================================== RCS file: /cvs/gnome/gnome-utils/gstripchart/gstripchart.c,v retrieving revision 1.1 diff -u -r1.1 gstripchart.c --- gstripchart.c 1998/07/13 17:22:01 1.1 +++ gstripchart.c 1998/11/29 23:55:22 @@ -18,30 +18,78 @@ * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" #include #include #include #include +#include #include #include #include #include #include + +#include // for ex-pee-arse-geometry routine +#include // for flags returned above -#include #include +#ifdef HAVE_LIBGTOP + +#include +#include +#include + +typedef struct +{ + glibtop_cpu cpu; + glibtop_mem mem; + glibtop_swap swap; + glibtop_uptime uptime; + glibtop_loadavg loadavg; +} +gtop_struct; + +#endif + static char *prog_name = "gstripchart"; -static char *prog_version = "1.4"; +static char *prog_version = "1.5"; static char *config_file = NULL; static float chart_interval = 5.0; +static float chart_filter = 0.0; static float slider_interval = 0.2; -static int include_menu = 1; +static float slider_filter = 0.0; +static int include_menubar = 0; static int include_slider = 1; static int post_init = 0; +static int minor_tick=0, major_tick=0; +static int geometry_flags; +static int geometry_w=160, geometry_h=100; +static int geometry_x, geometry_y; +/* + * streq -- case-blind string comparison returning true on equality. + */ +static int +streq(const char *s1, const char *s2) +{ + return strcasecmp(s1, s2) == 0; +} + +/* + * isident -- ctype-style test for identifier chars. + */ +static int +isident(int c) +{ + return isalnum(c) || c=='-' || c=='_'; +} + /* * Expr -- the info required to evaluate an expression. * @@ -56,14 +104,25 @@ char *eqn_base, *eqn_src; double t_diff; int vars; - double *last, *now; char *s; jmp_buf err_jmp; float val; + + double *last, *now; +#ifdef HAVE_LIBGTOP + gtop_struct *gtp; +#endif } Expr; +/* + * eval_error -- called to report an error in expression evaluation. + * + * Only pre-initialization errors are reported. After initialization, + * the expression evaluator just returns a result of zero, and keeps + * on truckin'. + */ static void eval_error(Expr *e, char *msg, ...) { @@ -80,6 +139,9 @@ exit(EXIT_FAILURE); } +/* + * trimtb -- trims trailing whitespace from a string. + */ static char * trimtb(char *s) { @@ -89,6 +151,9 @@ return s; } +/* + * skipbl -- skips over leading whitespace in a string. + */ static char * skipbl(char *s) { @@ -97,15 +162,119 @@ return s; } +/* + * stripbl -- skips some chars, then strips any leading whitespace. + */ static void stripbl(Expr *e, int skip) { e->s = skipbl(e->s + skip); } -static double -add_op(Expr *e); +#ifdef HAVE_LIBGTOP +typedef struct +{ + char *name; + char type; /* L=long, D=double */ + int *used; + size_t off; +} +Gtop_data; + +static int gtop_cpu; +static int gtop_mem; +static int gtop_swap; +static int gtop_uptime; +static int gtop_loadavg; + +#define GTOP_OFF(el) offsetof(gtop_struct, el) + +Gtop_data gtop_vars[] = +{ + /* glibtop cpu stats */ + { "cpu_total", 'L', >op_cpu, GTOP_OFF(cpu.total) }, + { "cpu_user", 'L', >op_cpu, GTOP_OFF(cpu.user) }, + { "cpu_nice", 'L', >op_cpu, GTOP_OFF(cpu.nice) }, + { "cpu_sys", 'L', >op_cpu, GTOP_OFF(cpu.sys) }, + { "cpu_idle", 'L', >op_cpu, GTOP_OFF(cpu.idle) }, + { "cpu_freq", 'L', >op_cpu, GTOP_OFF(cpu.frequency) }, + + /* glibtop memory stats */ + { "mem_total", 'L', >op_mem, GTOP_OFF(mem.total) }, + { "mem_used", 'L', >op_mem, GTOP_OFF(mem.used) }, + { "mem_free", 'L', >op_mem, GTOP_OFF(mem.free) }, + { "mem_shared", 'L', >op_mem, GTOP_OFF(mem.shared) }, + { "mem_buffer", 'L', >op_mem, GTOP_OFF(mem.buffer) }, + { "mem_cached", 'L', >op_mem, GTOP_OFF(mem.cached) }, + { "mem_user", 'L', >op_mem, GTOP_OFF(mem.user) }, + { "mem_locked", 'L', >op_mem, GTOP_OFF(mem.locked) }, + + /* glibtop swap stats */ + { "swap_total", 'L', >op_swap, GTOP_OFF(swap.total) }, + { "swap_used", 'L', >op_swap, GTOP_OFF(swap.used) }, + { "swap_free", 'L', >op_swap, GTOP_OFF(swap.free) }, + { "swap_pagein", 'L', >op_swap, GTOP_OFF(swap.pageout) }, + { "swap_pageout", 'L', >op_swap, GTOP_OFF(swap.pagein) }, + + /* glibtop uptime stats */ + { "uptime", 'D', >op_uptime, GTOP_OFF(uptime.uptime) }, + { "idletime", 'D', >op_uptime, GTOP_OFF(uptime.idletime) }, + + /* glibtop loadavg stats */ + { "loadavg_running", 'L', >op_loadavg, GTOP_OFF(loadavg.nr_running) }, + { "loadavg_tasks", 'L', >op_loadavg, GTOP_OFF(loadavg.nr_tasks) }, + { "loadavg_1m", 'D', >op_loadavg, GTOP_OFF(loadavg.loadavg[0]) }, + { "loadavg_5m", 'D', >op_loadavg, GTOP_OFF(loadavg.loadavg[1]) }, + { "loadavg_15m", 'D', >op_loadavg, GTOP_OFF(loadavg.loadavg[2]) }, + + /* end of array marker */ + { NULL, 0, NULL, 0 } +}; +/* + * gtop_value -- looks up a glibtop datum name, and returns its value. + */ +static int +gtop_value(char *str, gtop_struct *gtp, double *val) +{ + int i, init = !post_init; + + for (i=0; gtop_vars[i].name; i++) + { + if (streq(str, gtop_vars[i].name)) + { + char *cp = ((char *)gtp) + gtop_vars[i].off; + if (init) + { + (*gtop_vars[i].used)++; + *val = 0; + } + else if (gtop_vars[i].type == 'D') + { + *val = *((double *)cp); + } + else // if (gtop_vars[i].type == 'L') + { + unsigned long ul = *((unsigned long *)cp); + *val = ul; + } + return 1; + } + } + return 0; +} +#endif + +/* + * add_op requires a forward prototype since it gets called from + * num_op when recursing to evaluate a parenthesized expression. + */ +static double add_op(Expr *e); + +/* + * num_op -- evaluates numeric constants, parenthesized expressions, + * and named variables. + */ static double num_op(Expr *e) { @@ -127,45 +296,52 @@ } else if (*e->s == '$' || *e->s == '~') { - if (isdigit(e->s[1])) + int c, id_intro; + char *idp, id[1000]; /* FIX THIS */ + + id_intro = *e->s++; + for (idp = id; isalnum(c = (*idp++ = *e->s++)) || c == '_'; ) + ; + idp[-1] = '\0'; + e->s--; + + if (isdigit(*id)) { - int i = 0, c = *e->s; - for (e->s++; isdigit(*e->s); e->s++) - i = i * 10 + *e->s-'0'; - if (i > e->vars) - eval_error(e, "no such field: %d", i); + int id_num = atoi(id); + if (id_num > e->vars) + eval_error(e, "no such field: %d", id_num); stripbl(e, 0); - val = e->now[i-1]; - if (c == '~') - val -= e->last[i-1]; + val = e->now[id_num-1]; + if (id_intro == '~') + val -= e->last[id_num-1]; } - else + else if (streq(id, "i")) /* nominal update interval */ { - switch (*++e->s) - { - case 'i': /* interval, in seconds */ - val = chart_interval; - /* if (e->s[-1] == '~') val = 0; */ - e->s++; - break; - case 't': /* time of day, in seconds */ - val = e->t_diff; - /* if (e->s[-1] == '~') val = 0; */ - e->s++; - break; - case '\0': - eval_error(e, "missing variable identifer"); - break; - default: - eval_error(e, "invalid variable identifer"); - } + val = chart_interval; + /* if (e->s[-1] == '~') val = 0; */ } + else if (streq(id, "t")) /* time of day, in seconds */ + { + val = e->t_diff; + /* if (e->s[-1] == '~') val = 0; */ + } +#ifdef HAVE_LIBGTOP + else if (gtop_value(id, e->gtp, &val)) + ; /* gtop_value handles the assignment to val */ +#endif + else if (!*id) + eval_error(e, "missing variable identifer"); + else + eval_error(e, "invalid variable identifer: %s", id); } else eval_error(e, "number expected"); return val; } +/* + * mul_op -- evaluates multiplication, division, and remaindering. + */ static double mul_op(Expr *e) { @@ -184,6 +360,9 @@ return val; } +/* + * add_op -- evaluates addition and subtraction, + */ static double add_op(Expr *e) { @@ -200,8 +379,13 @@ return val; } -static double -eval(char *eqn, char *src, double t_diff, int vars, double *last, double *now) +/* + * eval -- sets up an Expr, then calls add_op to evaluate an expression. + */ +static double eval( + char *eqn, char *src, + double t_diff, gtop_struct *gtp, + int vars, double *last, double *now ) { Expr e; e.eqn_base = e.s = eqn; @@ -210,6 +394,7 @@ e.last = last; e.now = now; e.t_diff = t_diff; + e.gtp = gtp; if (setjmp(e.err_jmp)) return e.val; @@ -245,12 +430,19 @@ } Param; +/* + * Param_glob -- an array of Pamams, and a few common variables. + */ typedef struct { int params; Param **parray; + double lpf_const; double t_diff; struct timeval t_last, t_now; +#ifdef HAVE_LIBGTOP + gtop_struct gtop; +#endif } Param_glob; @@ -258,7 +450,18 @@ static Param **chart_param, **slider_param; static Param_glob chart_glob, slider_glob; -static void (*display)(void); /* routine called to redisplay new parameters */ +/* + * display routines: the display variable will be set to one of these. + */ +static void no_display(void); +static void numeric_with_ident(void); +static void numeric_with_graph(void); +static void gtk_graph(void); + +/* + * The display variable points to the display proccessing routine. + */ +static void (*display)(void) = gtk_graph; /* * defns_error -- reports error and exits during config file parsing. @@ -278,24 +481,6 @@ exit(EXIT_FAILURE); } -/* - * streq -- case-blind string comparison returning true on equality. - */ -static int -streq(const char *s1, const char *s2) -{ - return strcasecmp(s1, s2) == 0; -} - -/* - * isident -- ctype-style test for identifier chars. - */ -static int -isident(int c) -{ - return isalnum(c) || c=='-' || c=='_'; -} - /* * split -- breaks a 'key: val' string into two pieces, returning the * broken-out parts of the original string, and a count of how many @@ -329,6 +514,23 @@ return p; } +/* + * yes_no -- evaluates yes/no response strings. + */ +static int +yes_no(char *str) +{ + if (str) + { + str = skipbl(str); + if (*str == '1' || *str == 'y' || *str == 'Y') + return 1; + if (streq("on", str) || streq("true", str)) + return 1; + } + return 0; +} + /* * read_param_defns -- reads parameter definitions from the * configuration file. Allocates space and performs other param @@ -337,7 +539,7 @@ static int read_param_defns(Param ***ppp) { - int i, lineno = 0, params = 0; + int i, j, lineno = 0, params = 0; Param **p = NULL; char fn[FILENAME_MAX], home_fn[FILENAME_MAX]; FILE *fd; @@ -382,12 +584,48 @@ char *key, *val; trimtb(bp); split(bp, &key, &val); - /* An "identifier" keyword introduces a new parameter. - We bump the params count, and allocate and initialize - a new Param struct with default values. */ - if (streq(key, "identifier")) + + /* Handle config file equivalents for some of the + command line options. FIX THIS: These should be + restricted to the beginning of the file, before the + first paramater. */ + if (streq(key, "chart-interval")) + chart_interval = atof(val); + else if (streq(key, "chart-filter")) + chart_filter = atof(val); + else if (streq(key, "slider-interval")) + slider_interval = atof(val); + else if (streq(key, "slider-filter")) + slider_filter = atof(val); + else if (streq(key, "menu")) + include_menubar = yes_no(val); + else if (streq(key, "slider")) + include_slider = yes_no(val); + else if (streq(key, "minor_ticks")) + minor_tick = atoi(val); + else if (streq(key, "major_ticks")) + major_tick = atoi(val); + else if (streq(key, "type")) { - params ++; + if (streq("none", val)) + display = no_display; + else if (streq("text", val)) + display = numeric_with_ident; + else if (streq("graph", val)) + display = numeric_with_graph; + else if (streq("gtk", val)) + display = gtk_graph; + else + defns_error( + fn, lineno, "invalid display type: %s", val); + } + /* An "identifier" or "begin" keyword introduces a new + parameter. We bump the params count, and allocate + and initialize a new Param struct with default + values. */ + else if (streq(key, "identifier") || streq(key, "begin")) + { + params++; p = realloc(p, params * sizeof(*p)); p[params-1] = malloc(sizeof(*p[params-1])); p[params-1]->ident = strdup(val); @@ -404,8 +642,14 @@ p[params-1]->top = 1.0; p[params-1]->bot = 0.0; } + else if (streq(key, "end")) + { + if (!streq(p[params-1]->ident, val)) + defns_error(fn, lineno, "found %s when expecting %s", + val, p[params-1]->ident); + } else if (params == 0) - defns_error(fn, lineno, "ident must be first"); + defns_error(fn, lineno, "identifier or begin must be first"); else if (streq(key, "id_char")) p[params-1]->id_char = val[0]; else if (streq(key, "color")) @@ -442,8 +686,13 @@ for (i=0; ival = malloc(p[i]->max_val * sizeof(p[i]->val[0])); - p[i]->now = malloc(p[i]->vars * sizeof(p[i]->now[0])); p[i]->last = malloc(p[i]->vars * sizeof(p[i]->last[0])); + p[i]->now = malloc(p[i]->vars * sizeof(p[i]->now[0])); + /* The initial `now' values get used as the first set of `last' + values. Set them to zero rather than using whatever random + cruft gets returned by malloc. */ + for (j = 0; j < p[i]->vars; j ++) + p[i]->now[j] = 0; } *ppp = p; @@ -457,64 +706,73 @@ split_and_extract(char *str, Param *p) { int i = 0; - char *t = strtok(str, " \t"); + char *t = strtok(str, " \t:"); while (t && i < p->vars) { p->now[i] = atof(t); - t = strtok(NULL, " \t"); + t = strtok(NULL, " \t:"); i++; } return i; } -static float -get_value(double dt, Param *p) -{ - double val = 0; - FILE *pu = NULL; - char buf[1000]; - - if (p->filename) - if (*p->filename == '|') - pu = popen(p->filename+1, "r"); - else - pu = fopen(p->filename, "r"); - if (pu) - { - fgets(buf, sizeof(buf), pu); - if (p->pattern) - while (!ferror(pu) && !feof(pu) && !strstr(buf, p->pattern)) - fgets(buf, sizeof(buf), pu); - if (*p->filename == '|') pclose(pu); else fclose(pu); - } - - /* Copy now vals to last vals, update now vals, and compute a new - param value based on the new now vals. */ - memcpy(p->last, p->now, p->vars * sizeof(*(p->last))); - split_and_extract(buf, p); - val = eval(p->eqn, p->eqn_src, dt, p->vars, p->last, p->now); - - /* Put the new val into the val history. */ - p->new_val = (p->new_val+1) % p->max_val; - p->val[p->new_val] = val; - if (p->num_val < p->max_val) - p->num_val++; - - return val; -} - static void update_values(Param_glob *pgp) { - int p; + int param_num; pgp->t_last = pgp->t_now; gettimeofday(&pgp->t_now, NULL); pgp->t_diff = (pgp->t_now.tv_sec - pgp->t_last.tv_sec) + (pgp->t_now.tv_usec - pgp->t_last.tv_usec) / 1e6; +#ifdef HAVE_LIBGTOP + if (gtop_cpu) + glibtop_get_cpu(&pgp->gtop.cpu); + if (gtop_mem) + glibtop_get_mem(&pgp->gtop.mem); + if (gtop_swap) + glibtop_get_swap(&pgp->gtop.swap); + if (gtop_uptime) + glibtop_get_uptime(&pgp->gtop.uptime); + if (gtop_loadavg) + glibtop_get_loadavg(&pgp->gtop.loadavg); +#endif + for (param_num = 0; param_num < pgp->params; param_num++) + { + Param *p = pgp->parray[param_num]; + double prev, val = 0; + FILE *pu = NULL; + char buf[1000]; + + if (p->filename) + if (*p->filename == '|') + pu = popen(p->filename+1, "r"); + else + pu = fopen(p->filename, "r"); + if (pu) + { + fgets(buf, sizeof(buf), pu); + if (p->pattern) + while (!ferror(pu) && !feof(pu) && !strstr(buf, p->pattern)) + fgets(buf, sizeof(buf), pu); + if (*p->filename == '|') pclose(pu); else fclose(pu); + } - for (p=0; pparams; p++) - get_value(pgp->t_diff, pgp->parray[p]); + /* Copy now vals to last vals, update now vals, and compute a new + param value based on the new now vals. */ + memcpy(p->last, p->now, p->vars * sizeof(*(p->last))); + split_and_extract(buf, p); + val = eval( + p->eqn, p->eqn_src, pgp->t_diff, &pgp->gtop, + p->vars, p->last, p->now ); + + /* Put the new val into the val history. */ + prev = p->val[p->new_val]; + p->new_val = (p->new_val+1) % p->max_val; + p->val[p->new_val] = prev + pgp->lpf_const * (val - prev); + if (p->num_val < p->max_val) + p->num_val++; + } } /* @@ -638,6 +896,29 @@ return 0; } +/* + * overlay_tick_marks -- draws tick marks along the horizontal center + * of the chart window at the major and minor intervals. + */ +static void +overlay_tick_marks(GtkWidget *widget, int minor, int major) +{ + int p, q, w = widget->allocation.width, c = widget->allocation.height / 2; + + if (minor) + for (q = 1, p = w-1; p >= 0; p -= minor) + { + int d = 1; + if (major && --q == 0) + d += 2, q = major; + gdk_draw_line( + widget->window, widget->style->black_gc, p, c-d, p, c+d); + } +} + +/* + * val2y -- scales a parameter value into a y coordinate value. + */ static int val2y(float val, float top, int height) { @@ -677,6 +958,8 @@ event->area.x, event->area.y, event->area.width, event->area.height); + overlay_tick_marks(widget, minor_tick, major_tick); + return 0; } @@ -710,6 +993,8 @@ widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], pixmap, 0,0, 0,0, w,h); + overlay_tick_marks(widget, minor_tick, major_tick); + return 1; } @@ -771,7 +1056,7 @@ static gint about_callback(void) { - gchar *authors[] = { "John Kodis, kodis@jagunet.com", NULL }; + const gchar *authors[] = { "John Kodis, kodis@jagunet.com", NULL }; GtkWidget *about = gnome_about_new( _(prog_name), prog_version, _("Copyright 1998 John Kodis"), @@ -806,12 +1091,63 @@ GNOMEUIINFO_END }; +/* + * help_menu_action -- pops up an instance of the Gnome help browser, + * and points it toward the gstripchart help file. + */ +static void +help_menu_action(GtkWidget *menu) +{ + static GnomeHelpMenuEntry help_entry = { "gstripchart", "index.html" }; + gnome_help_display(NULL, &help_entry); +} + +/* + * click_handler -- activated on any mouse click in the chart window. + * Creates and pops up a menu in response to the mouse click. + */ +static void +click_handler(GtkWidget *widget, GdkEvent *event, gpointer data) +{ + static GtkWidget *menu=NULL, *menu_item; + + if (menu == NULL) + { + menu = gtk_menu_new(); + + menu_item = gtk_menu_item_new_with_label("Help"); + gtk_menu_append(GTK_MENU(menu), menu_item); + gtk_signal_connect_object(GTK_OBJECT(menu_item), "activate", + GTK_SIGNAL_FUNC(help_menu_action), GTK_OBJECT(widget)); + gtk_widget_show(menu_item); + + menu_item = gtk_menu_item_new_with_label("About"); + gtk_menu_append(GTK_MENU(menu), menu_item); + gtk_signal_connect_object(GTK_OBJECT(menu_item), "activate", + GTK_SIGNAL_FUNC(about_callback), NULL); + gtk_widget_show(menu_item); + + menu_item = gtk_menu_item_new_with_label("Exit"); + gtk_menu_append(GTK_MENU(menu), menu_item); + gtk_signal_connect_object(GTK_OBJECT(menu_item), "activate", + GTK_SIGNAL_FUNC(exit_callback), NULL); + gtk_widget_show(menu_item); + } + // FIX THIS: Should use gnome-popup-menu() instead. + gtk_menu_popup( + GTK_MENU(menu), NULL, NULL, NULL, NULL, + ((GdkEventButton*)event)->button, ((GdkEventButton*)event)->time); +} + +/* + * gtk_graph -- the display processor for the main, Gtk-based + * graphical display. + */ static void gtk_graph(void) { GtkWidget *frame, *h_box, *drawing; - /* Set the nominal drawing window and slider sizes. */ - const int nom_w=160, nom_h=100, slide_w=10; + const int slide_w=10; /* Set the slider width. */ /* Create a top-level window. Set the title and establish delete and destroy event handlers. */ @@ -822,13 +1158,19 @@ gtk_signal_connect( GTK_OBJECT(frame), "delete_event", GTK_SIGNAL_FUNC(destroy_handler), NULL); - if (include_menu) + + /* Set up the pop-up menu handler. If a mennubar was requested, set + that up as well. */ + gtk_signal_connect( + GTK_OBJECT(frame), "button_press_event", + GTK_SIGNAL_FUNC(click_handler), NULL); + if (include_menubar) gnome_app_create_menus(GNOME_APP(frame), mainmenu); /* Create a drawing area. Add it to the window, show it, and set its expose event handler. */ drawing = gtk_drawing_area_new(); - gtk_drawing_area_size(GTK_DRAWING_AREA(drawing), nom_w, nom_h); + gtk_drawing_area_size(GTK_DRAWING_AREA(drawing), geometry_w, geometry_h); h_box = gtk_hbox_new(FALSE, 0); gnome_app_set_contents(GNOME_APP(frame), h_box); @@ -847,7 +1189,7 @@ gtk_box_pack_start(GTK_BOX(h_box), sep, FALSE, TRUE, 0); gtk_widget_show(sep); - gtk_drawing_area_size(GTK_DRAWING_AREA(slider), slide_w, nom_h); + gtk_drawing_area_size(GTK_DRAWING_AREA(slider), slide_w, geometry_h); gtk_box_pack_start(GTK_BOX(h_box), slider, FALSE, FALSE, 0); gtk_widget_show(slider); @@ -859,36 +1201,50 @@ (GtkFunction)slider_timer_handler, slider); } gtk_widget_show(h_box); - gtk_widget_set_events(drawing, GDK_EXPOSURE_MASK); + gtk_widget_set_events(drawing, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK); /* Create timer events for the drawing and slider widgets. */ gtk_timeout_add((int)(1000 * chart_interval), (GtkFunction)chart_timer_handler, drawing); - /* Show the top-level window, set its minimum size, and enter the - main event loop. */ - //gtk_window_set_policy(GTK_WINDOW(frame), TRUE, TRUE, TRUE); - //gtk_widget_set_usize(frame, 300,200); + /* This mess is a failed attempt to handle negative position specs. + It fails to accomodate the size of the window decorations. And + its ugly. And there's got to be a better way. Other than that, + it's perfect. FIX THIS */ + if (geometry_flags & (XNegative | YNegative)) + { + int x, y, w, h, d; + gdk_window_get_geometry(NULL, &x, &y, &w, &h, &d); + if (XNegative) + geometry_x = w + geometry_x - geometry_w - (include_slider? slide_w: 0); + if (YNegative) + geometry_y = h + geometry_y - geometry_h; + } + if (geometry_flags & (XValue | YValue)) + gtk_widget_set_uposition(frame, geometry_x, geometry_y); + + /* Show the top-level window and enter the main event loop. */ gtk_widget_show(frame); - //gdk_window_set_hints( - // frame->window, 0,0, nom_w/2,nom_h/2, 0,0, GDK_HINT_MIN_SIZE); gtk_main(); } -static error_t -arg_proc(int key, char *arg, struct argp_state *state) +static int +proc_arg(int opt, const char *arg) { - switch (key) + printf("proc_arg: opt=%c, arg=%s\n", opt, arg); + switch (opt) { - case 'f': - config_file = arg; - return 0; - case 'i': - chart_interval = atof(arg); - return 0; - case 'j': - slider_interval = atof(arg); - return 0; + case 'f': config_file = strdup(arg); break; + case 'i': chart_interval = atof(arg); break; + case 'I': chart_filter = atof(arg); break; + case 'j': slider_interval = atof(arg); break; + case 'J': slider_filter = atof(arg); break; + case 'M': include_menubar = 1; break; + case 'S': include_slider = 0; break; + case 'g': + geometry_flags = XParseGeometry( + arg, &geometry_x, &geometry_y, &geometry_w, &geometry_h); + break; case 't': if (streq("none", arg)) display = no_display; @@ -901,34 +1257,49 @@ else { fprintf(stderr, "invalid display type: %s\n", arg); - return ARGP_ERR_UNKNOWN; /* FIX THIS */ + return -1; } - return 0; - case 'M': - include_menu = 0; - return 0; - case 'S': - include_slider = 0; - return 0; - default: - return ARGP_ERR_UNKNOWN; } + return 0; } -static struct argp_option arglist[] = -{ - { "config-file", 'f', N_("FILE"), 0, N_("configuration file"), 1 }, - { "chart-interval", 'i', N_("SECS"), 0, N_("chart update interval"), 1 }, - { "slider-interval", 'j', N_("SECS"), 0, N_("slider update interval"), 1 }, - { "display-type", 't', N_("TYPE"), 0, N_("type of display"), 1 }, - { "omit-menu", 'M', NULL, 0, N_("omit menu"), 0 }, - { "omit-slider", 'S', NULL, 0, N_("omit slider"), 0 }, - { NULL, 0, NULL, 0, NULL, 0 } -}; +static void +popt_arg_extractor( + poptContext state, enum poptCallbackReason reason, + const struct poptOption *opt, const char *arg, void *data ) +{ + if (proc_arg(opt->val, arg)) + { + // FIX THIS: the prog name includes trailing junk, and long + // options aren't shown but all the Gnome internal options are. + poptPrintUsage(state, stderr, 0); + exit(EXIT_FAILURE); + } +} -static struct argp argp_opts = -{ - arglist, arg_proc, NULL, NULL, NULL, NULL, NULL +static struct +poptOption arglist[] = +{ + { NULL, '\0', POPT_ARG_CALLBACK, popt_arg_extractor, '\0' }, + { "geometry", 'g', POPT_ARG_STRING, NULL, 'g', + N_("GEO"), N_("geometry") }, + { "config-file", 'f', POPT_ARG_STRING, NULL, 'f', + N_("FILE"), N_("configuration file") }, + { "chart-interval", 'i', POPT_ARG_STRING, NULL, 'i', + N_("SECS"), N_("chart update interval") }, + { "chart-filter", 'I', POPT_ARG_STRING, NULL, 'I', + N_("SECS"), N_("chart LP filter TC") }, + { "slider-interval", 'j', POPT_ARG_STRING, NULL, 'j', + N_("SECS"), N_("slider update interval") }, + { "slider-filter", 'J', POPT_ARG_STRING, NULL, 'J', + N_("SECS"), N_("slider LP filter TC") }, + { "menubar", 'M', POPT_ARG_NONE, NULL, 'M', + NULL, N_("add menubar") }, + { "omit-slider", 'S', POPT_ARG_NONE, NULL, 'S', + NULL, N_("omit slider") }, + { "display-type", 't', POPT_ARG_STRING, NULL, 't', + N_("TYPE"), N_("gtk|text|graph|none") }, + { NULL, '\0', 0, NULL, 0 } }; /* @@ -937,18 +1308,35 @@ int main(int argc, char **argv) { - /* Initialize the Gtk stuff first, whether Gtk is used or not, since - failure to do so will cause the color name to rgb translation - done in read_param_defns to dump core. */ - - /* Get program options and read in the parameter definition file. */ - display = gtk_graph; - argp_program_version = prog_version; - gnome_init(prog_name, &argp_opts, argc, argv, 0, NULL); + int c; + poptContext popt_context; + /* Initialize the i18n stuff. If the gtop library is linked in, + initialize that as well. Let gnome_init initialize the Gnome and + Gtk stuff. */ + bindtextdomain(PACKAGE, GNOMELOCALEDIR); + textdomain(PACKAGE); +#ifdef HAVE_LIBGTOP + glibtop_init_r(&glibtop_global_server, 0, 0); +#endif + + /* Arrange for gnome_init to parse the command line options. The + only option that matters here is the configuration filename. We + then process whatever configuration file is set, either by + default or as specified on the command line. */ + gnome_init_with_popt_table( + prog_name, prog_version, argc, argv, arglist, 0, &popt_context); params = read_param_defns(&chart_param); + + /* Next, we re-parse the command line options so that they'll + override any values set in the configuration file. */ + popt_context = poptGetContext(prog_name, argc, argv, arglist, 0); + while ((c = poptGetNextOpt(popt_context)) > 0) + proc_arg(c, poptGetOptArg(popt_context)); + chart_glob.params = params; chart_glob.parray = chart_param; + chart_glob.lpf_const = exp(-chart_filter / chart_interval); update_values(&chart_glob); /* Clone chart_param into a new slider_param array, then override @@ -973,6 +1361,7 @@ } slider_glob.params = params; slider_glob.parray = slider_param; + slider_glob.lpf_const = exp(-slider_filter / slider_interval); update_values(&slider_glob); } Index: gstripchart.conf =================================================================== RCS file: /cvs/gnome/gnome-utils/gstripchart/gstripchart.conf,v retrieving revision 1.1 diff -u -r1.1 gstripchart.conf --- gstripchart.conf 1998/07/13 17:22:01 1.1 +++ gstripchart.conf 1998/11/29 23:55:22 @@ -2,58 +2,96 @@ # chart.conf -- a configuration file for the stripchart program. # +menu: off +slider: on +type: gtk + +minor_ticks: 12 +major_ticks: 5 + +chart-interval: 5.000 +chart-filter: 0.500 +slider-interval: 0.200 +slider-filter: 0.200 + + # Busy -- the fraction of time that the processor was not idle. # Fields one and two are the total seconds of uptime and idle time. The # difference between these is the busy time. Dividing the busy time # by the delta time scales this into (0..1) regardless of the actual # time interval. -identifier: Busy -id_char: @ -color: blue -filename: /proc/uptime -fields: 2 -equation: (~1-~2)/~1 +begin: Busy + filename: /proc/uptime + fields: 2 + equation: (~1-~2)/~1 + color: blue + id_char: @ +end: Busy # Load -- there are one, five, and fifteen minute load averages # available with no further processing required. -identifier: Load -id_char: + -color: white -filename: /proc/loadavg -fields: 1 -equation: $1 - -# Net inbound data -- the number of bytes received divided by the elapsed -# time interval. This requires that packet accounting be enabled. -# This can be done by putting lines such as these in your ip-up script. +begin: Load + filename: /proc/loadavg + fields: 1 + equation: $1 + color: white + id_char: + +end: Load + +# PPP data rates, Linux v2.1 edition -- the number of bytes received divided by +# the elapsed time interval. The maximum of 6kB/s is about right for use +# with a 33kb/s modem. + +begin: PPP In + filename: /proc/net/dev + pattern: ppp + fields: 3 + equation: ~2/~t + maximum: 6000 + color: red + id_char: I +end: PPP In + +begin: PPP Out + filename: /proc/net/dev + pattern: ppp + fields: 11 + equation: ~10/~t + maximum: 6000 + color: green + id_char: o +end: PPP Out + +# Net data rates, Linux 2.0 edition -- the number of bytes received +# divided by the elapsed time interval. This requires that packet +# accounting be enabled. This can be done by putting lines such as +# these in your ip-up script. The maximum of 6kB/s is about right for +# use with a 33kb/s modem. # # IPADDR= # /sbin/ipfwadm -A -f # /sbin/ipfwadm -A -a -P all -S $IPADDR -D 0/0 # /sbin/ipfwadm -A -a -P all -S 0/0 -D $IPADDR +# +#begin: Net In +# filename: /proc/net/ip_acct +# pattern: 00000000-> +# fields: 8 +# equation: ~8/~t +# maximum: 6000 +# color: red +# id_char: I +#end: Net In # -# The maximum of 6kB/s is about right for use with a 33kb/s modem. +#begin: Net Out +# filename: /proc/net/ip_acct +# pattern: ->00000000 +# fields: 8 +# equation: ~8/~t +# maximum: 6000 +# color: green +# id_char: o +#end: Net Out -identifier: Net In -id_char: I -color: red -filename: /proc/net/ip_acct -pattern: 00000000-> -fields: 8 -equation: ~8/~t -maximum: 6000 - -# Net outbound data -- the number of bytes transmitted divided by the elapsed -# time interval. This requires that packet accounting be enabled, as above. -# The maximum of 6kB/s is about right for use with a 33kb/s modem. - -identifier: Net Out -id_char: o -color: green -filename: /proc/net/ip_acct -pattern: ->00000000 -fields: 8 -equation: ~8/~t -maximum: 6000 Index: gstripchart.html =================================================================== RCS file: /cvs/gnome/gnome-utils/gstripchart/gstripchart.html,v retrieving revision 1.1 diff -u -r1.1 gstripchart.html --- gstripchart.html 1998/07/13 17:22:01 1.1 +++ gstripchart.html 1998/11/29 23:55:22 @@ -39,9 +39,12 @@ behavior of the program.
   -f, --config-file=FILE     configuration file
+  -g, --geometry=GEOMETRY    geometry
   -i, --chart-interval=SECS  chart update interval
+  -I, --chart-filter=SECS    chart low-pass filter time constant
   -j, --slider-interval=SECS slider update interval
-  -M, --omit-menu            omit menu
+  -J, --slider-filter=SECS   slider low-pass filter time constant
+  -M, --menubar              add menubar
   -S, --omit-slider          omit slider
   -t, --display-type=TYPE    type of display
 		none:	no display is produced (for debugging);
@@ -61,6 +64,45 @@
   -V, --version              Print program version
 
+
+
configuration file +
Specifies a file from which to read configuration information. + If unspecified, the current working directory is checked for a file + named "gstripchart.conf", the user's home directory is checked for a + file named ".gstripchart.conf", and the /etc directory is checked + for a file named "gstripchart.conf". The first such file found is + used. + +
geometry +
A standard X11 geometry specification of the form WxH+X+Y. + +
chart-interval +
slider-interval +
Specifies the time interval in seconds between updates to the + chart window and slider window. If unspecified, the chart window + will be updated every 5 seconds and the slider window will be + updated every 0.2 seconds. + +
chart-filter +
slider-filter +
Specifies the time constant in seconds to be used in low-pass + filtering the data displayed in the chart or slider windows. A time + constant of 0 seconds turns low-pass filtering off, which can result + in a jumpy display. A time constant in the same range as the + interval parameter, described above, is usually a good choice. Much + larger values cause display updates to become sluggish. If + unspecified, no low pass filtering is performed in either window. + +
menubar +
Adds an application menubar to the main window. Normally this + is omitted, and the menu is popped up by right-clicking on the chart + window. + +
omit-slider +
Causes the display of the slider window to be suppressed. + +
+

Configuration

@@ -137,5 +179,33 @@ ~t. The requested update interval is $i (and the delta is ~i, but will always be zero). All the usual infix arithmatic operators are available. + +

If libgtop support has been compiled into the gstripchart program, +a value can be obtained from this library. This provides a portable +method of obtaining many system performance parameters. The following +libgtop parameters are available: + +

+
CPU Statistics +
cpu_total, cpu_user, cpu_nice, cpu_sys, cpu_idle, and cpu_freq + +
Memory Statistics +
mem_total, mem_used, mem_free, mem_shared, mem_buffer, + mem_cached, mem_user, mem_locked + +
Swap Statistics +
swap_total, swap_used, swap_free, swap_pagein, swap_pageout + +
Uptime Statistics +
uptime, idletime + +
Loadavg Statistics +
loadavg_running, loadavg_tasks, + loadavg_1m, loadavg_5m, loadavg_15m +
+ +

These are all signed long integer quantities, except for uptime, +idletime, and the three loadavg values which are floating point +values.