Main Page   Class Hierarchy   Alphabetical List   Data Structures   File List   Data Fields   Globals  

emcstripchart.c

Go to the documentation of this file.
00001 /*
00002  * This is emcstripchart, a modified version of gstripchart. The modifications
00003  * were made by Will Shackleford of the National Institute of Standards and
00004  * Technology and include the following:.
00005 
00006  Modifications:
00007   3-Apr-2000 bindtextdomain stuff disabled.
00008  3-Apr-2000 The "nml" configuration file option was added.
00009  3-Apr-2000 The "emcmot" configuration file option was added.
00010  3-Apr-2000 The "minimum" configuration file option was more completely implemented.
00011  3-Apr-2000 An additional update timeout was added. This allows the data to be polled
00012  more often than display is refreshed.
00013  3-Apr-2000 The text display is updated by a timer not just by clicking on it.
00014  3-Apr-2000 The buttons "pause" and "go" were added. (They only pause and restart the plotting to give you more time to look at something, the EMC is not paused.)
00015  3-Apr-2000 The "bottom" column was added.
00016  3-Apr-2000 When the scale is readjusted, if the starting the starting minimum equaled
00017  the negative of the starting maximum the scale is modified so that it will
00018  maintain this symmetry about zero.
00019  3-Apr-2000 Several of the defualts were changed.
00020 
00021  *
00022  *
00023  * The gstripchart program produces
00024  * stripchart-like graphs using a file-based parameter input mechanism
00025  * and a Gtk-based display mechanism.  It is a part of the Gnome
00026  * project, http://www.gnome.org/.
00027  *
00028  * Copyright (C) 1998,1999 John Kodis <kodis@jagunet.com>
00029  *
00030  * This program is free software; you can redistribute it and/or modify
00031  * it under the terms of the GNU General Public License as published by
00032  * the Free Software Foundation; either version 2, or (at your option)
00033  * any later version.
00034  *
00035  * This program is distributed in the hope that it will be useful,
00036  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00037  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00038  * See the GNU General Public License for more details.
00039  *
00040  * You should have received a copy of the GNU General Public License along
00041  * with this program; if not, write to the Free Software Foundation, Inc.,
00042  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00043  */
00044 
00045 #ifdef HAVE_CONFIG_H
00046 #include "config.h"
00047 #endif
00048 
00049 #include <ctype.h>
00050 #include <errno.h>
00051 #include <math.h>
00052 #include <setjmp.h>
00053 #include <stdarg.h>
00054 #include <stddef.h>
00055 #include <stdio.h>
00056 #include <stdlib.h>
00057 #include <string.h>
00058 #include <sys/time.h>
00059 #include <unistd.h>
00060 #include <signal.h>
00061 
00062 #include <X11/Xlib.h>   /* for the ex-pee-arse-geometry routine */
00063 #include <X11/Xutil.h>  /* for flags returned above */
00064 
00065 #include <gnome.h>
00066 
00067 #define NELS(a) (sizeof(a) / sizeof(*a))
00068 
00069 #ifdef HAVE_LIBGTOP
00070 #include <glibtop.h>
00071 #include <glibtop/union.h>
00072 typedef struct
00073 {
00074   glibtop_cpu     cpu;
00075   glibtop_mem     mem;
00076   glibtop_swap    swap;
00077   glibtop_uptime  uptime;
00078   glibtop_loadavg loadavg;
00079   glibtop_netload netload;
00080 }
00081 gtop_struct;
00082 #endif
00083 
00084 #include "emcglb.h"
00085 
00086 static char *prog_name = "emcstripchart";
00087 static char *prog_version = "1.6";
00088 static char *config_file = NULL;
00089 static float chart_interval = 0.5;
00090 static float chart_filter = 0.0;
00091 static float slider_interval = 0.2;
00092 static float slider_filter = 0.0;
00093 static float update_interval = 0.01;
00094 static int include_menubar = 0;
00095 static int include_slider = 1;
00096 static int timingcomp = 0;
00097 static int status_outline = 0;
00098 static int minor_tick=100, major_tick=0;
00099 static int geometry_flags;
00100 static int geometry_w=300, geometry_h=100;
00101 static int geometry_x, geometry_y;
00102 static int root_width, root_height;
00103 static int update_count = 0;
00104 static int update_chart = 0;
00105 GtkWidget *drawing=NULL;
00106 GtkWidget *slider=NULL;
00107 int update_timer_count = 0;
00108 
00109 extern double gstrip_nml_get(void *);
00110 extern void * gstrip_nml_open(const char *);
00111 extern int nml_new_data();
00112 extern double gstrip_emcmot_get(void *);
00113 extern void * gstrip_emcmot_open(const char *);
00114 extern int emcIniLoad(const char *filename);
00115 
00116 
00117 /*
00118  * streq -- case-blind string comparison returning true on equality.
00119  */
00120 static int
00121 streq(const char *s1, const char *s2)
00122 {
00123   return strcasecmp(s1, s2) == 0;
00124 }
00125 
00126 /*
00127  * isident -- ctype-style test for identifier chars.
00128  */
00129 static int
00130 isident(int c)
00131 {
00132   return isalnum(c) || c=='-' || c=='_';
00133 }
00134 
00135 /*
00136  * digit_shift -- a hi_lo_fmt helper function.
00137  */
00138 static void
00139 digit_shift(char *str, char *digits, int width, int dpos)
00140 {
00141   int w;
00142   if (++dpos <= 0)
00143     {
00144       *str++ = '.';
00145       for (w = 1; w < width; w++)
00146         *str++ = (++dpos <= 0) ? '0' : *digits++;
00147     }
00148   else
00149     for (w = 0; w < width; w++)
00150       {
00151         *str++ = *digits++;
00152         if (--dpos == 0)
00153           {
00154             *str++ = '.';
00155             w++;
00156           }
00157       }
00158 }
00159 
00160 /*
00161  * hi_lo_fmt -- formats a hi and lo value to the same magnitude.
00162  */
00163 static void
00164 hi_lo_fmt(double hv, char *hs, double lv, char *ls)
00165 {
00166   int e, p, t, f;
00167   char s[100];
00168 
00169   sprintf(s, "% 22.16e", hv);
00170   e = atoi(s+20);
00171   s[2] = s[1]; s[1] = '0';
00172   t = 3 * ((e - (e < 0)) / 3);
00173   p = e - t;
00174   hs[0] = s[0];
00175   digit_shift(hs+1, s+2, 4, p);
00176   hs[6] = '\0';
00177   if (0 <= t && t <= 5)
00178     hs[5] = "\0KMGTP"[t/3];
00179   else
00180     sprintf(hs+5, "e%+d", t);
00181 
00182   if (ls)
00183     {
00184       sprintf(s, "% 22.16e", lv);
00185       f = atoi(s+20);
00186       s[2] = s[1]; s[1] = '0';
00187       p = f - t;
00188       ls[0] = s[0];
00189       digit_shift(ls+1, s+2, 4, p);
00190       ls[6] = '\0';
00191       if (0 <= t && t <= 5)
00192         ls[5] = "\0KMGTP"[t/3];
00193       else
00194         sprintf(ls+5, "e%+d", t);
00195     }
00196 }
00197 
00198 /*
00199  * Expr -- the info required to evaluate an expression.
00200  *
00201  * The last and now arrays are doubles in order to handle values that
00202  * are very large integers without loss of precision.  The resultant
00203  * val is expected to fall in a much narrower range with less need for
00204  * precision.  A float is used to conserve storage space in the
00205  * history list.
00206  */
00207 typedef struct
00208 {
00209   char *eqn_base, *eqn_src;
00210   double t_diff;
00211   int vars;
00212 
00213   char *s;
00214   jmp_buf err_jmp;
00215   float val;
00216 
00217   double *last, *now;
00218 #ifdef HAVE_LIBGTOP
00219   gtop_struct *gtp_now, *gtp_last;
00220 #endif
00221 }
00222 Expr;
00223 
00224 /*
00225  * eval_error -- called to report an error in expression evaluation.
00226  *
00227  * Only pre-initialization errors are reported.  After initialization,
00228  * the expression evaluator just returns a result of zero, and keeps
00229  * on truckin'.
00230  */
00231 static void
00232 eval_error(Expr *e, char *msg, ...)
00233 {
00234   va_list args;
00235   e->val = 0.0;
00236   if (update_chart)
00237     longjmp(e->err_jmp, 1);
00238   fflush(stdout);
00239   va_start(args, msg);
00240   fprintf(stderr, "%s: %s: ", prog_name, e->eqn_src? e->eqn_src: "");
00241   vfprintf(stderr, msg, args);
00242   fprintf(stderr, "\n");
00243   va_end(args);
00244   exit(EXIT_FAILURE);
00245 }
00246 
00247 /*
00248  * trimtb -- trims trailing whitespace from a string.
00249  */
00250 static char *
00251 trimtb(char *s)
00252 {
00253   char *e = s + strlen(s) - 1;
00254   while (e >= s && isspace(*e))
00255     *e-- = '\0';
00256   return s;
00257 }
00258 
00259 /*
00260  * skipbl -- skips over leading whitespace in a string.
00261  */
00262 static char *
00263 skipbl(char *s)
00264 {
00265   while (isspace(*s))
00266     s++;
00267   return s;
00268 }
00269 
00270 /*
00271  * stripbl -- skips some chars, then strips any leading whitespace.
00272  */
00273 static void
00274 stripbl(Expr *e, int skip)
00275 {
00276   e->s = skipbl(e->s + skip);
00277 }
00278 
00279 #ifdef HAVE_LIBGTOP
00280 typedef struct
00281 {
00282   char *name;
00283   char type;    /* L=long, D=double */
00284   int *used;
00285   size_t off;
00286 }
00287 Gtop_data;
00288 
00289 /*
00290  * Flags indicating that one or more of the libgtop values is
00291  * referenced, and so the corresponding libgtop routine should be
00292  * called on each eval cycle.
00293  */
00294 static int gtop_cpu;
00295 static int gtop_mem;
00296 static int gtop_swap;
00297 static int gtop_uptime;
00298 static int gtop_loadavg;
00299 static int gtop_netload;
00300 
00301 #define GTOP_OFF(el) offsetof(gtop_struct, el)
00302 
00303 Gtop_data gtop_vars[] =
00304 {
00305   /* glibtop cpu stats */
00306   { "cpu_total",        'L', &gtop_cpu,     GTOP_OFF(cpu.total)             },
00307   { "cpu_user",         'L', &gtop_cpu,     GTOP_OFF(cpu.user)              },
00308   { "cpu_nice",         'L', &gtop_cpu,     GTOP_OFF(cpu.nice)              },
00309   { "cpu_sys",          'L', &gtop_cpu,     GTOP_OFF(cpu.sys)               },
00310   { "cpu_idle",         'L', &gtop_cpu,     GTOP_OFF(cpu.idle)              },
00311   { "cpu_freq",         'L', &gtop_cpu,     GTOP_OFF(cpu.frequency)         },
00312 
00313   /* glibtop memory stats */
00314   { "mem_total",        'L', &gtop_mem,     GTOP_OFF(mem.total)             },
00315   { "mem_used",         'L', &gtop_mem,     GTOP_OFF(mem.used)              },
00316   { "mem_free",         'L', &gtop_mem,     GTOP_OFF(mem.free)              },
00317   { "mem_shared",       'L', &gtop_mem,     GTOP_OFF(mem.shared)            },
00318   { "mem_buffer",       'L', &gtop_mem,     GTOP_OFF(mem.buffer)            },
00319   { "mem_cached",       'L', &gtop_mem,     GTOP_OFF(mem.cached)            },
00320   { "mem_user",         'L', &gtop_mem,     GTOP_OFF(mem.user)              },
00321   { "mem_locked",       'L', &gtop_mem,     GTOP_OFF(mem.locked)            },
00322 
00323   /* glibtop swap stats */
00324   { "swap_total",       'L', &gtop_swap,    GTOP_OFF(swap.total)            },
00325   { "swap_used",        'L', &gtop_swap,    GTOP_OFF(swap.used)             },
00326   { "swap_free",        'L', &gtop_swap,    GTOP_OFF(swap.free)             },
00327   { "swap_pagein",      'L', &gtop_swap,    GTOP_OFF(swap.pageout)          },
00328   { "swap_pageout",     'L', &gtop_swap,    GTOP_OFF(swap.pagein)           },
00329 
00330   /* glibtop uptime stats */
00331   { "uptime",           'D', &gtop_uptime,  GTOP_OFF(uptime.uptime)         },
00332   { "idletime",         'D', &gtop_uptime,  GTOP_OFF(uptime.idletime)       },
00333 
00334   /* glibtop loadavg stats */
00335   { "loadavg_running",  'L', &gtop_loadavg, GTOP_OFF(loadavg.nr_running)    },
00336   { "loadavg_tasks",    'L', &gtop_loadavg, GTOP_OFF(loadavg.nr_tasks)      },
00337   { "loadavg_1m",       'D', &gtop_loadavg, GTOP_OFF(loadavg.loadavg[0])    },
00338   { "loadavg_5m",       'D', &gtop_loadavg, GTOP_OFF(loadavg.loadavg[1])    },
00339   { "loadavg_15m",      'D', &gtop_loadavg, GTOP_OFF(loadavg.loadavg[2])    },
00340 
00341   /* netload stats -- Linux-specific for the time being */
00342   { "net_pkts_in",      'L', &gtop_netload, GTOP_OFF(netload.packets_in)    },
00343   { "net_pkts_out",     'L', &gtop_netload, GTOP_OFF(netload.packets_out)   },
00344   { "net_pkts_total",   'L', &gtop_netload, GTOP_OFF(netload.packets_total) },
00345 
00346   { "net_bytes_in",     'L', &gtop_netload, GTOP_OFF(netload.bytes_in)      },
00347   { "net_bytes_out",    'L', &gtop_netload, GTOP_OFF(netload.bytes_out)     },
00348   { "net_bytes_total",  'L', &gtop_netload, GTOP_OFF(netload.bytes_total)   },
00349 
00350   { "net_errs_in",      'L', &gtop_netload, GTOP_OFF(netload.errors_in)     },
00351   { "net_errs_out",     'L', &gtop_netload, GTOP_OFF(netload.errors_out)    },
00352   { "net_errs_total",   'L', &gtop_netload, GTOP_OFF(netload.errors_total)  },
00353 
00354   /* end of array marker */
00355   { NULL,                0,  NULL,          0 }
00356 };
00357 
00358 /*
00359  * gtop_value -- looks up a glibtop datum name, and returns its value.
00360  */
00361 static int
00362 gtop_value(
00363   char *str, int delta,
00364   gtop_struct *gtp_now, gtop_struct *gtp_last, double *val)
00365 {
00366   int i;
00367   for (i=0; gtop_vars[i].name; i++)
00368     {
00369       if (streq(str, gtop_vars[i].name))
00370         {
00371           char *cp = ((char *)gtp_now) + gtop_vars[i].off;
00372           if (update_chart == 0)
00373             {
00374               (*gtop_vars[i].used)++;
00375               *val = 0;
00376             }
00377           else if (gtop_vars[i].type == 'D')
00378             {
00379               double df = *((double *)cp);
00380               if (delta)
00381                 {
00382                   cp = ((char *)gtp_last) + gtop_vars[i].off;
00383                   df -= *((double *)cp);
00384                 }
00385               *val = df;
00386             }
00387           else /* if (gtop_vars[i].type == 'L') */
00388             {
00389               unsigned long ul = *((unsigned long *)cp);
00390               if (delta)
00391                 {
00392                   cp = ((char *)gtp_last) + gtop_vars[i].off;
00393                   ul -= *((unsigned long *)cp);
00394                 }
00395               *val = ul;
00396             }
00397           return 1;
00398         }
00399     }
00400   return 0;
00401 }
00402 
00403 /*
00404  * get_all_netload -- a Linux-specific routine to get net load
00405  * information for all interfaces
00406  */
00407 static void
00408 get_all_netload(glibtop_netload *netload)
00409 {
00410   FILE *fd;
00411   memset(netload, 0, sizeof(*netload));
00412   if ((fd = fopen("/proc/net/dev", "r")) != NULL)
00413     {
00414       unsigned long bytes_in, pkts_in, errs_in, bytes_out, pkts_out, errs_out;
00415       fscanf(fd, "%*[^\n]\n%*[^\n]\n");
00416       while (fscanf(fd,
00417         "%*[^:]:%ld%ld%ld%*d%*d%*d%*d%*d%ld%ld%ld%*d%*d%*d%*d%*d",
00418         &bytes_in, &pkts_in, &errs_in, &bytes_out, &pkts_out, &errs_out) == 6)
00419         {
00420           netload->packets_in    += pkts_in;
00421           netload->packets_out   += pkts_out;
00422           netload->packets_total += pkts_in + pkts_out;
00423           netload->bytes_in      += bytes_in;
00424           netload->bytes_out     += bytes_out;
00425           netload->bytes_total   += bytes_in + bytes_out;
00426           netload->errors_in     += errs_in;
00427           netload->errors_out    += errs_out;
00428           netload->errors_total  += errs_in + errs_out;
00429         }
00430       fclose(fd);
00431     }
00432 }
00433 #endif
00434 
00435 /*
00436  * add_op requires a forward prototype since it gets called from
00437  * num_op when recursing to evaluate a parenthesized expression.
00438  */
00439 static double add_op(Expr *e);
00440 
00441 /*
00442  * num_op -- evaluates numeric constants, parenthesized expressions,
00443  * and named variables.
00444  */
00445 static double
00446 num_op(Expr *e)
00447 {
00448   double val=0;
00449   if (isdigit(*e->s) || (*e->s && strchr("+-.", *e->s) && isdigit(e->s[1])))
00450     {
00451       char *r;
00452       val = strtod(e->s, &r);
00453       stripbl(e, (int)(r - e->s));
00454     }
00455   else if (*e->s == '(')
00456     {
00457       stripbl(e, 1);
00458       val = add_op(e);
00459       if (*e->s == ')')
00460         stripbl(e, 1);
00461       else
00462         eval_error(e, _("rparen expected"));
00463     }
00464   else if (*e->s == '$' || *e->s == '~')
00465     {
00466       int c, id_intro;
00467       char *idp, id[1000]; /* FIX THIS */
00468 
00469       id_intro = *e->s++;
00470       for (idp = id; isalnum(c = (*idp++ = *e->s++)) || c == '_'; )
00471         ;
00472       idp[-1] = '\0';
00473       e->s--;
00474 
00475       if (isdigit(*id))
00476         {
00477           int id_num = atoi(id);
00478           if (id_num > e->vars)
00479             eval_error(e, _("no such field: %d"), id_num);
00480           val = e->now[id_num-1];
00481           if (id_intro == '~')
00482             val -= e->last[id_num-1];
00483         }
00484       else if (streq(id, "i"))  /* nominal update interval */
00485         {
00486           val = chart_interval;
00487           /* if (e->s[-1] == '~') val = 0; */
00488         }
00489       else if (streq(id, "t"))  /* delta time, in seconds */
00490         {
00491           val = e->t_diff;
00492           /* if (e->s[-1] == '~') val = 0; */
00493         }
00494       else if (streq(id, "u"))  /* update count, for debugging */
00495         {
00496           val = update_count;
00497         }
00498       else if (streq(id, "c"))  /* chart update count, for debugging */
00499         {
00500           val = update_chart;
00501         }
00502 #ifdef HAVE_LIBGTOP
00503       else if (gtop_value(id, id_intro == '~', e->gtp_now, e->gtp_last, &val))
00504           ; /* gtop_value handles the assignment to val */
00505 #endif
00506       else if (!*id)
00507         eval_error(e, _("missing variable identifer"));
00508       else
00509         eval_error(e, _("invalid variable identifer: %s"), id);
00510       stripbl(e, 0);
00511     }
00512   else
00513     eval_error(e, _("number expected"));
00514   return val;
00515 }
00516 
00517 /*
00518  * mul_op -- evaluates multiplication, division, and remaindering.
00519  */
00520 static double
00521 mul_op(Expr *e)
00522 {
00523   double val1 = num_op(e);
00524   while (*e->s=='*' || *e->s=='/' || *e->s=='%')
00525     {
00526       char c = *e->s;
00527       stripbl(e, 1);
00528       if (c == '*')
00529         val1 *= num_op(e);
00530       else
00531         {
00532           double val2 = num_op(e);
00533           if (val2 == 0) /* FIX THIS: there's got to be a better way. */
00534             val1 = 0;
00535           else if (c == '/')
00536             val1 /= val2;
00537           else
00538             val1 = fmod(val1, val2);
00539         }
00540     }
00541   return val1;
00542 }
00543 
00544 /*
00545  * add_op -- evaluates addition and subtraction,
00546  */
00547 static double
00548 add_op(Expr *e)
00549 {
00550   double val = mul_op(e);
00551   while (*e->s=='+' || *e->s=='-')
00552     {
00553       char c = *e->s;
00554       stripbl(e, 1);
00555       if (c == '+')
00556         val += mul_op(e);
00557       else
00558         val -= mul_op(e);
00559     }
00560   return val;
00561 }
00562 
00563 /*
00564  * eval -- sets up an Expr, then calls add_op to evaluate an expression.
00565  */
00566 static double eval(
00567   char *eqn, char *src, double t_diff,
00568 #ifdef HAVE_LIBGTOP
00569   gtop_struct *gtp_now,
00570   gtop_struct *gtp_last,
00571 #endif
00572   int vars, double *last, double *now )
00573 {
00574   Expr e;
00575   e.eqn_base = e.s = eqn;
00576   e.eqn_src = src;
00577   e.vars = vars;
00578   e.last = last;
00579   e.now  = now;
00580   e.t_diff = t_diff;
00581 #ifdef HAVE_LIBGTOP
00582   e.gtp_now = gtp_now;
00583   e.gtp_last = gtp_last;
00584 #endif
00585 
00586   if (setjmp(e.err_jmp))
00587     return e.val;
00588 
00589   stripbl(&e, 0);
00590   e.val = add_op(&e);
00591   if (*e.s)
00592     eval_error(&e, _("extra gunk at end: \"%s\""), e.s);
00593 
00594   return e.val;
00595 }
00596 
00597 /*
00598  * Param -- struct describing a single parameter.
00599  */
00600 typedef struct
00601 {
00602   char active;          /* set if this parameter should be evaluated */
00603   char is_led;          /* set if this is an LED display, rather than a plot */
00604   char id_char;         /* single-char parameter name abbreviation */
00605   char *ident;          /* paramater name */
00606   char *filename;       /* the file to read this parameter from */
00607   char *pattern;        /* marks the line in the file for this paramater */
00608   char *eqn;            /* equation used to compute this paramater value */
00609   char *eqn_src;        /* where the eqn was defined, for error reporting */
00610   float hi, lo;         /* highest and lowest values expected */
00611   float top, bot;       /* highest and lowest display values */
00612   char *color_name;     /* the name of the fg color for this parameter */
00613   GdkColor gdk_color;   /* the rgb values for the color */
00614   GdkGC *gdk_gc;        /* the graphics context entry for the color */
00615   int num_leds;
00616   GdkColor *led_color;  /* the rgb values for the LED colors */
00617   GdkGC **led_gc;       /* the graphics context entries for the LED colors */
00618   int vars;             /* how many vars to read from the line in the file */
00619   double *last, *now;   /* the last and current vars, as enumerated above */
00620   float *val;           /* value history */
00621   void *nml_data;
00622   void *emcmot_data;
00623 }
00624 Param;
00625 
00626 /*
00627  * Param_glob -- an array of Pamams, and a few common variables.
00628  */
00629 typedef struct
00630 {
00631   int params;
00632   Param **parray;
00633   int max_val;          /* how many vals are allocated  */
00634   int num_val;          /* how many vals are currently in use */
00635   int new_val;          /* the index of the newest val */
00636   double lpf_const;
00637   double t_diff;
00638   struct timeval t_last, t_now;
00639 #ifdef HAVE_LIBGTOP
00640   gtop_struct gtop_now, gtop_last;
00641 #endif
00642 }
00643 Param_glob;
00644 
00645 static Param_glob chart_glob, slider_glob;
00646 
00647 /*
00648  * display routines: the display variable will be set to one of these.
00649  */
00650 static void no_display(void);
00651 static void numeric_with_ident(void);
00652 static void numeric_with_graph(void);
00653 static void gtk_graph(void);
00654 
00655 /*
00656  * The display variable points to the display proccessing routine.
00657  */
00658 static void (*display)(void) = gtk_graph;
00659 
00660 /*
00661  * defns_error -- reports error and exits during config file parsing.
00662  */
00663 static void
00664 defns_error(char *fn, int ln, char *fmt, ...)
00665 {
00666   va_list args;
00667   fflush(stdout);
00668   va_start(args, fmt);
00669   fprintf(stderr, "%s: ", prog_name);
00670   if (fn)
00671     fprintf(stderr, _("%s, line %d: "), fn, ln);
00672   vfprintf(stderr, fmt, args);
00673   fprintf(stderr, "\n");
00674   va_end(args);
00675   exit(EXIT_FAILURE);
00676 }
00677 
00678 /*
00679  * split -- breaks a 'key: val' string into two pieces, returning the
00680  * broken-out parts of the original string, and a count of how many
00681  * pieces were found, which should be 2.  This is ugly, and could
00682  * stand improvement.
00683  */
00684 static int
00685 split(char *str, char **key, char **val)
00686 {
00687   int p=0;
00688   *key = *val = NULL;
00689   str = skipbl(str);
00690   if (isalpha(*str))
00691     {
00692       p = 1;
00693       *key = str;
00694       while (isident(*str))
00695         str++;
00696       if (*str)
00697         {
00698           *str++ = '\0';
00699           while (isspace(*str))
00700             str++;
00701           if (*str)
00702             {
00703               *val = str;
00704               p = 2;
00705             }
00706         }
00707     }
00708   return p;
00709 }
00710 
00711 /*
00712  * yes_no -- evaluates yes/no response strings.
00713  */
00714 static int
00715 yes_no(char *str)
00716 {
00717   if (str)
00718     {
00719       str = skipbl(str);
00720       if (*str == '1' || *str == 'y' || *str == 'Y')
00721         return 1;
00722       if (streq("on", str) || streq("true", str))
00723         return 1;
00724     }
00725   return 0;
00726 }
00727 
00728 /*
00729  * read_param_defns -- reads parameter definitions from the
00730  * configuration file.  Allocates space and performs other param
00731  * initialization.
00732  */
00733 static int
00734 read_param_defns(Param_glob *pgp)
00735 {
00736   int i, j, lineno = 0, params = 0;
00737   Param **p = NULL;
00738   char fn[FILENAME_MAX], home_fn[FILENAME_MAX], etc_fn[FILENAME_MAX];
00739 #ifdef CONFDIR
00740   char conf_fn[FILENAME_MAX];
00741 #endif
00742   FILE *fd;
00743 
00744   if (config_file)
00745     {
00746       strcpy(fn, config_file);
00747       if ((fd=fopen(fn, "r")) == NULL)
00748         defns_error(NULL, 0, _("can't open config file \"%s\""), config_file);
00749     }
00750   else
00751     {
00752       /* Try in the current working directory. */
00753       sprintf(fn, "%s", "emcstripchart.conf");
00754       if ((fd=fopen(fn, "r")) == NULL)
00755         {
00756           /* Try in the user's home directory. */
00757           sprintf(fn, "%s/%s", getenv("HOME"), ".emcstripchart.conf");
00758           strcpy(home_fn, fn);
00759           if ((fd=fopen(fn, "r")) == NULL)
00760             {
00761               /* Try in the /etc directory. */
00762               strcpy(fn, "/etc/emcstripchart.conf");
00763               strcpy(etc_fn, fn);
00764               if ((fd=fopen(fn, "r")) == NULL)
00765                 {
00766 #ifdef CONFDIR
00767                   /* Try in the conf directory. */
00768                   sprintf(fn, "%s/%s", CONFDIR, "emcstripchart.conf");
00769                   strcpy(conf_fn, fn);
00770                   if ((fd=fopen(fn, "r")) == NULL)
00771 #endif
00772                     {
00773 #ifdef CONFDIR
00774                       defns_error(NULL, 0,
00775                         _("can't open config file \"./emcstripchart.conf\", "
00776                           "\"%s\", \"%s\", or \"%s\""),
00777                         home_fn, etc_fn, conf_fn);
00778 #else
00779                       defns_error(NULL, 0,
00780                         _("can't open config file \"./emcstripchart.conf\", "
00781                           "\"%s\", or \"%s\""),
00782                         home_fn, etc_fn);
00783 #endif
00784                     }
00785                 }
00786             }
00787         }
00788     }
00789 
00790   while (!feof(fd) && !ferror(fd))
00791     {
00792       char *bp, buf[1000]; /* FIX THIS */
00793       if (fgets(buf, sizeof(buf), fd))
00794         {
00795           lineno++;
00796           bp = skipbl(buf);
00797           if (*bp && *bp != '#')
00798             {
00799               char *key, *val;
00800               trimtb(bp);
00801               split(bp, &key, &val);
00802 
00803               /* Handle config file equivalents for some of the
00804                  command line options.  FIX THIS: These should be
00805                  restricted to the beginning of the file, before the
00806                  first paramater. */
00807               if (streq(key, "chart-interval"))
00808                 chart_interval = atof(val);
00809               else if (streq(key, "chart-filter"))
00810                 chart_filter = atof(val);
00811               else if (streq(key, "slider-interval"))
00812                 slider_interval = atof(val);
00813               else if (streq(key, "update-interval"))
00814                 {
00815                   update_interval = atof(val);
00816                   if(update_interval > 0.0005)
00817                     {
00818                       minor_tick = (int) (1.0/update_interval);
00819                       minor_tick += (10 - minor_tick%10)%10;
00820                       major_tick = minor_tick;
00821                     }
00822 #if 0
00823                   printf("update_interval = %f;\tminor_tick=%d\n",update_interval,minor_tick);
00824 #endif
00825                 }
00826               else if (streq(key, "slider-filter"))
00827                 slider_filter = atof(val);
00828               else if (streq(key, "menu"))
00829                 include_menubar = yes_no(val);
00830               else if (streq(key, "timingcomp"))
00831                 timingcomp = yes_no(val);
00832               else if (streq(key, "slider"))
00833                 include_slider = yes_no(val);
00834               else if (streq(key, "status-outline"))
00835                 status_outline = yes_no(val);
00836               else if (streq(key, "minor_ticks"))
00837                 minor_tick = atoi(val);
00838               else if (streq(key, "major_ticks"))
00839                 major_tick = atoi(val);
00840               else if (streq(key, "type"))
00841                 {
00842                   if (streq("none", val))
00843                     display = no_display;
00844                   else if (streq("text", val))
00845                     display = numeric_with_ident;
00846                   else if (streq("graph", val))
00847                     display = numeric_with_graph;
00848                   else if (streq("gtk", val))
00849                     display = gtk_graph;
00850                   else
00851                     defns_error(
00852                       fn, lineno, _("invalid display type: %s"), val);
00853                 }
00854               /* An "identifier" or "begin" keyword introduces a new
00855                  parameter.  We bump the params count, and allocate
00856                  and initialize a new Param struct with default
00857                  values. */
00858               else if (streq(key, "identifier") || streq(key, "begin"))
00859                 {
00860                   params++;
00861                   p = realloc(p, params * sizeof(*p));
00862                   p[params-1] = malloc(sizeof(*p[params-1]));
00863                   p[params-1]->ident = strdup(val);
00864                   p[params-1]->active = 1;
00865                   p[params-1]->is_led = 0;
00866                   p[params-1]->vars = 0;
00867                   p[params-1]->id_char = '*';
00868                   p[params-1]->pattern = NULL;
00869                   p[params-1]->filename = NULL;
00870                   p[params-1]->eqn = NULL;
00871                   p[params-1]->eqn_src = NULL;
00872                   p[params-1]->color_name = NULL;
00873                   p[params-1]->num_leds = 0;
00874                   p[params-1]->led_color = NULL;
00875                   p[params-1]->led_gc = NULL;
00876                   p[params-1]->nml_data=NULL;
00877                   p[params-1]->emcmot_data=NULL;
00878                   p[params-1]->hi = p[params-1]->top = 1.0;
00879                   p[params-1]->lo = p[params-1]->bot = 0.0;
00880                 }
00881               else if (streq(key, "end"))
00882                 {
00883                   if (!streq(p[params-1]->ident, val))
00884                     defns_error(fn, lineno, "found %s when expecting %s",
00885                       val, p[params-1]->ident);
00886                 }
00887               else if (params == 0)
00888                 defns_error(fn, lineno,_("identifier or begin must be first"));
00889               else if (streq(key, "id_char"))
00890                 p[params-1]->id_char = val[0];
00891               else if (streq(key, "color"))
00892                 {
00893                   p[params-1]->color_name = strdup(val);
00894                   if (!gdk_color_parse(val, &p[params-1]->gdk_color))
00895                     defns_error(fn, lineno, _("unrecognized color: %s"), val);
00896                 }
00897               else if (streq(key, "lights"))
00898                 {
00899                   int n;
00900                   Param *pp = p[params-1];
00901                   char *t = strtok(val, ",");
00902                   for (n = 0; t != NULL; n++, t = strtok(NULL, ","))
00903                     {
00904                       char *cname = strdup(skipbl(t));
00905                       trimtb(cname);
00906                       pp->led_color = realloc(
00907                         pp->led_color, (n+1) * sizeof(*pp->led_color));
00908                       if (!gdk_color_parse(cname, &pp->led_color[n]))
00909                         {
00910                           defns_error(
00911                             fn, lineno, _("unrecognized color: %s"), cname);
00912                         }
00913                       free(cname);
00914                     }
00915                   pp->num_leds = n;
00916                   pp->is_led = 1;
00917                 }
00918               else if (streq(key, "active"))
00919                 p[params-1]->active = yes_no(val);
00920               else if (streq(key, "filename"))
00921                 p[params-1]->filename = strdup(val);
00922               else if (streq(key, "pattern"))
00923                 p[params-1]->pattern = strdup(val);
00924               else if (streq(key, "fields"))
00925                 p[params-1]->vars = atoi(val);
00926               else if (streq(key, "equation"))
00927                 {
00928                   char src[FILENAME_MAX+20];
00929                   sprintf(src, "%s, line %d", fn, lineno);
00930                   p[params-1]->eqn_src = strdup(src);
00931                   p[params-1]->eqn = strdup(val);
00932                 }
00933               else if (streq(key, "maximum"))
00934                 p[params-1]->hi = p[params-1]->top = atof(val);
00935               else if (streq(key, "minimum"))
00936                 p[params-1]->lo = p[params-1]->bot = atof(val);
00937               else if (streq(key, "nml"))
00938                   p[params-1]->nml_data = gstrip_nml_open(val);
00939               else if (streq(key, "emcmot"))
00940                   p[params-1]->emcmot_data = gstrip_emcmot_open(val);
00941               else
00942                 defns_error(fn, lineno, _("invalid option: \"%s\""), bp);
00943             }
00944         }
00945     }
00946   fclose(fd);
00947 
00948   /* Allocate space after sizes have been established. */
00949   for (i=0; i<params; i++)
00950     {
00951       p[i]->val  = malloc(pgp->max_val * sizeof(p[i]->val[0]));
00952       p[i]->last = malloc(p[i]->vars * sizeof(p[i]->last[0]));
00953       p[i]->now  = malloc(p[i]->vars * sizeof(p[i]->now[0]));
00954       /* The initial `now' values get used as the first set of `last'
00955          values.  Set them to zero rather than using whatever random
00956          cruft gets returned by malloc. */
00957       for (j = 0; j < p[i]->vars; j ++)
00958         p[i]->now[j] = 0;
00959     }
00960 
00961   pgp->params = params;
00962   pgp->parray = p;
00963   return params;
00964 }
00965 
00966 /*
00967  * split_and_extract -- reads whitespace delimited floats into now values.
00968  */
00969 static int
00970 split_and_extract(char *str, Param *p)
00971 {
00972   int i = 0;
00973   char *t = strtok(str, " \t:");
00974   while (t && i < p->vars)
00975     {
00976       p->now[i] = atof(t);
00977       t = strtok(NULL, " \t:");
00978       i++;
00979     }
00980   return i;
00981 }
00982 
00983 /*
00984  * cap -- quickly finds the next highest number of the form {1,2,5}eX.
00985  */
00986 static double
00987 cap(double x)
00988 {
00989   static const double ranges[] = { .1,.2,.5, 1,2,5, 10,20,50 };
00990 
00991   static const double pow10[] =
00992   {
00993     1e-30, 1e-29, 1e-28, 1e-27, 1e-26, 1e-25, 1e-24, 1e-23, 1e-22, 1e-21,
00994     1e-20, 1e-19, 1e-18, 1e-17, 1e-16, 1e-15, 1e-14, 1e-13, 1e-12, 1e-11,
00995     1e-10, 1e-09, 1e-08, 1e-07, 1e-06, 1e-05, 1e-04, 1e-03, 1e-02, 1e-01,
00996     1e+00, 1e+01, 1e+02, 1e+03, 1e+04, 1e+05, 1e+06, 1e+07, 1e+08, 1e+09,
00997     1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19,
00998     1e+20, 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e30
00999   };
01000 
01001   int e, f, j;
01002   double d;
01003 
01004   if (x < pow10[1] || pow10[NELS(pow10)-2] < x)
01005     {
01006       errno = EDOM;
01007       return x;
01008     }
01009 
01010   frexp( x, &e );               /* e gets the base 2 log of x */
01011   f = e * 0.90308998699194;     /* scale e by 3 / ( log(10) / log(2) ) */
01012   j = f % 3 + 3;                /* j gets index of nearest value in ranges */
01013   d = pow10[ 30 + f / 3 ];      /* d gets decade value */
01014 
01015   while (ranges[j] * d > x) --j;
01016   while (ranges[j] * d < x) ++j;
01017 
01018   return ranges[j] * d;
01019 }
01020 
01021 int redundant_timer_count  =0;
01022 int old_data_count = 0;
01023 
01024 static void
01025 update_values(Param_glob *pgp, Param_glob *slave_pgp)
01026 {
01027   int param_num, last_val_pos, next_val_pos, missed,i, new_data;
01028 
01029   update_count++;
01030   pgp->t_last = pgp->t_now;
01031   gettimeofday(&pgp->t_now, NULL);
01032   pgp->t_diff = (pgp->t_now.tv_sec - pgp->t_last.tv_sec) +
01033     (pgp->t_now.tv_usec - pgp->t_last.tv_usec) / 1e6;
01034 
01035   if(timingcomp)
01036     {
01037       if(pgp->t_diff < update_interval/2)
01038         {
01039           pgp->t_now = pgp->t_last;
01040           update_count--;
01041           update_timer_count--;
01042           redundant_timer_count++;
01043           return;
01044         }
01045       redundant_timer_count = 0;
01046     }
01047 
01048 #ifdef HAVE_LIBGTOP
01049   pgp->gtop_last = pgp->gtop_now;
01050   if (gtop_cpu)
01051     glibtop_get_cpu(&pgp->gtop_now.cpu);
01052   if (gtop_mem)
01053     glibtop_get_mem(&pgp->gtop_now.mem);
01054   if (gtop_swap)
01055     glibtop_get_swap(&pgp->gtop_now.swap);
01056   if (gtop_uptime)
01057     glibtop_get_uptime(&pgp->gtop_now.uptime);
01058   if (gtop_loadavg)
01059     glibtop_get_loadavg(&pgp->gtop_now.loadavg);
01060   if (gtop_netload)
01061     get_all_netload(&pgp->gtop_now.netload);
01062 #endif
01063 
01064   if (update_chart < 3)
01065     {
01066       last_val_pos = 0;
01067       pgp->num_val = next_val_pos = 1;
01068     }
01069   else
01070     {
01071       last_val_pos = pgp->new_val;
01072       if (pgp->num_val < pgp->max_val)
01073         pgp->num_val++;
01074       next_val_pos = ++pgp->new_val;
01075       if (next_val_pos >= pgp->max_val)
01076         next_val_pos = pgp->new_val = 0;
01077     }
01078   new_data = 0;
01079 
01080   for (param_num = 0; param_num < pgp->params; param_num++)
01081     {
01082       if (pgp->parray[param_num]->active)
01083         {
01084           Param *p = pgp->parray[param_num];
01085           double prev, val = 0;
01086           FILE *pu = NULL;
01087           char buf[1000];
01088 
01089           if (p->filename)
01090             {
01091               if (*p->filename == '|')
01092                 pu = popen(p->filename+1, "r");
01093               else
01094                 pu = fopen(p->filename, "r");
01095             }
01096 
01097           *buf = '\0';
01098           if (pu)
01099             {
01100               fgets(buf, sizeof(buf), pu);
01101               if (p->pattern)
01102                 while (!ferror(pu) && !feof(pu) && !strstr(buf, p->pattern))
01103                   fgets(buf, sizeof(buf), pu);
01104               if (*p->filename == '|') pclose(pu); else fclose(pu);
01105             }
01106 
01107           /* Copy now vals to last vals, update now vals, and compute
01108              a new param value based on the new now vals.  */
01109           memcpy(p->last, p->now, p->vars * sizeof(p->last[0]));
01110           if(p->nml_data)
01111             {
01112               val = gstrip_nml_get(p->nml_data);
01113               if(p->eqn)
01114                 {
01115                   p->vars = 1;
01116                   p->now[0] = val;
01117                   val = eval(p->eqn, p->eqn_src, pgp->t_diff,
01118                              p->vars, p->last, p->now);
01119                 }
01120               if(nml_new_data())
01121                 {
01122                   new_data=1;
01123                 }
01124             }
01125           else if(p->emcmot_data)
01126             {
01127               val = gstrip_emcmot_get(p->emcmot_data);
01128               if(p->eqn)
01129                 {
01130                   p->vars = 1;
01131                   p->now[0] = val;
01132                   val = eval(p->eqn, p->eqn_src, pgp->t_diff,
01133                              p->vars, p->last, p->now);
01134                 }
01135               new_data=1;
01136             }
01137           else if ((!p->pattern || strstr(buf, p->pattern))
01138                    && p->vars <= split_and_extract(buf, p))
01139             {
01140               val = eval(
01141                          p->eqn, p->eqn_src, pgp->t_diff,
01142 #ifdef HAVE_LIBGTOP
01143                          &pgp->gtop_now, &pgp->gtop_last,
01144 #endif
01145                          p->vars, p->last, p->now );
01146             }
01147           /* Low-pass filter the new val, and add to the val history. */
01148           prev = update_chart < 3 ? 0 : p->val[last_val_pos];
01149           if(!new_data)
01150             {
01151               if(fabs(prev-val) > fabs(p->top - p->bot)/2000.0)
01152                 {
01153                   new_data = 1;
01154                 }
01155             }
01156           p->val[next_val_pos] = prev + pgp->lpf_const * (val - prev);
01157         }
01158     }
01159   if(timingcomp)
01160     {
01161       if(!new_data && old_data_count < (chart_interval/update_interval)/2)
01162         {
01163           pgp->t_now = pgp->t_last;
01164           update_count--;
01165           update_timer_count--;
01166           old_data_count++;
01167           pgp->new_val = last_val_pos;
01168           return;
01169         }
01170       old_data_count=0;
01171       missed = (int) ((pgp->t_diff+update_interval/2.0)/update_interval);
01172       if(missed > 1 && missed < pgp->max_val)
01173         {
01174           for (param_num = 0; param_num < pgp->params; param_num++)
01175             {
01176               Param *p = pgp->parray[param_num];
01177               double prev, val = 0;
01178               val =  p->val[next_val_pos];
01179               prev = p->val[last_val_pos];
01180 #if 0
01181               if( missed > 3)
01182                 {
01183                   printf("missed = %d, prev = %f, val = %f, last_val_pos=%d, next_val_pos=%d\n",
01184                          missed,prev,val, last_val_pos, next_val_pos);
01185                 }
01186 #endif
01187               for(i = 0; i < missed; i++)
01188                 {
01189                   p->val[next_val_pos] = prev +
01190                     pgp->lpf_const * (val - prev)*
01191                     (((double)(i+1))/((double) missed));
01192 #if 0
01193                   if(i == missed/2 && missed > 3)
01194                     {
01195                       printf("p->val[%d] = %f\n", next_val_pos, p->val[next_val_pos]);
01196                     }
01197 #endif
01198                   if( i + 1 < missed)
01199                     {
01200                       next_val_pos++;
01201                       next_val_pos %= pgp->max_val;
01202                       update_count++;
01203                       update_timer_count++;
01204                     }
01205                 }
01206               if(param_num == pgp->params-1)
01207                 {
01208                   pgp->new_val = next_val_pos;
01209                 }
01210               else
01211                 {
01212                   next_val_pos = pgp->new_val;
01213                 }
01214             }
01215         }
01216     }
01217 }
01218 
01219 /*
01220  * no_display -- does everything but displaying stuff.  For timing purposes.
01221  */
01222 static void
01223 no_display(void)
01224 {
01225   while (1)
01226     {
01227       update_chart++;
01228       update_values(&chart_glob, NULL);
01229       usleep((int)(1e6 * chart_interval));
01230     }
01231 }
01232 
01233 /*
01234  * numeric_with_ident -- prints a line of numeric values.
01235  */
01236 static void
01237 numeric_with_ident(void)
01238 {
01239   int p;
01240 
01241   while (1)
01242     {
01243       update_chart++;
01244       update_values(&chart_glob, NULL);
01245 
01246       for (p=0; p<chart_glob.params; p++)
01247         if (chart_glob.parray[p]->active)
01248           fprintf(stdout, " %12.3f",
01249             chart_glob.parray[p]->val[chart_glob.new_val]);
01250       fprintf(stdout, "\n");
01251       for (p=0; p<chart_glob.params; p++)
01252         if (chart_glob.parray[p]->active)
01253           fprintf(stdout, " %12s", chart_glob.parray[p]->ident);
01254       fprintf(stdout, "\r");
01255       fflush(stdout);
01256       usleep((int)(1e6 * chart_interval));
01257     }
01258 }
01259 
01260 /*
01261  * numeric_with_graph -- creates a lame vertically scrolling plot.
01262  */
01263 static void
01264 numeric_with_graph(void)
01265 {
01266   int p, width=76;
01267   char buf[79+1];
01268 
01269   memset(buf, ' ', sizeof(buf));
01270   buf[sizeof(buf)-1] = '\0';
01271 
01272   while (1)
01273     {
01274       update_chart++;
01275       update_values(&chart_glob, NULL);
01276 
01277       for (p = 0; p < chart_glob.params; p++)
01278         if (chart_glob.parray[p]->active)
01279           {
01280             float v = chart_glob.parray[p]->val[chart_glob.new_val];
01281             float t = chart_glob.parray[p]->top;
01282             float s = (t<=0) ? 0 : v / t;
01283             char per[8];
01284             int i = (int)((width-1) * s + 0.5);
01285             buf[i] = chart_glob.parray[p]->id_char;
01286             sprintf(per, "%d", (int)(100*s));
01287             memcpy(buf+i+1, per, strlen(per));
01288           }
01289       trimtb(buf);
01290       fprintf(stdout, "%s\n", buf);
01291       usleep((int)(1e6 * chart_interval));
01292     }
01293 }
01294 
01295 static int
01296 readjust_top_bottom_for_width(int width)
01297 {
01298   float hi, t, top;
01299   float b, bot,lo;
01300   int p, n, i, v, readjust = 0;
01301 
01302   n = width < chart_glob.num_val ? width : chart_glob.num_val;
01303   for (p = 0; p < chart_glob.params; p++)
01304     {
01305       /* Find the largest and smallest value that'll be visible at this width. */
01306       v = chart_glob.new_val;
01307       b = t = chart_glob.parray[p]->val[v];
01308       for (i = 1; i < n; i++)
01309         {
01310           if (--v < 0)
01311             v = chart_glob.max_val - 1;
01312           if (t < chart_glob.parray[p]->val[v])
01313             t = chart_glob.parray[p]->val[v];
01314           if (b >  chart_glob.parray[p]->val[v])
01315             b = chart_glob.parray[p]->val[v];
01316         }
01317       /* If the lowest or  highest visible value of this parameter differs
01318          significantly from the current top value, adjust top values
01319          and return 1 to initiate a redraw. */
01320       hi = chart_glob.parray[p]->hi;
01321       lo = chart_glob.parray[p]->lo;
01322       top = hi<t ? t : hi;
01323       bot = lo>b ? b : lo;
01324       if(fabs((hi+lo)/(hi-lo)) < 0.001)
01325         {
01326           if(fabs(top) > fabs(bot))
01327             {
01328               bot = -top;
01329             }
01330           else
01331             {
01332               top = -bot;
01333             }
01334         }
01335       if (
01336           (fabs((chart_glob.parray[p]->top - top) / top) > 0.01) ||
01337           (fabs((chart_glob.parray[p]->bot - bot) / bot) > 0.01)
01338           )
01339         {
01340           chart_glob.parray[p]->top = top;
01341           chart_glob.parray[p]->bot = bot;
01342           if (include_slider)
01343             {
01344             slider_glob.parray[p]->top = top;
01345             slider_glob.parray[p]->bot = bot;
01346             }
01347 #if 0
01348           printf("top[%d] %s\t= %g\t=> %g\n",
01349             p, chart_glob.parray[p]->ident, t, chart_glob.parray[p]->top);
01350           printf("bot[%d] %s\t= %g\t=> %g\n",
01351             p, chart_glob.parray[p]->ident, t, chart_glob.parray[p]->bot);
01352 #endif
01353           readjust = 1;
01354         }
01355     }
01356   return readjust;
01357 }
01358 
01359 /*
01360  * Gtk display handler stuff...
01361  */
01362 static GdkPixmap *pixmap;
01363 static GdkColormap *colormap;
01364 
01365 static gint
01366 config_handler(GtkWidget *widget, GdkEventConfigure *e, gpointer whence)
01367 {
01368   int p, c, w = widget->allocation.width, h = widget->allocation.height;
01369 
01370   /* On the initial configuration event, get the window colormap and
01371      allocate a color in it for each parameter color. */
01372   if (colormap == NULL)
01373     {
01374       colormap = gdk_window_get_colormap(widget->window);
01375       for (p = 0; p < chart_glob.params; p++)
01376         {
01377           Param *cgp = chart_glob.parray[p];
01378           if (cgp->is_led)
01379             {
01380               cgp->led_gc = malloc(cgp->num_leds * sizeof(*cgp->led_gc));
01381               for (c = 0; c < cgp->num_leds; c++)
01382                 {
01383                   gdk_color_alloc(colormap, &cgp->led_color[c]);
01384                   cgp->led_gc[c] = gdk_gc_new(widget->window);
01385                   gdk_gc_set_foreground(cgp->led_gc[c], &cgp->led_color[c]);
01386                 }
01387             }
01388           else
01389             {
01390               gdk_color_alloc(colormap, &cgp->gdk_color);
01391               cgp->gdk_gc = gdk_gc_new(widget->window);
01392               gdk_gc_set_foreground(cgp->gdk_gc, &cgp->gdk_color);
01393               if (include_slider)
01394                 slider_glob.parray[p]->gdk_gc = cgp->gdk_gc;
01395             }
01396         }
01397     }
01398 
01399   /* Free any previous pixmap, create a pixmap of window size and
01400      depth, and fill it with the window background color. */
01401   if (pixmap)
01402     gdk_pixmap_unref(pixmap);
01403   pixmap = gdk_pixmap_new(widget->window, w, h, -1);
01404   gdk_draw_rectangle(
01405     pixmap, widget->style->bg_gc[GTK_WIDGET_STATE(widget)], TRUE, 0,0, w,h);
01406 
01407   /* Adjust top values appropriately for the new chart window width. */
01408   readjust_top_bottom_for_width(w);
01409 
01410   return FALSE;
01411 }
01412 
01413 /*
01414  * overlay_tick_marks -- draws tick marks along the horizontal center
01415  * of the chart window at the major and minor intervals.
01416  */
01417 static void
01418 overlay_tick_marks(GtkWidget *widget, int minor, int major)
01419 {
01420   int p, q, w = widget->allocation.width, c = widget->allocation.height / 2;
01421 
01422   if (minor)
01423     for (q = 1, p = w-1; p >= 0; p -= minor)
01424       {
01425         int d = 1;
01426         if (major && --q == 0)
01427           d += 2, q = major;
01428         gdk_draw_line(widget->window, widget->style->black_gc,
01429           p, c-d, p, c+d);
01430       }
01431 }
01432 
01433 /*
01434  * overlay_status_box -- draws a box in the upper-left corner of the
01435  * chart window.
01436  */
01437 static void
01438 overlay_status_box(GtkWidget *widget)
01439 {
01440   int p, x=0, y=0, s=10;
01441 
01442   for (p = 0; p < chart_glob.params; p++)
01443     {
01444       Param *cgp = chart_glob.parray[p];
01445       if (cgp->is_led)
01446         {
01447           int c = cgp->val[chart_glob.new_val] + 0.5;
01448           if (status_outline)
01449             gdk_draw_rectangle(
01450               widget->window, widget->style->black_gc, FALSE, x,y, s,s);
01451           if (c > 0)
01452             {
01453               if (c > cgp->num_leds)
01454                 c = cgp->num_leds;
01455               gdk_draw_rectangle(
01456                 widget->window, cgp->led_gc[c-1], TRUE, x+1,y+1, s-1,s-1);
01457             }
01458           x += s;
01459         }
01460     }
01461 }
01462 
01463 /*
01464  * val2y -- scales a parameter value into a y coordinate value.
01465  */
01466 static int
01467 val2y(float val, float top, int height)
01468 {
01469   return height - ((height-1) * val / top);
01470 }
01471 
01472 static gint
01473 chart_expose_handler(GtkWidget *widget, GdkEventExpose *event)
01474 {
01475   int p, w = widget->allocation.width, h = widget->allocation.height;
01476 
01477   /* Plot as much of the value history as is available and as the
01478      window will hold.  Plot points from newest to oldest until we run
01479      out of data or the window is full. */
01480   for (p = chart_glob.params; p--; )
01481     if (chart_glob.parray[p]->active && !chart_glob.parray[p]->is_led)
01482       {
01483         float top = chart_glob.parray[p]->top;
01484         float bot = chart_glob.parray[p]->bot;
01485         int n = w < chart_glob.num_val ? w : chart_glob.num_val;
01486         int i, j = chart_glob.new_val;
01487         int x0, x1 = w - 1;
01488         int y0, y1 = val2y(chart_glob.parray[p]->val[j]-bot,top-bot, h);
01489         for (i=0; i<n; i++)
01490           {
01491             if (--j < 0) j = chart_glob.max_val - 1;
01492             x0 = x1; y0 = y1;
01493             x1 = x0 - 1;
01494             y1 = val2y(chart_glob.parray[p]->val[j]-bot, top-bot, h);
01495             gdk_draw_line(pixmap, chart_glob.parray[p]->gdk_gc, x0,y0, x1,y1);
01496           }
01497       }
01498 
01499   /* Draw the exposed portions of the pixmap in its window. */
01500   gdk_draw_pixmap(
01501     widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], pixmap,
01502     event->area.x, event->area.y,
01503     event->area.x, event->area.y,
01504     event->area.width, event->area.height);
01505 
01506   overlay_tick_marks(widget, minor_tick, major_tick);
01507   overlay_status_box(widget);
01508 
01509   return FALSE;
01510 }
01511 
01512 
01513 static gint
01514 update_timer_handler(GtkWidget *widget)
01515 {
01516   update_chart++;
01517   update_timer_count++;
01518   update_values(&chart_glob, include_slider? &slider_glob: NULL);
01519   return(TRUE);
01520 }
01521 static gint
01522 chart_timer_handler(GtkWidget *widget)
01523 {
01524   int p, w = widget->allocation.width, h = widget->allocation.height;
01525   /* Collect new parameter values.  If the scale has changed, clear
01526      the pixmap and fake an expose event to reload the pixmap.
01527      Otherwise plot each value in the RHS of the pixmap. */
01528   update_chart++;
01529 #if 0
01530   update_values(&chart_glob, include_slider? &slider_glob: NULL);
01531 #endif
01532   if (readjust_top_bottom_for_width(w))
01533     {
01534       GdkEventExpose expose;
01535       expose.area.x = expose.area.y = 0;
01536       expose.area.width = w; expose.area.height = h;
01537       gdk_draw_rectangle(
01538         pixmap, widget->style->bg_gc[GTK_WIDGET_STATE(widget)],
01539         TRUE, 0,0, w-1,h-1);
01540       chart_expose_handler(widget, &expose);
01541     }
01542   else
01543     {
01544       if(update_timer_count > w)
01545         {
01546           update_timer_count = w;
01547         }
01548       /* Shift the pixmap one pixel left, and clear the RHS  */
01549       gdk_window_copy_area(
01550         pixmap, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], 0, 0,
01551         pixmap, update_timer_count, 0, w-update_timer_count, h);
01552       gdk_draw_rectangle(
01553         pixmap, widget->style->bg_gc[GTK_WIDGET_STATE(widget)],
01554         TRUE, w-update_timer_count, 0, update_timer_count, h);
01555       for (p = chart_glob.params; p--; )
01556         if (chart_glob.parray[p]->active && !chart_glob.parray[p]->is_led)
01557           {
01558             float top = chart_glob.parray[p]->top;
01559             float bot = chart_glob.parray[p]->bot;
01560             int i = chart_glob.new_val;
01561             int m = chart_glob.max_val;
01562             int j =0;
01563             for(j = 0; j < update_timer_count && j < m; j++)
01564               {
01565                 int y1 = val2y(chart_glob.parray[p]->val[(i - update_timer_count +j +m)%m]-bot, top-bot, h);
01566                 int y0 = val2y(chart_glob.parray[p]->val[(i-1 - update_timer_count + j +m)%m]-bot, top-bot, h);
01567                 gdk_draw_line(pixmap, chart_glob.parray[p]->gdk_gc,
01568                               w-2-update_timer_count+j,y0, w-1-update_timer_count +j,y1);
01569               }
01570           }
01571       gdk_draw_pixmap(
01572         widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
01573         pixmap, 0,0, 0,0, w,h);
01574       overlay_tick_marks(widget, minor_tick, major_tick);
01575       overlay_status_box(widget);
01576     }
01577   update_timer_count = 0;
01578   return TRUE;
01579 }
01580 
01581 static gint
01582 slider_redraw(GtkWidget *widget)
01583 {
01584   int p, w = widget->allocation.width, h = widget->allocation.height;
01585   GdkGC *bg = widget->style->bg_gc[GTK_WIDGET_STATE(widget)];
01586   gdk_draw_rectangle(widget->window, bg, TRUE, 0,0, w,h);
01587   for (p = slider_glob.params; p--; )
01588     if (slider_glob.parray[p]->active && !chart_glob.parray[p]->is_led)
01589       {
01590         GdkPoint tri[3];
01591         int y = val2y(
01592           slider_glob.parray[p]->val[slider_glob.new_val]-slider_glob.parray[p]->bot,
01593           slider_glob.parray[p]->top-slider_glob.parray[p]->bot, h);
01594         tri[0].x = 0; tri[0].y = y;
01595         tri[1].x = w; tri[1].y = y-w/2;
01596         tri[2].x = w; tri[2].y = y+w/2;
01597         gdk_draw_polygon(
01598           widget->window, slider_glob.parray[p]->gdk_gc, TRUE, tri, 3);
01599       }
01600   return FALSE;
01601 }
01602 
01603 static gint
01604 slider_expose_handler(GtkWidget *widget, GdkEventExpose *event)
01605 {
01606   slider_redraw(widget);
01607   return FALSE;
01608 }
01609 
01610 static gint
01611 slider_timer_handler(GtkWidget *widget)
01612 {
01613 #if 0
01614   update_values(&slider_glob, NULL);
01615 #endif
01616   slider_redraw(widget);
01617   return TRUE;
01618 }
01619 
01620 static gint
01621 exit_callback(void)
01622 {
01623   gtk_main_quit();
01624   return TRUE;
01625 }
01626 
01627 GnomeUIInfo file_menu[] =
01628 {
01629   {
01630     GNOME_APP_UI_ITEM, N_("E_xit"), N_("Terminate the stripchart program"),
01631     exit_callback, NULL, NULL,
01632     GNOME_APP_PIXMAP_NONE, GNOME_STOCK_MENU_EXIT, 'x', GDK_CONTROL_MASK, NULL
01633   },
01634   GNOMEUIINFO_END
01635 };
01636 
01637 /*
01638  * about_callback -- pops up the standard "about" window.
01639  */
01640 static gint
01641 about_callback(void)
01642 {
01643   const gchar *authors[] = { "John Kodis, kodis@jagunet.com -- Original gstripchart", "Will Shackleford,  shackle@nist.gov -- EMC modifications.", NULL };
01644   GtkWidget *about = gnome_about_new(
01645     _(prog_name), prog_version,
01646     _("Copyright 1998 John Kodis"),
01647     authors,
01648     _("EMC stripchart is a modified version of GNOME stripchart. It plots EMC"
01649       "specific parameters like position and following error."
01650       "The GNOME stripchart program plots various user-specified parameters "
01651       "as a function of time.  Its main use is to chart system performance "
01652       "parameters such as CPU load, CPU utilization, network traffic levels, "
01653       "and the like.  Other more ingenious uses are left as an exercise for "
01654       "the interested user."),
01655     "/usr/local/share/pixmaps/gnoapp-logo.xpm");
01656   gtk_widget_show(about);
01657   return 1;
01658 }
01659 
01660 GnomeUIInfo help_menu[] =
01661 {
01662   {
01663     GNOME_APP_UI_ITEM, N_("_About"), N_("Info about the stripchart program"),
01664     about_callback, NULL, NULL,
01665     GNOME_APP_PIXMAP_NONE, GNOME_STOCK_MENU_ABOUT, 0, 0, NULL
01666   },
01667   GNOMEUIINFO_SEPARATOR,
01668   GNOMEUIINFO_HELP("emcstripchart"),
01669   GNOMEUIINFO_END
01670 };
01671 
01672 GnomeUIInfo mainmenu[] =
01673 {
01674   GNOMEUIINFO_SUBTREE(N_("_File"), file_menu),
01675   GNOMEUIINFO_SUBTREE(N_("_Help"), help_menu),
01676   GNOMEUIINFO_END
01677 };
01678 
01679 /*
01680  * help_menu_action -- pops up an instance of the Gnome help browser,
01681  * and points it toward the emcstripchart help file.
01682  */
01683 static void
01684 help_menu_action(GtkWidget *menu)
01685 {
01686   static GnomeHelpMenuEntry help_entry = { "emcstripchart", "//./doc/emcstripchart.html" };
01687   char fname_buf[200];
01688   char cwd_buf[200];
01689   getcwd(cwd_buf,200);
01690   sprintf(fname_buf,"file://localhost%s/doc/emcstripchart.html",cwd_buf);
01691   gnome_help_goto(NULL, fname_buf);
01692 }
01693 
01694 /*
01695  * Preferences stuff...
01696  */
01697 static GtkWidget **param_active = NULL;
01698 
01699 static void
01700 prefs_apply(GtkWidget *button, gpointer dialog)
01701 {
01702   int p;
01703   for (p = 0; p < chart_glob.params; p++)
01704     {
01705       chart_glob.parray[p]->active =
01706         GTK_TOGGLE_BUTTON(param_active[p])->active;
01707       if (include_slider)
01708         slider_glob.parray[p]->active =
01709           GTK_TOGGLE_BUTTON(param_active[p])->active;
01710     }
01711 }
01712 
01713 static void
01714 prefs_cancel(GtkWidget *button, gpointer dialog)
01715 {
01716   gnome_dialog_close(GNOME_DIALOG(dialog));
01717 }
01718 
01719 static void
01720 prefs_okay(GtkWidget *button, gpointer dialog)
01721 {
01722   prefs_apply(button, dialog);
01723   prefs_cancel(button, dialog);
01724 }
01725 
01726 /*
01727  * prefs_callback -- opens a dialog window for examining and adjusting
01728  * chart parameters.
01729  */
01730 static gint
01731 prefs_callback(GtkWidget *chart, gpointer unused)
01732 {
01733   int p;
01734   GtkWidget *dialog, *notebook, *vbox, *clist, *active, *label;
01735 
01736   notebook = gtk_notebook_new();
01737   param_active = realloc(param_active,
01738     chart_glob.params * sizeof(*param_active));
01739   for (p = 0; p < chart_glob.params; p++)
01740     {
01741       char lo[20], hi[20], range[100];
01742       char *row[2], *ttls[2];
01743       ttls[0] = _("Param"); ttls[1] = _("Value");
01744 
01745       clist = gtk_clist_new_with_titles(NELS(ttls), ttls);
01746 
01747       row[0] = _("Identifier"); row[1] = _(chart_glob.parray[p]->ident);
01748       gtk_clist_append(GTK_CLIST(clist), row);
01749       row[0] = _("Color"); row[1] = _(chart_glob.parray[p]->color_name);
01750       gtk_clist_append(GTK_CLIST(clist), row);
01751       row[0] = _("Filename"); row[1] = chart_glob.parray[p]->filename;
01752       gtk_clist_append(GTK_CLIST(clist), row);
01753       row[0] = _("Pattern"); row[1] = chart_glob.parray[p]->pattern;
01754       gtk_clist_append(GTK_CLIST(clist), row);
01755       row[0] = _("Equation"); row[1] = chart_glob.parray[p]->eqn;
01756       gtk_clist_append(GTK_CLIST(clist), row);
01757       row[0] = _("Expected range"); row[1] = range;
01758       hi_lo_fmt(chart_glob.parray[p]->lo, lo, chart_glob.parray[p]->hi, hi);
01759       sprintf(range, "%s ... %s", lo, hi);
01760       gtk_clist_append(GTK_CLIST(clist), row);
01761       row[0] = _("Displayed range"); row[1] = range;
01762       hi_lo_fmt(chart_glob.parray[p]->bot, lo, chart_glob.parray[p]->top, hi);
01763       sprintf(range, "%s ... %s", lo, hi);
01764       gtk_clist_append(GTK_CLIST(clist), row);
01765       row[0] = _("Current value"); row[1] = range;
01766       hi_lo_fmt(chart_glob.parray[p]->val[chart_glob.new_val], range, 0, NULL);
01767       gtk_clist_append(GTK_CLIST(clist), row);
01768 
01769       gtk_clist_columns_autosize(GTK_CLIST(clist));
01770       gtk_widget_show(clist);
01771 
01772       param_active[p] = active = gtk_check_button_new_with_label(_("Active"));
01773       gtk_toggle_button_set_active(
01774         GTK_TOGGLE_BUTTON(active), chart_glob.parray[p]->active);
01775       gtk_widget_show(active);
01776 
01777       vbox = gtk_vbox_new(FALSE, 0);
01778       gtk_box_pack_start(GTK_BOX(vbox), clist, TRUE, TRUE, 0);
01779       gtk_box_pack_start(GTK_BOX(vbox), active, TRUE, TRUE, 0);
01780       gtk_widget_show(vbox);
01781 
01782       label = gtk_label_new(_(chart_glob.parray[p]->ident));
01783       gtk_widget_show(label);
01784       gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label);
01785     }
01786   gtk_widget_show(notebook);
01787 
01788   dialog = gnome_dialog_new(_("Gnome Stripchart Parameters"),
01789     GNOME_STOCK_BUTTON_OK, GNOME_STOCK_BUTTON_APPLY,
01790     GNOME_STOCK_BUTTON_CANCEL, GNOME_STOCK_BUTTON_HELP, NULL);
01791   gnome_dialog_set_parent(GNOME_DIALOG(dialog), GTK_WINDOW(chart));
01792   gnome_dialog_button_connect(GNOME_DIALOG(dialog), 0,
01793     GTK_SIGNAL_FUNC(prefs_okay), GNOME_DIALOG(dialog));
01794   gnome_dialog_button_connect(GNOME_DIALOG(dialog), 1,
01795     GTK_SIGNAL_FUNC(prefs_apply), GNOME_DIALOG(dialog));
01796   gnome_dialog_button_connect(GNOME_DIALOG(dialog), 2,
01797     GTK_SIGNAL_FUNC(prefs_cancel), GNOME_DIALOG(dialog));
01798   gnome_dialog_button_connect(GNOME_DIALOG(dialog), 3,
01799     GTK_SIGNAL_FUNC(help_menu_action), GNOME_DIALOG(dialog));
01800   gtk_container_add(GTK_CONTAINER(GNOME_DIALOG(dialog)->vbox), notebook);
01801 
01802   gtk_widget_show(dialog);
01803   return 1;
01804 }
01805 
01806 int first_text_load_clist = 1;
01807 /*
01808  * text_load_clist -- fills a clist with chart ident, current, and top values.
01809  */
01810 static void
01811 text_load_clist(GtkWidget *txt, GtkWidget *box)
01812 {
01813   int n;
01814   gtk_clist_freeze(GTK_CLIST(txt));
01815   gtk_clist_clear(GTK_CLIST(txt));
01816   for (n = 0; n < chart_glob.params; n++)
01817     {
01818       Param *p = chart_glob.parray[n];
01819       char val_str[100], top_str[100],bot_str[100];
01820       char *row_strs[4];
01821 
01822       row_strs[0] = _(p->ident);
01823       row_strs[1] = val_str;
01824       row_strs[2] = top_str;
01825       row_strs[3] = bot_str;
01826       hi_lo_fmt(p->top, top_str, p->val[chart_glob.new_val], val_str);
01827       hi_lo_fmt(p->top, top_str, p->bot, bot_str);
01828       gtk_clist_append(GTK_CLIST(txt), row_strs);
01829       gtk_clist_set_foreground(GTK_CLIST(txt), n, &p->gdk_color);
01830       gtk_clist_set_background(GTK_CLIST(txt), n, &box->style->bg[0]);
01831     }
01832   if(first_text_load_clist)
01833     {
01834       gtk_clist_columns_autosize(GTK_CLIST(txt));
01835       first_text_load_clist = 0;
01836     }
01837   gtk_clist_thaw(GTK_CLIST(txt));
01838 }
01839 
01840 /*
01841  * text_update -- updates the text box values on response to a box click.
01842  */
01843 static void
01844 text_update(GtkWidget *box, GdkEvent *event, GtkWidget *txt)
01845 {
01846   first_text_load_clist=1;
01847   text_load_clist(txt, box);
01848 }
01849 
01850 GtkWidget *text_box =NULL;
01851 GtkWidget *text_box_txt=NULL;
01852 guint text_box_timer_id = 0;
01853 guint chart_timer_id = 0;
01854 guint slider_timer_id = 0;
01855 guint update_timer_id = 0;
01856 
01857 
01858 /*
01859  * textbox_close -- updates the text box values on response to a box click.
01860  */
01861 static void
01862 textbox_close(GtkWidget *box, GdkEvent *event, GtkWidget *txt)
01863 {
01864   if(text_box_timer_id > 0)
01865     {
01866       gtk_timeout_remove(text_box_timer_id);
01867       text_box_timer_id=0;
01868     }
01869   if(text_box)
01870     {
01871       gtk_widget_destroy(GTK_WIDGET(text_box));
01872     }
01873   text_box_txt = NULL;
01874   text_box = NULL;
01875 }
01876 
01877 
01878 gint text_box_timer_handler(GtkWidget *widget)
01879 {
01880   if(NULL != text_box && NULL != text_box_txt)
01881     {
01882       text_load_clist(text_box_txt,text_box);
01883       return(TRUE);
01884     }
01885   return(FALSE);
01886 }
01887 
01888 
01889 void on_pause_button_clicked(GtkButton *button, gpointer user_data)
01890 {
01891   if(update_timer_id > 0)
01892     {
01893       gtk_timeout_remove(update_timer_id);
01894       update_timer_id=0;
01895     }
01896   if(chart_timer_id > 0)
01897     {
01898       gtk_timeout_remove(chart_timer_id);
01899       chart_timer_id=0;
01900     }
01901   if(text_box_timer_id > 0)
01902     {
01903       gtk_timeout_remove(text_box_timer_id);
01904       text_box_timer_id=0;
01905     }
01906   if(slider_timer_id > 0)
01907     {
01908       gtk_timeout_remove(slider_timer_id);
01909       slider_timer_id=0;
01910     }
01911 }
01912 
01913 
01914 void on_go_button_clicked(GtkButton *button, gpointer user_data)
01915 {
01916   if(update_timer_id == 0)
01917     {
01918       if(update_interval > 0.0005)
01919         {
01920           minor_tick = (int) (1.0/update_interval);
01921           minor_tick += (10 - minor_tick%10)%10;
01922           major_tick = minor_tick;
01923         }
01924       update_timer_id = gtk_timeout_add((int)(1000 * update_interval),
01925                                         (GtkFunction)update_timer_handler,
01926                                         drawing);
01927     }
01928   if(text_box_timer_id == 0)
01929     {
01930       text_box_timer_id = gtk_timeout_add((int)(2000),
01931                                           (GtkFunction)text_box_timer_handler,
01932                                           text_box);
01933     }
01934   if(chart_timer_id == 0)
01935     {
01936       chart_timer_id = gtk_timeout_add((int)(1000 * chart_interval),
01937                                        (GtkFunction)chart_timer_handler,
01938                                        drawing);
01939     }
01940   if(include_slider)
01941     {
01942       if(slider_timer_id == 0)
01943         {
01944           slider_timer_id = gtk_timeout_add((int)(1000 * slider_interval),
01945                                             (GtkFunction)slider_timer_handler,
01946                                             slider);
01947         }
01948     }
01949 }
01950 
01951 /*
01952  * text_popup -- pops up a top level textual display of current
01953  * parameter values in response to a mouse click.
01954  */
01955 static void
01956 text_popup(GtkWidget *widget, GdkEvent *event)
01957 {
01958  GdkEventButton *button = (GdkEventButton*)event;
01959   GtkWidget *vpaned1;
01960   GtkWidget *hbuttonbox1;
01961   GtkWidget *pause_button;
01962   GtkWidget *go_button;
01963   GtkWidget *list1;
01964 
01965   if (text_box)
01966     {
01967       if(first_text_load_clist)
01968         {
01969           return;
01970         }
01971       if(text_box_timer_id > 0)
01972         {
01973           gtk_timeout_remove(text_box_timer_id);
01974           text_box_timer_id = 0;
01975         }
01976       gtk_widget_destroy(GTK_WIDGET(text_box));
01977       text_box_txt = NULL;
01978       text_box = NULL;
01979     }
01980   else
01981     {
01982       char *titles[4];
01983       titles[0] = _("Param"); titles[1] = _("Current"); titles[2] = _("Top"); titles[3] = _("Bottom");
01984       text_box_txt = gtk_clist_new_with_titles(NELS(titles), titles);
01985       gtk_widget_show(text_box_txt);
01986 
01987       text_box = gtk_window_new(GTK_WINDOW_TOPLEVEL);
01988 
01989       vpaned1 = gtk_vpaned_new ();
01990       gtk_widget_ref (vpaned1);
01991       gtk_object_set_data_full (GTK_OBJECT (text_box), "vpaned1", vpaned1,
01992                                 (GtkDestroyNotify) gtk_widget_unref);
01993       gtk_widget_show (vpaned1);
01994       gtk_container_add (GTK_CONTAINER (text_box), vpaned1);
01995 
01996       hbuttonbox1 = gtk_hbutton_box_new ();
01997       gtk_widget_ref (hbuttonbox1);
01998       gtk_object_set_data_full (GTK_OBJECT (text_box), "hbuttonbox1", hbuttonbox1,
01999                             (GtkDestroyNotify) gtk_widget_unref);
02000       gtk_widget_show (hbuttonbox1);
02001       gtk_container_add (GTK_CONTAINER (vpaned1), hbuttonbox1);
02002 
02003       pause_button = gtk_button_new_with_label ("pause");
02004       gtk_widget_ref (pause_button);
02005       gtk_object_set_data_full (GTK_OBJECT (text_box), "pause_button", pause_button,
02006                                 (GtkDestroyNotify) gtk_widget_unref);
02007       gtk_widget_show (pause_button);
02008       gtk_container_add (GTK_CONTAINER (hbuttonbox1), pause_button);
02009       GTK_WIDGET_SET_FLAGS (pause_button, GTK_CAN_DEFAULT);
02010 
02011       go_button = gtk_button_new_with_label ("go");
02012       gtk_widget_ref (go_button);
02013       gtk_object_set_data_full (GTK_OBJECT (text_box), "go_button", go_button,
02014                                 (GtkDestroyNotify) gtk_widget_unref);
02015       gtk_widget_show (go_button);
02016       gtk_container_add (GTK_CONTAINER (hbuttonbox1), go_button);
02017       GTK_WIDGET_SET_FLAGS (go_button, GTK_CAN_DEFAULT);
02018 
02019       gtk_widget_ref (text_box_txt);
02020       gtk_object_set_data_full (GTK_OBJECT (text_box), "text_box_txt", text_box_txt,
02021                                 (GtkDestroyNotify) gtk_widget_unref);
02022       gtk_widget_show (text_box_txt);
02023       gtk_container_add (GTK_CONTAINER (vpaned1), text_box_txt);
02024 
02025 
02026       gtk_signal_connect (GTK_OBJECT (pause_button), "clicked",
02027                           GTK_SIGNAL_FUNC (on_pause_button_clicked),
02028                           NULL);
02029 
02030       gtk_signal_connect (GTK_OBJECT (go_button), "clicked",
02031                           GTK_SIGNAL_FUNC (on_go_button_clicked),
02032                           NULL);
02033       gtk_signal_connect(GTK_OBJECT(text_box),
02034         "button_press_event", GTK_SIGNAL_FUNC(text_update), text_box_txt);
02035       gtk_signal_connect(GTK_OBJECT(text_box),
02036                          "destroy", GTK_SIGNAL_FUNC(textbox_close), NULL);
02037       gtk_signal_connect(GTK_OBJECT(text_box),
02038                          "delete_event", GTK_SIGNAL_FUNC(textbox_close), NULL);
02039 
02040       first_text_load_clist = 1;
02041       text_load_clist(text_box_txt, widget);
02042       gtk_widget_show(text_box);
02043       /* Create timer events for the drawing and slider widgets. */
02044       text_box_timer_id = gtk_timeout_add((int)(2000),
02045                                           (GtkFunction)text_box_timer_handler,
02046                                           text_box);
02047     }
02048 }
02049 
02050 /*
02051  * menu_popup -- pops up a top level menu in response to a mouse click.
02052  */
02053 static void
02054 menu_popup(GtkWidget *widget, GdkEvent *event)
02055 {
02056   static GtkWidget *menu_item, *menu;
02057 
02058   if (menu == NULL)
02059     {
02060       menu = gtk_menu_new();
02061 
02062       menu_item = gtk_menu_item_new_with_label(_("Key"));
02063       gtk_menu_append(GTK_MENU(menu), menu_item);
02064       gtk_signal_connect_object(GTK_OBJECT(menu_item), "activate",
02065         GTK_SIGNAL_FUNC(text_popup), GTK_OBJECT(widget));
02066       gtk_widget_show(menu_item);
02067       menu_item = gtk_menu_item_new_with_label(_("Help"));
02068       gtk_menu_append(GTK_MENU(menu), menu_item);
02069       gtk_signal_connect_object(GTK_OBJECT(menu_item), "activate",
02070         GTK_SIGNAL_FUNC(help_menu_action), GTK_OBJECT(widget));
02071       gtk_widget_show(menu_item);
02072       menu_item = gtk_menu_item_new_with_label(_("About"));
02073       gtk_menu_append(GTK_MENU(menu), menu_item);
02074       gtk_signal_connect_object(GTK_OBJECT(menu_item), "activate",
02075         GTK_SIGNAL_FUNC(about_callback), NULL);
02076       gtk_widget_show(menu_item);
02077 
02078       menu_item = gtk_menu_item_new_with_label(_("Params"));
02079       gtk_menu_append(GTK_MENU(menu), menu_item);
02080       gtk_signal_connect_object(GTK_OBJECT(menu_item), "activate",
02081         GTK_SIGNAL_FUNC(prefs_callback), GTK_OBJECT(widget));
02082       gtk_widget_show(menu_item);
02083 
02084       menu_item = gtk_menu_item_new_with_label(_("Exit"));
02085       gtk_menu_append(GTK_MENU(menu), menu_item);
02086       gtk_signal_connect_object(GTK_OBJECT(menu_item), "activate",
02087         GTK_SIGNAL_FUNC(exit_callback), NULL);
02088       gtk_widget_show(menu_item);
02089     }
02090 
02091   /* FIX THIS: Should we use gnome_popup_menu() instead? */
02092   gtk_menu_popup(
02093     GTK_MENU(menu), NULL, NULL, NULL, NULL,
02094     ((GdkEventButton*)event)->button, ((GdkEventButton*)event)->time);
02095 }
02096 
02097 /*
02098  * click_handler -- activated on any mouse click in the chart window.
02099  * Creates and pops up a text box for button 1, and a menu for button 3.
02100  */
02101 static void
02102 click_handler(GtkWidget *widget, GdkEvent *event, gpointer unused)
02103 {
02104   GdkEventButton *button = (GdkEventButton*)event;
02105   switch (button->button)
02106     {
02107     case 1: text_popup(widget, event); break;
02108     case 3: menu_popup(widget, event); break;
02109     }
02110 }
02111 
02112 /*
02113  * save_handler -- run in response to a "save-yourself" signal.
02114  */
02115 static int
02116 save_handler(GnomeClient *client,
02117   gint phase, GnomeRestartStyle restart,
02118   gint shutdown, GnomeInteractStyle interact,
02119   gint fast, GtkWidget *frame)
02120 {
02121   int i = 0;
02122   char *argv[20];
02123 
02124   argv[i++] = program_invocation_name;
02125   argv[i++] = "-g";
02126   argv[i++] = gnome_geometry_string(frame->window);
02127   if (config_file)
02128     {
02129       argv[i++] = "-f";
02130       argv[i++] = config_file;
02131     }
02132   gnome_client_set_restart_command(client, i, argv);
02133 
02134   return TRUE;
02135 }
02136 
02137 /*
02138  * gtk_graph -- the display processor for the main, Gtk-based
02139  * graphical display.
02140  */
02141 static void
02142 gtk_graph(void)
02143 {
02144   GnomeClient *client;
02145   GtkWidget *frame, *h_box;
02146   const int slide_w=10;         /* Set the slider width. */
02147 
02148   /* Create a drawing area.  Add it to the window, show it, and
02149      set its expose event handler. */
02150   drawing = gtk_drawing_area_new();
02151   gtk_drawing_area_size(GTK_DRAWING_AREA(drawing), geometry_w, geometry_h);
02152   gtk_widget_set_events(drawing, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK);
02153   gtk_widget_show(drawing);
02154 
02155   h_box = gtk_hbox_new(FALSE, 0);
02156   gtk_box_pack_start(GTK_BOX(h_box), drawing, TRUE, TRUE, 0);
02157 
02158   gtk_signal_connect(GTK_OBJECT(drawing),
02159     "configure_event", (GtkSignalFunc)config_handler, "draw");
02160   gtk_signal_connect(GTK_OBJECT(drawing),
02161     "expose_event", (GtkSignalFunc)chart_expose_handler, NULL);
02162   if (include_slider)
02163     {
02164       GtkWidget *sep = gtk_vseparator_new();
02165       slider = gtk_drawing_area_new();
02166 
02167       gtk_box_pack_start(GTK_BOX(h_box), sep, FALSE, TRUE, 0);
02168       gtk_widget_show(sep);
02169 
02170       gtk_drawing_area_size(GTK_DRAWING_AREA(slider), slide_w, geometry_h);
02171       gtk_box_pack_start(GTK_BOX(h_box), slider, FALSE, FALSE, 0);
02172       gtk_widget_show(slider);
02173 
02174       gtk_widget_set_events(slider, GDK_EXPOSURE_MASK);
02175       gtk_signal_connect(GTK_OBJECT(slider),
02176         "expose_event", (GtkSignalFunc)slider_expose_handler, NULL);
02177 
02178       slider_timer_id = gtk_timeout_add((int)(1000 * slider_interval),
02179                                         (GtkFunction)slider_timer_handler,
02180                                         slider);
02181     }
02182   gtk_widget_show(h_box);
02183 
02184   /* Create a top-level window. Set the title, minimum size (_usize),
02185      initial size (_default_size), and establish delete and destroy
02186      event handlers. */
02187   frame = gnome_app_new(_("emcstripchart"), _("EMC stripchart viewer"));
02188   gtk_widget_set_usize(frame, 1, 1); /* min_w, min_h */
02189   gtk_window_set_default_size(GTK_WINDOW(frame), geometry_w, geometry_h);
02190   gtk_signal_connect(GTK_OBJECT(frame),
02191     "destroy", GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
02192   gtk_signal_connect(GTK_OBJECT(frame),
02193     "delete_event", GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
02194 
02195   /* Set up the pop-up menu handler.  If a mennubar was requested, set
02196      that up as well.  Pack the whole works into the top-level frame. */
02197   gtk_signal_connect(GTK_OBJECT(frame),
02198     "button_press_event", GTK_SIGNAL_FUNC(click_handler), NULL);
02199   if (include_menubar)
02200     gnome_app_create_menus(GNOME_APP(frame), mainmenu);
02201   gnome_app_set_contents(GNOME_APP(frame), h_box);
02202 
02203   /* Create timer events for the drawing and slider widgets. */
02204   chart_timer_id = gtk_timeout_add((int)(1000 * chart_interval),
02205                                    (GtkFunction)chart_timer_handler,
02206                                    drawing);
02207 
02208   /* FIX THIS: This mess is a failed attempt to handle negative
02209      position specs.  It fails to accomodate the size of the window
02210      decorations. And its ugly.  And there's got to be a better way.
02211      Other than that, it's perfect.  */
02212   if (geometry_flags & (XNegative | YNegative))
02213     {
02214       if (XNegative)
02215         geometry_x = root_width  + geometry_x - geometry_w - slide_w;
02216       if (YNegative)
02217         geometry_y = root_height + geometry_y - geometry_h;
02218     }
02219   if (geometry_flags & (XValue | YValue))
02220     gtk_widget_set_uposition(frame, geometry_x, geometry_y);
02221 
02222   /* Set up session management "save-yourself" signal handler. */
02223   if ((client = gnome_master_client()) != NULL)
02224     {
02225       char cwd[PATH_MAX];
02226       getcwd(cwd, sizeof(cwd));
02227       gnome_client_set_current_directory(client, cwd);
02228       gtk_signal_connect(GTK_OBJECT(client),
02229         "save_yourself", GTK_SIGNAL_FUNC(save_handler), frame);
02230     }
02231 
02232   if(update_interval > 0.0005)
02233     {
02234       minor_tick = (int) (1.0/update_interval);
02235       minor_tick += (10 - minor_tick%10)%10;
02236       major_tick = minor_tick;
02237       update_timer_id = gtk_timeout_add((int)(1000 * update_interval),
02238                                         (GtkFunction)update_timer_handler,
02239                                         drawing);
02240     }
02241   else
02242     {
02243       major_tick = minor_tick = 0;
02244     }
02245 
02246   /* Show the top-level window and enter the main event loop. */
02247   gtk_widget_show(frame);
02248   gtk_main();
02249 }
02250 
02251 static int
02252 proc_arg(int opt, const char *arg)
02253 {
02254   printf("opt=%c, arg=%s\n",opt,arg);
02255   switch (opt)
02256     {
02257     case 'f': config_file = strdup(arg); break;
02258     case 'i': chart_interval = atof(arg); break;
02259     case 'I': chart_filter = atof(arg); break;
02260     case 'j': slider_interval = atof(arg); break;
02261     case 'J': slider_filter = atof(arg); break;
02262     case 'M': include_menubar = 1; break;
02263     case 'T': timingcomp = 1; break;
02264     case 'S': include_slider = 0; break;
02265     case 'u':
02266       update_interval = atof(arg);
02267       if(update_interval > 0.0005)
02268         {
02269           minor_tick = (int) (1.0/update_interval);
02270           minor_tick += (10 - minor_tick%10)%10;
02271           major_tick = minor_tick;
02272         }
02273 #if 0
02274       printf("update_interval = %f;\tminor_tick=%d\n",update_interval,minor_tick);
02275 #endif
02276       break;
02277     case 'g':
02278       geometry_flags = XParseGeometry(arg,
02279         &geometry_x, &geometry_y, &geometry_w, &geometry_h);
02280       break;
02281     case 't':
02282       if (streq("gtk", arg))
02283         display = gtk_graph;
02284       else
02285         {
02286           gnome_client_disable_master_connection();
02287           if (streq("none", arg))
02288             display = no_display;
02289           else if (streq("text", arg))
02290             display = numeric_with_ident;
02291           else if (streq("graph", arg))
02292             display = numeric_with_graph;
02293           else
02294             {
02295               fprintf(stderr, _("invalid display type: %s\n"), arg);
02296               return -1;
02297             }
02298         }
02299     }
02300   return 0;
02301 }
02302 
02303 static void
02304 popt_arg_extractor(
02305   poptContext state, enum poptCallbackReason reason,
02306   const struct poptOption *opt, const char *arg, void *data )
02307 {
02308   if (proc_arg(opt->val, arg))
02309     {
02310       /* FIX THIS: the program name includes trailing junk, and
02311        * although the long options aren't shown, all of the Gnome
02312        * internal options are. */
02313       poptPrintUsage(state, stderr, 0);
02314     }
02315 }
02316 
02317 static struct
02318 poptOption arglist[] =
02319 {
02320   { NULL,             '\0', POPT_ARG_CALLBACK, popt_arg_extractor },
02321   { "geometry",        'g', POPT_ARG_STRING, NULL, 'g',
02322     N_("Geometry string: WxH+X+Y"), N_("GEO") },
02323   { "config-file",     'f', POPT_ARG_STRING, NULL, 'f',
02324     N_("Configuration file name"), N_("FILE") },
02325   { "chart-interval",  'i', POPT_ARG_STRING, NULL, 'i',
02326     N_("Chart redisplay interval"), N_("SECS") },
02327   { "chart-filter",    'I', POPT_ARG_STRING, NULL, 'I',
02328     N_("Chart low-pass filter time constant"), N_("SECS"),  },
02329   { "slider-interval", 'j', POPT_ARG_STRING, NULL, 'j',
02330     N_("Slider redisplay interval"), N_("SECS") },
02331   { "slider-filter",   'J', POPT_ARG_STRING, NULL, 'J',
02332     N_("Slider low-pass filter time constant"), N_("SECS") },
02333   { "update-interval", 'u', POPT_ARG_STRING, NULL, 'u',
02334     N_("Interval at which data is actually polled."), N_("SECS") },
02335   { "menubar",         'M', POPT_ARG_NONE, NULL, 'M',
02336     N_("Adds a menubar to the main window"), NULL },
02337   { "timingcomp",         'T', POPT_ARG_NONE, NULL, 'T',
02338     N_("Attempt to compensate for irregularities in timing."), NULL },
02339   { "omit-slider",     'S', POPT_ARG_NONE, NULL, 'S',
02340     N_("Omits slider window"), NULL },
02341   { "display-type",    't', POPT_ARG_STRING, NULL, 't',
02342     N_("TYPE is one of gtk, text, graph, or none"), N_("TYPE") },
02343   { NULL,             '\0', 0, NULL, 0 }
02344 };
02345 
02346 
02347 int first_control_c =0;
02348 void control_c_sighandler(int sig)
02349 {
02350   if(first_control_c)
02351     {
02352       gtk_main_quit();
02353     }
02354   else
02355     {
02356       exit(-1);
02357     }
02358 }
02359 
02360 /*
02361  * main -- for the stripchart display program.
02362  */
02363 int
02364 main(int argc, char **argv)
02365 {
02366   int c;
02367   poptContext popt_context;
02368 
02369   signal(SIGINT, control_c_sighandler);
02370 
02371   emcGetArgs(argc, argv);
02372   emcIniLoad(EMC_INIFILE);
02373 
02374   /* Initialize the i18n stuff.  If the gtop library is linked in,
02375      initialize that as well.  Let gnome_init initialize the Gnome and
02376      Gtk stuff. */
02377 #if 0
02378   bindtextdomain(PACKAGE, GNOMELOCALEDIR);
02379   textdomain(PACKAGE);
02380 #endif
02381 #ifdef HAVE_LIBGTOP
02382   glibtop_init_r(&glibtop_global_server, 0, 0);
02383 #endif
02384 
02385   /* Arrange for gnome_init to parse the command line options.  The
02386      only option that matters here is the configuration filename.  We
02387      then process whatever configuration file is set, either by
02388      default or as specified on the command line. */
02389   gnome_init_with_popt_table(
02390     prog_name, prog_version, argc, argv, arglist, 0, &popt_context);
02391 
02392   root_width = 1;
02393   if (display == gtk_graph)
02394     gdk_window_get_geometry(NULL, NULL, NULL, &root_width, &root_height, NULL);
02395 
02396   chart_glob.max_val = root_width> 1000 ? root_width : 1000;
02397   chart_glob.new_val = chart_glob.num_val = 0;
02398   chart_glob.params = read_param_defns(&chart_glob);
02399   chart_glob.lpf_const = exp(-chart_filter / chart_interval);
02400   update_values(&chart_glob, NULL);
02401 
02402   /* Next, we re-parse the command line options so that they'll
02403      override any values set in the configuration file. */
02404   popt_context = poptGetContext(prog_name, argc, argv, arglist, 0);
02405   while ((c = poptGetNextOpt(popt_context)) != -1)
02406     if (c > 0)
02407       proc_arg(c, poptGetOptArg(popt_context));
02408 
02409   /* Clone chart_param into a new slider_param array, then override
02410      the next, last, and val arrays and associated sizing values. */
02411   if (display == gtk_graph && include_slider)
02412     {
02413       int p;
02414       slider_glob = chart_glob;
02415       slider_glob.max_val = 1;  /* The slider has no history whatsoever. */
02416       slider_glob.num_val = slider_glob.new_val = 0;
02417       slider_glob.parray =
02418         malloc(slider_glob.params * sizeof(*slider_glob.parray));
02419       for (p = 0; p < slider_glob.params; p++)
02420         {
02421           slider_glob.parray[p] = malloc(sizeof(*slider_glob.parray[p]));
02422           *slider_glob.parray[p] = *chart_glob.parray[p];
02423           slider_glob.parray[p]->val =
02424             calloc(slider_glob.max_val,
02425                    sizeof(slider_glob.parray[p]->val[0]));
02426           slider_glob.parray[p]->now =
02427             calloc(slider_glob.parray[p]->vars,
02428                    sizeof(slider_glob.parray[p]->now[0]));
02429           slider_glob.parray[p]->last =
02430             calloc(slider_glob.parray[p]->vars,
02431                    sizeof(slider_glob.parray[p]->last[0]));
02432         }
02433       slider_glob.lpf_const = exp(-slider_filter / slider_interval);
02434       update_values(&slider_glob, NULL);
02435     }
02436 
02437   /* This is part of a none-too-satisfactory initialization sequence.
02438      During the prior update_chart==0 pass, the parameter equations
02439      are evaluated solely to detect syntax errors and to determine
02440      which libgtop routines should be called at the beginning of each
02441      update_values pass.  Any equation evaluation errors detected
02442      during this pass are fatal. */
02443 
02444   /* During the update_chart==1 pass, a set of parameter variables is
02445      gathered, but the resulting parameter values are not evaluated.
02446      This pass serves only to populate the various *_now values so
02447      that reasonable delta values can be computed in the next pass. */
02448   update_chart = 1;
02449   update_values(&chart_glob, NULL);
02450 
02451   /* During the update_chart==2 pass, the normal chart_interval time
02452      delta has elapsed, and can be used to compute time-dependent rate
02453      values.  */
02454   update_chart = 2;
02455   usleep(1000000);
02456   update_values(&chart_glob, NULL);
02457 
02458   /* During the update_chart==3 pass, each prev value of each
02459      parameter is considered valid, and is used in the low-pass
02460      filtering of this and subsequent parameter values. */
02461   /* update_chart = 3, set by the display() prior to update_values(). */
02462   usleep(1000000);
02463   display();
02464 
02465   return EXIT_SUCCESS;
02466 }

Generated on Sun Dec 2 15:27:40 2001 for EMC by doxygen1.2.11.1 written by Dimitri van Heesch, © 1997-2001