1919 lines
41 KiB
C
1919 lines
41 KiB
C
/*
|
|
* wmnd: main program module
|
|
*
|
|
* Copyright(c) 2000-2001 by Reed Lai
|
|
* Copyright(c) 2001 by Timecop <timecop@japan.co.jp>
|
|
* Copyright(c) 2001-2008 by wave++ "Yuri D'Elia" <wavexx@users.sf.net>
|
|
*
|
|
* Distributed under GNU GPL (v2 or above) WITHOUT ANY WARRANTY.
|
|
*/
|
|
|
|
#include "wmnd.h"
|
|
#include "cfgdata.h"
|
|
#include "display.h"
|
|
#include "misc.h"
|
|
#include "master.xpm"
|
|
#include "drivers.h"
|
|
#include "messages.h"
|
|
|
|
|
|
struct var *vars; /* config file option structure */
|
|
struct Devices *devices; /* devices to monitor */
|
|
DevTable wmnd; /* interesting information about devices */
|
|
Dockapp dockapp; /* dockapp structure */
|
|
MRegion mr[32]; /* mouse regions */
|
|
|
|
|
|
/* GUI */
|
|
static void redraw_window(void);
|
|
unsigned long get_color(char* name);
|
|
void new_window(char* res_name, char* res_class,
|
|
int width, int height, int argc, char** argv);
|
|
|
|
/* config file read/write */
|
|
void conf_read(char* filename);
|
|
void conf_write(char* filename);
|
|
void assign(char* name, char* value);
|
|
struct var *lookup(char* name);
|
|
char* value(char* name);
|
|
char* vcopy(char* str);
|
|
|
|
/* utils for command line */
|
|
int strval_fe(const struct pair_strint *data, const char* val);
|
|
int defcon_lk(const char* token);
|
|
void defcon_touch(char* token, char* val);
|
|
|
|
/* support */
|
|
int add_mr(int index, int x, int y, int width, int height);
|
|
int check_mr(int x, int y);
|
|
void beat_event(void);
|
|
void click_event(unsigned int region, unsigned int button);
|
|
static void led_control(const unsigned char led, const unsigned char mode);
|
|
void scale(char* rx_buf, char* tx_buf, unsigned long rx,
|
|
unsigned long tx, const int gap);
|
|
void metric_scale(unsigned char sign, unsigned long value, char* buf);
|
|
void binary_scale(unsigned char sign, unsigned long value, char* buf);
|
|
void draw_string(const char* buf, unsigned int x, unsigned int y);
|
|
void smooth(unsigned long* stat, const unsigned long last, const float smooth);
|
|
|
|
/* device statistics */
|
|
void draw_interface(void);
|
|
void draw_rate(unsigned long rx, unsigned long tx, const int gap);
|
|
void draw_max(unsigned long rx, unsigned long tx);
|
|
void draw_stats(struct Devices *ptr, const int gap);
|
|
|
|
/* driver functions */
|
|
int devices_init(const char* driver, const char* interface);
|
|
void devices_select(const char* interface);
|
|
void devices_prev(void);
|
|
void devices_getstat(struct Devices *device, unsigned long* ip,
|
|
unsigned long* op, unsigned long* ib, unsigned long* ob);
|
|
void devices_destroy(void);
|
|
|
|
/* useless shit */
|
|
void reaper(int sig);
|
|
void usage(void);
|
|
void printversion(void);
|
|
|
|
|
|
int
|
|
add_mr(int index, int x, int y, int width, int height)
|
|
{
|
|
mr[index].enable = 1;
|
|
mr[index].x = x;
|
|
mr[index].y = y;
|
|
mr[index].width = width;
|
|
mr[index].height = height;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
check_mr(int x, int y)
|
|
{
|
|
register int i;
|
|
register int found = 0;
|
|
|
|
for(i = 0; i < 32 && !found; i++)
|
|
{
|
|
if(mr[i].enable && x >= mr[i].x &&
|
|
x <= mr[i].x + mr[i].width &&
|
|
y >= mr[i].y && y <= mr[i].y + mr[i].height)
|
|
found = 1;
|
|
}
|
|
if(!found)
|
|
return REG_NOREG;
|
|
return (i - 1);
|
|
}
|
|
|
|
|
|
static void
|
|
redraw_window(void)
|
|
{
|
|
if(dockapp.update)
|
|
{
|
|
msg_dbg(__POSITION__, "redrawing window");
|
|
XCopyArea(dockapp.d, dockapp.pixmap, dockapp.iconwin, dockapp.gc,
|
|
0, 0, dockapp.width, dockapp.height, 0, 0);
|
|
XCopyArea(dockapp.d, dockapp.pixmap, dockapp.win, dockapp.gc,
|
|
0, 0, dockapp.width, dockapp.height, 0, 0);
|
|
dockapp.update = 0;
|
|
}
|
|
}
|
|
|
|
|
|
unsigned long
|
|
get_color(char* name)
|
|
{
|
|
XColor color;
|
|
XWindowAttributes attr;
|
|
|
|
color.pixel = 0;
|
|
|
|
XGetWindowAttributes(dockapp.d, DefaultRootWindow(dockapp.d), &attr);
|
|
XParseColor(dockapp.d, DefaultColormap(dockapp.d,
|
|
DefaultScreen(dockapp.d)), name, &color);
|
|
color.flags = DoRed | DoGreen | DoBlue;
|
|
XAllocColor(dockapp.d, attr.colormap, &color);
|
|
msg_dbg(__POSITION__, "pixel: %08lx", color.pixel);
|
|
return color.pixel;
|
|
}
|
|
|
|
|
|
void
|
|
new_window(char* res_name, char* res_class,
|
|
int width, int height, int argc, char** argv)
|
|
{
|
|
char* geometry;
|
|
XpmAttributes attr;
|
|
XpmColorSymbol cols[3] =
|
|
{
|
|
{"rx_color", NULL, 0},
|
|
{"tx_color", NULL, 0},
|
|
{"md_color", NULL, 0}
|
|
};
|
|
Pixel fg, bg;
|
|
XGCValues gcval;
|
|
XSizeHints sizehints;
|
|
XClassHint classhint;
|
|
XWMHints wmhints;
|
|
|
|
dockapp.width = width;
|
|
dockapp.height = height;
|
|
dockapp.screen = DefaultScreen(dockapp.d);
|
|
dockapp.root = DefaultRootWindow(dockapp.d);
|
|
|
|
/* parse the geometry spec, if any */
|
|
geometry = value("geometry");
|
|
if(geometry)
|
|
{
|
|
unsigned dummy;
|
|
XParseGeometry(geometry, &dockapp.x, &dockapp.y, &dummy, &dummy);
|
|
}
|
|
|
|
sizehints.flags = USSize | (geometry? USPosition: 0);
|
|
sizehints.x = dockapp.x;
|
|
sizehints.y = dockapp.y;
|
|
sizehints.width = width;
|
|
sizehints.height = height;
|
|
|
|
fg = BlackPixel(dockapp.d, dockapp.screen);
|
|
bg = WhitePixel(dockapp.d, dockapp.screen);
|
|
|
|
dockapp.win = XCreateSimpleWindow(dockapp.d, dockapp.root,
|
|
sizehints.x, sizehints.y, sizehints.width, sizehints.height, 1, fg, bg);
|
|
dockapp.iconwin = XCreateSimpleWindow(dockapp.d, dockapp.win,
|
|
sizehints.x, sizehints.y, sizehints.width, sizehints.height, 1, fg, bg);
|
|
|
|
XSetWMNormalHints(dockapp.d, dockapp.win, &sizehints);
|
|
classhint.res_name = res_name;
|
|
classhint.res_class = res_class;
|
|
XSetClassHint(dockapp.d, dockapp.win, &classhint);
|
|
|
|
#define EVENTS (ExposureMask | ButtonPressMask | \
|
|
ButtonReleaseMask | StructureNotifyMask)
|
|
XSelectInput(dockapp.d, dockapp.win, EVENTS);
|
|
XSelectInput(dockapp.d, dockapp.iconwin, EVENTS);
|
|
|
|
XStoreName(dockapp.d, dockapp.win, res_name);
|
|
XSetIconName(dockapp.d, dockapp.win, res_name);
|
|
|
|
gcval.foreground = fg;
|
|
gcval.background = bg;
|
|
gcval.graphics_exposures = False;
|
|
|
|
dockapp.gc = XCreateGC(dockapp.d, dockapp.win,
|
|
GCForeground | GCBackground | GCGraphicsExposures, &gcval);
|
|
|
|
cols[0].pixel = get_color(value("rx_color"));
|
|
cols[1].pixel = get_color(value("tx_color"));
|
|
cols[2].pixel = get_color(value("md_color"));
|
|
dockapp.stdColors.txColor = cols[1].pixel;
|
|
dockapp.stdColors.rxColor = cols[0].pixel;
|
|
dockapp.stdColors.mdColor = cols[2].pixel;
|
|
|
|
attr.exactColors = 0;
|
|
attr.alloc_close_colors = 1;
|
|
attr.closeness = 1L << 15;
|
|
attr.colorsymbols = cols;
|
|
attr.numsymbols = 3;
|
|
attr.valuemask =
|
|
(XpmColorSymbols | XpmExactColors | XpmAllocCloseColors | XpmCloseness);
|
|
if(XpmCreatePixmapFromData
|
|
(dockapp.d, dockapp.win, master_xpm, &dockapp.pixmap,
|
|
&dockapp.mask, &attr) != XpmSuccess)
|
|
{
|
|
msg_err("not enough colors!");
|
|
exit(-1);
|
|
}
|
|
XShapeCombineMask(dockapp.d, dockapp.win, ShapeBounding, 0, 0,
|
|
dockapp.mask, ShapeSet);
|
|
XShapeCombineMask(dockapp.d, dockapp.iconwin, ShapeBounding, 0, 0,
|
|
dockapp.mask, ShapeSet);
|
|
|
|
wmhints.initial_state = WithdrawnState;
|
|
wmhints.flags = StateHint;
|
|
wmhints.icon_window = dockapp.iconwin;
|
|
wmhints.icon_x = sizehints.x;
|
|
wmhints.icon_y = sizehints.y;
|
|
wmhints.window_group = dockapp.win;
|
|
wmhints.flags =
|
|
StateHint | IconWindowHint | IconPositionHint | WindowGroupHint;
|
|
XSetWMHints(dockapp.d, dockapp.win, &wmhints);
|
|
|
|
XSetCommand(dockapp.d, dockapp.win, argv, argc);
|
|
XSetCommand(dockapp.d, dockapp.iconwin, argv, argc);
|
|
|
|
XMapWindow(dockapp.d, dockapp.win);
|
|
dockapp.xfd = ConnectionNumber(dockapp.d);
|
|
}
|
|
|
|
|
|
/* support function - chop cr/lf off the end of a string */
|
|
void
|
|
chomp(char* buffer)
|
|
{
|
|
int i = strlen(buffer) - 1;
|
|
while (buffer[i] == '\n' || buffer[i] == '\r')
|
|
buffer[i--] = '\0';
|
|
}
|
|
|
|
|
|
void
|
|
conf_read(char* filename)
|
|
{
|
|
FILE *fp;
|
|
char buf[1024];
|
|
size_t pos;
|
|
int line = 0;
|
|
int exist;
|
|
|
|
fp = fopen(filename, "r");
|
|
|
|
if(fp)
|
|
{
|
|
/* actually read in the config file, skipping over short and #lines */
|
|
while (fgets(buf, 1024, fp)) {
|
|
line++;
|
|
if(buf[0] == '#' || (strlen(buf) < 2))
|
|
continue;
|
|
chomp(buf);
|
|
pos = strcspn(buf, "=");
|
|
if(pos < strlen(buf))
|
|
{
|
|
buf[pos++] = '\0';
|
|
|
|
/* check for existent value */
|
|
exist = defcon_lk(buf); /* search for buf in defaults */
|
|
if(exist != -1 && !pss_defcon[exist].used)
|
|
exist = -1;
|
|
if(exist == -1) /* only if isn't a default or it's not on cmdline */
|
|
assign(buf, buf+pos);
|
|
}
|
|
memset(buf, 0, 1024);
|
|
}
|
|
fclose(fp);
|
|
}
|
|
else
|
|
{
|
|
/* can't open the rc file, make a new one */
|
|
msg_err("can't open WMND rc file '%s', using defaults", filename);
|
|
conf_write(filename);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
conf_write(char* filename)
|
|
{
|
|
FILE *fp;
|
|
struct var *vp;
|
|
int dc;
|
|
|
|
fp = fopen(filename, "w");
|
|
if(!fp)
|
|
{
|
|
msg_err("can't open '%s' for writing", filename);
|
|
exit(1);
|
|
}
|
|
fprintf(fp, "# WMND configuration file (generated automatically)\n\n");
|
|
|
|
/* write only changed values */
|
|
for(vp = vars; vp; vp = vp->v_next)
|
|
{
|
|
if(!vp->v_value)
|
|
continue;
|
|
|
|
dc = defcon_lk(vp->v_name);
|
|
if(dc == -1 || !pss_defcon[dc].value ||
|
|
strcmp(pss_defcon[dc].value, vp->v_value))
|
|
fprintf(fp, "%s=%s\n", vp->v_name, vp->v_value);
|
|
}
|
|
fclose(fp);
|
|
}
|
|
|
|
|
|
void
|
|
assign(char* name, char* value)
|
|
{
|
|
struct var* vp;
|
|
struct var* newv;
|
|
|
|
/* search for existing values */
|
|
for(vp = vars; vp &&
|
|
(vp->v_next || !strcmp(name, vp->v_name)); vp = vp->v_next)
|
|
{
|
|
if(!vp->v_next || !strcmp(name, vp->v_name))
|
|
{
|
|
/* existing value */
|
|
if(vp->v_value)
|
|
free(vp->v_value);
|
|
vp->v_value = vcopy(value);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* append the value */
|
|
newv = (struct var*)malloc(sizeof(struct var));
|
|
newv->v_name = vcopy(name);
|
|
newv->v_value = vcopy(value);
|
|
newv->v_next = NULL;
|
|
|
|
if(!vars)
|
|
vars = newv;
|
|
else
|
|
vp->v_next = newv;
|
|
}
|
|
|
|
|
|
char*
|
|
vcopy(char* str)
|
|
{
|
|
if(str == NULL)
|
|
return NULL;
|
|
|
|
return strdup(str);
|
|
}
|
|
|
|
|
|
int
|
|
strval_fe(const struct pair_strint *data, const char* val)
|
|
{
|
|
/* returns the correct value for val as int */
|
|
int cnt = 0;
|
|
while (data[cnt].strval)
|
|
{
|
|
if(!strcmp(val,data[cnt].strval))
|
|
return data[cnt].val;
|
|
cnt++;
|
|
}
|
|
|
|
/* not found, return 0 */
|
|
return data->val;
|
|
}
|
|
|
|
|
|
int
|
|
waveval_fe(const struct drwStruct *data, const char* val)
|
|
{
|
|
int cnt = 0;
|
|
while (data[cnt].funcName)
|
|
{
|
|
if(!strcmp(val,data[cnt].funcName))
|
|
return cnt;
|
|
cnt++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Lookup for an index in the defcon table */
|
|
int
|
|
defcon_lk(const char* token)
|
|
{
|
|
int rtval = 0;
|
|
while (pss_defcon[rtval].token)
|
|
{
|
|
if(!strcmp(token,pss_defcon[rtval].token))
|
|
return rtval;
|
|
rtval++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* Touch an item in the defcon table (mark as 'used') */
|
|
void
|
|
defcon_touch(char* token, char* val)
|
|
{
|
|
int idx = defcon_lk(token);
|
|
|
|
assign(token, val);
|
|
if(idx != -1)
|
|
pss_defcon[idx].used = 1;
|
|
}
|
|
|
|
|
|
struct var*
|
|
lookup(char* name)
|
|
{
|
|
struct var *vp;
|
|
|
|
for(vp = vars; vp; vp = vp->v_next)
|
|
if(!strcmp(name, vp->v_name))
|
|
return vp;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
char*
|
|
value(char* name)
|
|
{
|
|
struct var *vp;
|
|
|
|
if(!(vp = lookup(name)))
|
|
return NULL;
|
|
return vp->v_value;
|
|
}
|
|
|
|
|
|
void
|
|
beat_event(void)
|
|
{
|
|
unsigned long diff;
|
|
unsigned int min, hr;
|
|
char temp[16];
|
|
|
|
msg_dbg(__POSITION__, "activated");
|
|
if(!wmnd.curdev->online)
|
|
{
|
|
if(!bit_get(RUN_ONLINE))
|
|
{
|
|
bit_set(RUN_ONLINE);
|
|
led_control(LED_POWER, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(bit_get(RUN_ONLINE))
|
|
{
|
|
bit_off(RUN_ONLINE);
|
|
led_control(LED_POWER, 0);
|
|
}
|
|
}
|
|
if(bit_get(CFG_SHOWTIME) && wmnd.curdev->devstart)
|
|
{
|
|
diff = (long unsigned)(difftime(time(NULL), wmnd.curdev->devstart) / 60);
|
|
min = diff % 60;
|
|
hr = (diff / 60) % 100;
|
|
sprintf(temp, "%02d.%02d", hr, min);
|
|
draw_string(temp, 71, 36);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
exec_perc_command(const char* cmd, int button)
|
|
{
|
|
char buf[4];
|
|
char* newcmd;
|
|
perctbl_t data[3];
|
|
|
|
/* button */
|
|
sprintf(buf, "%d", button);
|
|
data[0].c = 'b';
|
|
data[0].value = buf;
|
|
|
|
/* interface name */
|
|
data[1].c = 'i';
|
|
data[1].value = wmnd.curdev->name;
|
|
|
|
/* status */
|
|
data[2].c = 's';
|
|
data[2].value = (wmnd.curdev->online? "0": "1");
|
|
|
|
/* parse and exec the new command */
|
|
newcmd = percsubst(cmd, data, 3);
|
|
exec_command(newcmd);
|
|
free(newcmd);
|
|
}
|
|
|
|
|
|
#ifdef USE_TREND
|
|
/* static data for trend instance sharing */
|
|
static FILE** trendFd;
|
|
static int trendUpd;
|
|
|
|
|
|
static inline int
|
|
trend_idx(int d, int bp)
|
|
{
|
|
return(d * 2 + bp);
|
|
}
|
|
|
|
|
|
void
|
|
close_trend(int i)
|
|
{
|
|
if(trendFd[i])
|
|
{
|
|
pclose(trendFd[i]);
|
|
trendFd[i] = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
close_trends()
|
|
{
|
|
unsigned i;
|
|
for(i = 0; i != wmnd.nr_devices * 2; ++i)
|
|
close_trend(i);
|
|
free(trendFd);
|
|
}
|
|
|
|
|
|
int
|
|
check_trend(int i)
|
|
{
|
|
return(fputs("\n", trendFd[i]) == EOF || fflush(trendFd[i]) == EOF);
|
|
}
|
|
|
|
|
|
void
|
|
exec_trend(struct Devices* dev, int bp)
|
|
{
|
|
int d = dev->devnum;
|
|
int i = trend_idx(d, bp);
|
|
int dead = (!trendFd[i] || check_trend(i));
|
|
|
|
if(dead)
|
|
{
|
|
/* new trend instance required */
|
|
char cmd[256];
|
|
|
|
/* cmdline */
|
|
snprintf(cmd, sizeof(cmd),
|
|
"trend -t '%s %s' -G %d -d -s -c2a -Lin,out %s - %s",
|
|
dev->name, (bp? "bytes": "packets"),
|
|
(wmnd.scale == binary_scale? 1024: 1000) / (bp? 1: 100),
|
|
value("trend_flags"), value("trend_history"));
|
|
|
|
/* execute */
|
|
close_trend(i);
|
|
if(!(trendFd[i] = popen(cmd, "w")))
|
|
{
|
|
msg_err("cannot execute trend!");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(dead || !trendUpd)
|
|
{
|
|
/* refill the pipe */
|
|
int x, idx;
|
|
idx = (bp? 0: 2);
|
|
for(x = 0; x != 58; ++x)
|
|
fprintf(trendFd[i], "%lu %lu\n",
|
|
dev->his[x][idx + 0], dev->his[x][idx + 1]);
|
|
fflush(trendFd[i]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
void
|
|
click_event(unsigned int region, unsigned int button)
|
|
{
|
|
char* action = NULL;
|
|
if(region == REG_NOREG) /* no region */
|
|
return;
|
|
|
|
msg_dbg(__POSITION__, "clicked btn %d in region %d", button, region);
|
|
|
|
/* the wheel is valid everywhere */
|
|
if(button == Button4 || button == Button5)
|
|
{
|
|
if(button == Button4)
|
|
devices_prev();
|
|
else
|
|
devices_select(NULL);
|
|
|
|
draw_interface();
|
|
}
|
|
else if(region == REG_DEV)
|
|
{
|
|
switch(button)
|
|
{
|
|
case Button1:
|
|
devices_select(NULL);
|
|
draw_interface();
|
|
break;
|
|
|
|
case Button3:
|
|
bit_tgl(CFG_SHORTNAME);
|
|
msg_dbg(__POSITION__, "shortname: %d", bit_get(CFG_SHORTNAME));
|
|
draw_interface();
|
|
break;
|
|
}
|
|
}
|
|
else if(region == REG_RT_PB)
|
|
{
|
|
if(button == Button1)
|
|
{
|
|
bit_tgl(CFG_MODE);
|
|
led_control(LED_POWER, bit_get(RUN_ONLINE));
|
|
}
|
|
}
|
|
else if(region == REG_MAIN)
|
|
{
|
|
if(button == Button1)
|
|
{
|
|
if(wmnd.wavemode < (wmnd.nWavemodes - 1))
|
|
wmnd.wavemode++;
|
|
else
|
|
wmnd.wavemode = 0;
|
|
msg_dbg(__POSITION__, "wavemode: %d", wmnd.wavemode);
|
|
}
|
|
else if(button == Button3)
|
|
/* switch time visualization */
|
|
bit_tgl(CFG_SHOWTIME);
|
|
}
|
|
else if(region == REG_SCALE_RX || region == REG_SCALE_TX)
|
|
{
|
|
switch(button)
|
|
{
|
|
case Button1:
|
|
/* switch max screen/history */
|
|
bit_tgl(CFG_MAXSCREEN);
|
|
break;
|
|
#ifdef USE_TREND
|
|
case Button2:
|
|
/* launch trend */
|
|
exec_trend(wmnd.curdev, bit_get(CFG_MODE));
|
|
break;
|
|
#endif
|
|
case Button3:
|
|
/* toggle max display */
|
|
bit_tgl(CFG_SHOWMAX);
|
|
break;
|
|
}
|
|
}
|
|
else if(region == REG_SCRIPT)
|
|
{
|
|
switch(button)
|
|
{
|
|
case Button1:
|
|
action = value("bt1_action");
|
|
break;
|
|
case Button2:
|
|
action = value("bt2_action");
|
|
break;
|
|
case Button3:
|
|
action = value("bt3_action");
|
|
break;
|
|
}
|
|
if(action)
|
|
exec_perc_command(action, button);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
smooth(unsigned long* stat, const unsigned long last, const float smooth)
|
|
{
|
|
*stat = ((unsigned long)(last + (smooth * (*stat - last))));
|
|
}
|
|
|
|
|
|
void
|
|
mainExit(int sig)
|
|
{
|
|
msg_info("caught signal %d, terminating...", sig);
|
|
#ifdef USE_TREND
|
|
close_trends();
|
|
#endif
|
|
devices_destroy();
|
|
exit(1);
|
|
}
|
|
|
|
|
|
int main(int argc, char* *argv)
|
|
{
|
|
char* dispname = NULL;
|
|
char* active_interface = NULL;
|
|
char* drv_interface = NULL;
|
|
char* drv_name = NULL;
|
|
char* win_name = NULL;
|
|
int parse_conf = 1;
|
|
char* conf_file = NULL;
|
|
struct Devices* ptr;
|
|
unsigned long ib, ob, ip, op;
|
|
int ch;
|
|
unsigned btn = 0;
|
|
int rgn = -1;
|
|
XEvent event;
|
|
const struct drwStruct* drwPtr;
|
|
sigset_t masked;
|
|
struct timeval beat_time;
|
|
|
|
/* initialize messaging functions */
|
|
msg_prgName = argv[0];
|
|
|
|
msg_dbg(__POSITION__, "wmnd start");
|
|
|
|
/* detect the number of avaible wave modes */
|
|
for(drwPtr = drwFuncs; drwPtr->funcName; ++drwPtr)
|
|
++wmnd.nWavemodes;
|
|
msg_dbg(__POSITION__, "detected %d display modes", wmnd.nWavemodes);
|
|
|
|
/*
|
|
* set default config options before option parsing so command line can
|
|
* overwrite config options
|
|
*/
|
|
wmnd.nr_devices = 0;
|
|
wmnd.flags = 0;
|
|
vars = NULL;
|
|
bit_set(RUN_ONLINE);
|
|
bit_set(CFG_MODE);
|
|
|
|
/*
|
|
* initalize default values using pss_defcon
|
|
* we can use ch, because it's used later
|
|
*/
|
|
ch = 0;
|
|
while(pss_defcon[ch].token)
|
|
{
|
|
assign(pss_defcon[ch].token, pss_defcon[ch].value);
|
|
ch++;
|
|
}
|
|
|
|
/* parse command line */
|
|
while((ch =
|
|
getopt(argc, argv, "bc:C:L:d:g:i:hlmMf:Fr:s:S:tvw:D:I:qQo:n:a:")) != EOF)
|
|
{
|
|
switch(ch)
|
|
{
|
|
case 'b':
|
|
defcon_touch("binary_scale", "yes");
|
|
break;
|
|
case 'c':
|
|
defcon_touch("tx_color", optarg);
|
|
break;
|
|
case 'C':
|
|
defcon_touch("rx_color", optarg);
|
|
break;
|
|
case 'L':
|
|
defcon_touch("md_color", optarg);
|
|
break;
|
|
case 'd':
|
|
defcon_touch("display", optarg);
|
|
break;
|
|
case 'g':
|
|
defcon_touch("geometry", optarg);
|
|
break;
|
|
case 'i':
|
|
defcon_touch("interface_name", optarg);
|
|
break;
|
|
case 'l':
|
|
defcon_touch("use_long_names", "yes");
|
|
break;
|
|
case 'm':
|
|
defcon_touch("show_max_values", "no");
|
|
break;
|
|
case 'M':
|
|
defcon_touch("use_max_history", "yes");
|
|
break;
|
|
case 'f':
|
|
conf_file = optarg;
|
|
break;
|
|
case 'F':
|
|
parse_conf = 0;
|
|
break;
|
|
case 'r':
|
|
defcon_touch("refresh", optarg);
|
|
break;
|
|
case 's':
|
|
defcon_touch("scroll", optarg);
|
|
break;
|
|
case 'S':
|
|
defcon_touch("avg_steps", optarg);
|
|
break;
|
|
case 't':
|
|
defcon_touch("display_time", "no");
|
|
break;
|
|
case 'v':
|
|
printversion();
|
|
exit(0);
|
|
break;
|
|
case 'w':
|
|
defcon_touch("wave_mode", optarg);
|
|
break;
|
|
case 'D':
|
|
defcon_touch("driver", optarg);
|
|
break;
|
|
case 'I':
|
|
defcon_touch("driver_interface", optarg);
|
|
break;
|
|
case 'q':
|
|
defcon_touch("quiet", "yes");
|
|
break;
|
|
case 'Q':
|
|
defcon_touch("quiet", "no");
|
|
break;
|
|
case 'o':
|
|
defcon_touch("smooth", optarg);
|
|
break;
|
|
case 'n':
|
|
defcon_touch("name", optarg);
|
|
break;
|
|
case 'a':
|
|
defcon_touch("fixed_max", optarg);
|
|
break;
|
|
default:
|
|
usage();
|
|
exit(0);
|
|
break;
|
|
}
|
|
}
|
|
if(parse_conf)
|
|
{
|
|
if(!conf_file)
|
|
{
|
|
char* tmp;
|
|
tmp = getenv("HOME");
|
|
conf_file = (char*)calloc(1, strlen(tmp) + 9);
|
|
strcat(conf_file, tmp);
|
|
strcat(conf_file, "/.wmndrc");
|
|
}
|
|
conf_read(conf_file);
|
|
}
|
|
|
|
/* set stuff here */
|
|
wmnd.refresh = atoi(value("refresh"));
|
|
wmnd.scroll = MAX(atoi(value("scroll")), 1);
|
|
wmnd.avgSteps = MAX(atoi(value("avg_steps")), 1);
|
|
wmnd.smooth = atof(value("smooth"));
|
|
wmnd.maxScale = strtol(value("fixed_max"), NULL, 0);
|
|
win_name = value("name");
|
|
if(strval_fe(psi_bool, value("binary_scale")))
|
|
wmnd.scale = binary_scale;
|
|
else
|
|
wmnd.scale = metric_scale;
|
|
active_interface = value("interface_name");
|
|
if(!strcmp(active_interface, "%first"))
|
|
active_interface = NULL;
|
|
if(!strval_fe(psi_bool, value("use_long_names")))
|
|
bit_set(CFG_SHORTNAME);
|
|
if(strval_fe(psi_bool, value("show_max_values")))
|
|
bit_set(CFG_SHOWMAX);
|
|
if(!strval_fe(psi_bool, value("use_max_history")))
|
|
bit_set(CFG_MAXSCREEN);
|
|
if(strval_fe(psi_bool, value("display_time")))
|
|
bit_set(CFG_SHOWTIME);
|
|
wmnd.wavemode = waveval_fe(drwFuncs, value("wave_mode"));
|
|
drv_name = value("driver");
|
|
if(!strcmp(drv_name, "%auto"))
|
|
drv_name = NULL;
|
|
drv_interface = value("driver_interface");
|
|
if(!strcmp(drv_interface, "%any"))
|
|
drv_interface = NULL;
|
|
if(strval_fe(psi_bool, value("quiet")))
|
|
msg_messages = MSG_FERR;
|
|
if(strval_fe(psi_bool, value("debug")))
|
|
msg_messages |= MSG_FDBG;
|
|
|
|
/* check for at least one display mode */
|
|
if(!drwFuncs->funcName)
|
|
{
|
|
msg_err("no avaible display modes, exiting");
|
|
exit(1);
|
|
}
|
|
|
|
/* initialize drivers and devices */
|
|
if(!active_interface)
|
|
active_interface = drv_interface;
|
|
if(devices_init(drv_name, drv_interface))
|
|
{
|
|
msg_err("no drivers loaded, exiting");
|
|
exit(1);
|
|
}
|
|
|
|
/* now it's safe to connect signals */
|
|
signal(SIGINT, mainExit);
|
|
signal(SIGTERM, mainExit);
|
|
signal(SIGCHLD, reaper);
|
|
sigemptyset(&masked);
|
|
sigaddset(&masked, SIGTERM);
|
|
sigaddset(&masked, SIGINT);
|
|
|
|
#ifdef USE_TREND
|
|
/* initialize trend's data */
|
|
signal(SIGPIPE, SIG_IGN);
|
|
trendFd = (FILE**)calloc(sizeof(FILE*), wmnd.nr_devices * 2);
|
|
trendUpd = strval_fe(psi_bool, value("trend_update"));
|
|
#endif
|
|
|
|
msg_dbg(__POSITION__, "open X display");
|
|
dispname = value("display");
|
|
if(!(dockapp.d = XOpenDisplay(dispname)))
|
|
{
|
|
/* fprintf crashes on some systems if dispname is null */
|
|
if(dispname)
|
|
msg_err("unable to open display '%s'", dispname);
|
|
else
|
|
msg_err("unable to open default display");
|
|
|
|
exit(-1);
|
|
}
|
|
new_window(win_name, "wmnd", 64, 64, argc, argv);
|
|
|
|
add_mr(REG_DEV, 3, 3, 38, 9); /* device */
|
|
add_mr(REG_RT_PB, 54, 3, 7, 9); /* up/down packet/byte mode */
|
|
add_mr(REG_MAIN, 3, 22, 58, 31); /* main display area */
|
|
add_mr(REG_SCALE_RX, 3, 13, 29, 9); /* scale meter, left side (rx) */
|
|
add_mr(REG_SCALE_TX, 32, 13, 29, 9); /* scale meter, right side (tx) */
|
|
add_mr(REG_SCRIPT, 3, 54, 58, 7); /* user script */
|
|
|
|
/* updates should begin immediately */
|
|
memset(&beat_time, 0, sizeof(beat_time));
|
|
|
|
/* clear the number of remaining steps */
|
|
wmnd.avgRSteps = 1;
|
|
|
|
if(!active_interface) /* set default device name */
|
|
wmnd.curdev = devices;
|
|
else
|
|
devices_select(active_interface);
|
|
|
|
draw_interface();
|
|
|
|
msg_dbg(__POSITION__, "looping");
|
|
XSync(dockapp.d, False); /* kick off X11 queue */
|
|
|
|
/* loop forever */
|
|
for(;;)
|
|
{
|
|
unsigned int j;
|
|
sigset_t mask;
|
|
struct timeval beat_ctime;
|
|
unsigned long beat_gap;
|
|
int gap;
|
|
|
|
/* mask INT/TERM in get_stats */
|
|
sigprocmask(SIG_BLOCK, &masked, &mask);
|
|
|
|
/* get statistics for each existing device */
|
|
for(ptr = devices; ptr; ptr = ptr->next)
|
|
{
|
|
devices_getstat(ptr, &ip, &op, &ib, &ob);
|
|
|
|
/* check for device shutdown */
|
|
if(ib < ptr->ib_stat_last)
|
|
ptr->ib_stat_last = ib;
|
|
if(ob < ptr->ob_stat_last)
|
|
ptr->ob_stat_last = ob;
|
|
if(ip < ptr->ip_stat_last)
|
|
ptr->ip_stat_last = ip;
|
|
if(op < ptr->op_stat_last)
|
|
ptr->op_stat_last = op;
|
|
|
|
/*
|
|
* smoothing is performed before led setup and only on bytes, in order to
|
|
* let leds blink even when the graph is smoothed. Smoothing packets has
|
|
* no sense.
|
|
*/
|
|
if(wmnd.smooth)
|
|
{
|
|
smooth(&ib, ptr->ib_stat_last, wmnd.smooth);
|
|
smooth(&ob, ptr->ob_stat_last, wmnd.smooth);
|
|
}
|
|
|
|
/* setup leds */
|
|
if(ptr == wmnd.curdev)
|
|
{
|
|
if(ptr->ip_stat_last == ip || ptr->online)
|
|
led_control(LED_RX, 0);
|
|
else
|
|
led_control(LED_RX, 1);
|
|
|
|
if(ptr->op_stat_last == op || ptr->online)
|
|
led_control(LED_TX, 0);
|
|
else
|
|
led_control(LED_TX, 1);
|
|
}
|
|
|
|
/* save values in history */
|
|
ptr->his[57][0] += ib - ptr->ib_stat_last;
|
|
ptr->his[57][1] += ob - ptr->ob_stat_last;
|
|
ptr->his[57][2] += ip - ptr->ip_stat_last;
|
|
ptr->his[57][3] += op - ptr->op_stat_last;
|
|
ptr->ib_max_his = MAX(ptr->ib_max_his, ptr->his[57][0]);
|
|
ptr->ob_max_his = MAX(ptr->ob_max_his, ptr->his[57][1]);
|
|
ptr->ip_max_his = MAX(ptr->ip_max_his, ptr->his[57][2]);
|
|
ptr->op_max_his = MAX(ptr->op_max_his, ptr->his[57][3]);
|
|
|
|
/* save lastest values */
|
|
ptr->ib_stat_last = ib;
|
|
ptr->ob_stat_last = ob;
|
|
ptr->ip_stat_last = ip;
|
|
ptr->op_stat_last = op;
|
|
}
|
|
|
|
/* restore mask */
|
|
sigprocmask(SIG_SETMASK, &mask, NULL);
|
|
|
|
/* fetch current time */
|
|
gettimeofday(&beat_ctime, NULL);
|
|
|
|
/* estimate the time gap in milliseconds */
|
|
beat_gap = ((beat_ctime.tv_sec - beat_time.tv_sec) * 1000) +
|
|
((beat_ctime.tv_usec - beat_time.tv_usec) / 1000);
|
|
gap = (wmnd.avgSteps != 1? wmnd.scroll: beat_gap / 100);
|
|
|
|
if(beat_gap >= wmnd.scroll * 100)
|
|
{
|
|
beat_time = beat_ctime;
|
|
|
|
/* drift the average stats */
|
|
if(!--wmnd.avgRSteps)
|
|
{
|
|
float div = 1. / wmnd.avgSteps;
|
|
|
|
for(ptr = devices; ptr; ptr = ptr->next)
|
|
{
|
|
ptr->avg[0] = (unsigned long)(div * (ptr->ib_stat_last - ptr->avgBuf[0]));
|
|
ptr->avg[1] = (unsigned long)(div * (ptr->ob_stat_last - ptr->avgBuf[1]));
|
|
ptr->avg[2] = (unsigned long)(div * (ptr->ip_stat_last - ptr->avgBuf[2]));
|
|
ptr->avg[3] = (unsigned long)(div * (ptr->op_stat_last - ptr->avgBuf[3]));
|
|
ptr->avgBuf[0] = ptr->ib_stat_last;
|
|
ptr->avgBuf[1] = ptr->ob_stat_last;
|
|
ptr->avgBuf[2] = ptr->ip_stat_last;
|
|
ptr->avgBuf[3] = ptr->op_stat_last;
|
|
}
|
|
wmnd.avgRSteps = wmnd.avgSteps;
|
|
}
|
|
|
|
/* cause a beat_event to parse pending x requests */
|
|
beat_event();
|
|
|
|
/* scroll the statistics */
|
|
draw_stats(wmnd.curdev, gap);
|
|
for(ptr = devices; ptr; ptr = ptr->next)
|
|
{
|
|
#ifdef USE_TREND
|
|
if(trendUpd)
|
|
{
|
|
int i, bp, idx;
|
|
|
|
for(bp = 0; bp != 2; ++bp)
|
|
{
|
|
idx = (bp? 0: 2);
|
|
i = trend_idx(ptr->devnum, bp);
|
|
if(trendFd[i])
|
|
{
|
|
if(check_trend(i))
|
|
close_trend(i);
|
|
else
|
|
{
|
|
fprintf(trendFd[i], "%lu %lu\n",
|
|
ptr->his[57][idx + 0], ptr->his[57][idx + 1]);
|
|
fflush(trendFd[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for(j = 1; j < 58; j++)
|
|
{
|
|
ptr->his[j - 1][0] = ptr->his[j][0];
|
|
ptr->his[j - 1][1] = ptr->his[j][1];
|
|
ptr->his[j - 1][2] = ptr->his[j][2];
|
|
ptr->his[j - 1][3] = ptr->his[j][3];
|
|
}
|
|
memset(ptr->his[57], 0, sizeof(ptr->his[57]));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* beat_event and redraw added also on expose and button release
|
|
* events. Now the app is much responsive to user action
|
|
*/
|
|
while (XPending(dockapp.d))
|
|
{
|
|
msg_dbg(__POSITION__, "X11 activity");
|
|
XNextEvent(dockapp.d, &event);
|
|
switch (event.type)
|
|
{
|
|
case Expose:
|
|
dockapp.update = 1;
|
|
break;
|
|
case DestroyNotify:
|
|
XCloseDisplay(dockapp.d);
|
|
exit(0);
|
|
break;
|
|
case ButtonPress:
|
|
btn = event.xbutton.button;
|
|
rgn = check_mr(event.xbutton.x, event.xbutton.y);
|
|
break;
|
|
case ButtonRelease:
|
|
if(btn == event.xbutton.button)
|
|
{
|
|
if(rgn == check_mr(event.xbutton.x, event.xbutton.y))
|
|
{
|
|
click_event(rgn, btn);
|
|
beat_event();
|
|
draw_stats(wmnd.curdev, gap);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
redraw_window();
|
|
usleep(wmnd.refresh);
|
|
}
|
|
|
|
#ifdef USE_TREND
|
|
close_trends();
|
|
#endif
|
|
devices_destroy();
|
|
exit(0);
|
|
}
|
|
|
|
|
|
void
|
|
binary_scale(unsigned char sign, unsigned long value, char* buf)
|
|
{
|
|
unsigned char scale;
|
|
unsigned int i;
|
|
char* r;
|
|
|
|
if(value > 1073741823)
|
|
{
|
|
/* scale in giga */
|
|
value = value >> 30;
|
|
scale = 'G';
|
|
}
|
|
else
|
|
if(value > 1048575)
|
|
{
|
|
/* scale in mega */
|
|
value = value >> 20;
|
|
scale = 'M';
|
|
}
|
|
else
|
|
if(value > 1023)
|
|
{
|
|
/* scale in kilo */
|
|
value = value >> 10;
|
|
scale = 'K';
|
|
}
|
|
else
|
|
scale = ' ';
|
|
|
|
snprintf(buf, 7, "%c%lu", sign, value);
|
|
r = buf;
|
|
r++;
|
|
|
|
for(i = 3; i > 0 && *r != '\0'; i--)
|
|
{
|
|
if(*r == '+' || *r == '-' || *r == '.')
|
|
++i;
|
|
++r;
|
|
}
|
|
*r++ = scale;
|
|
*r = '\0';
|
|
}
|
|
|
|
|
|
void
|
|
metric_scale(unsigned char sign, unsigned long value, char* buf)
|
|
{
|
|
float f;
|
|
unsigned char scale;
|
|
unsigned int i;
|
|
char* r;
|
|
|
|
f = (float) value;
|
|
if(value > 999999999)
|
|
{
|
|
/* scale in giga */
|
|
f /= 1000000000;
|
|
scale = 'G';
|
|
}
|
|
else
|
|
if(value > 999999)
|
|
{
|
|
/* scale in mega */
|
|
f /= 1000000;
|
|
scale = 'M';
|
|
}
|
|
else
|
|
if(value > 999)
|
|
{
|
|
/* scale in kilo */
|
|
f /= 1000;
|
|
scale = 'K';
|
|
}
|
|
else
|
|
scale = ' ';
|
|
|
|
snprintf(buf, 7, "%c%f", sign, f);
|
|
r = buf;
|
|
r++;
|
|
|
|
for(i = 3; i > 0 && *r != '\0'; i--)
|
|
{
|
|
if(*r == '+' || *r == '-' || *r == '.')
|
|
++i;
|
|
++r;
|
|
}
|
|
*r++ = scale;
|
|
*r = '\0';
|
|
}
|
|
|
|
|
|
void
|
|
scale(char* rx_buf, char* tx_buf, unsigned long rx,
|
|
unsigned long tx, const int gap)
|
|
{
|
|
char rx_sign, tx_sign;
|
|
|
|
if(rx > tx)
|
|
{
|
|
rx_sign = '+';
|
|
tx_sign = '#';
|
|
}
|
|
else
|
|
{
|
|
rx_sign = '-';
|
|
tx_sign = '*';
|
|
}
|
|
|
|
/* return the speed in bps */
|
|
if(gap != 10)
|
|
{
|
|
float div = 10. / gap;
|
|
tx = (unsigned long)(div * tx);
|
|
rx = (unsigned long)(div * rx);
|
|
}
|
|
wmnd.scale(tx_sign, tx, tx_buf);
|
|
wmnd.scale(rx_sign, rx, rx_buf);
|
|
}
|
|
|
|
|
|
void
|
|
draw_string(const char* buf, unsigned int x, unsigned int y)
|
|
{
|
|
unsigned int w, sx = 0, sy = 0;
|
|
unsigned int draw;
|
|
const char* r;
|
|
|
|
w = 3;
|
|
draw = 0;
|
|
for(r = buf; *r != '\0'; r++)
|
|
{
|
|
if(*r >= '0' && *r <= '9')
|
|
{
|
|
w = 5;
|
|
sx = 1 + (w * (*r - '0'));
|
|
sy = 86;
|
|
draw = 1;
|
|
}
|
|
else
|
|
if(*r == '.')
|
|
{
|
|
w = 2;
|
|
sx = 62;
|
|
sy = 86;
|
|
draw = 1;
|
|
}
|
|
else
|
|
if(*r >= 'A' && *r <= 'Z')
|
|
{
|
|
w = 5;
|
|
sx = 1 + (w * (*r - 'A'));
|
|
sy = 94;
|
|
draw = 1;
|
|
}
|
|
else
|
|
if(*r == '+')
|
|
{
|
|
w = 6;
|
|
if(bit_get(CFG_MAXSCREEN))
|
|
{
|
|
sx = 133;
|
|
sy = 94;
|
|
}
|
|
else
|
|
{
|
|
sx = 66;
|
|
sy = 46;
|
|
}
|
|
draw = 1;
|
|
}
|
|
else
|
|
if(*r == '-')
|
|
{
|
|
w = 6;
|
|
if(bit_get(CFG_MAXSCREEN))
|
|
{
|
|
sx = 139;
|
|
sy = 94;
|
|
}
|
|
else
|
|
{
|
|
sx = 72;
|
|
sy = 46;
|
|
}
|
|
draw = 1;
|
|
}
|
|
else
|
|
if(*r == '*')
|
|
{
|
|
w = 6;
|
|
if(bit_get(CFG_MAXSCREEN))
|
|
{
|
|
sx = 145;
|
|
sy = 94;
|
|
}
|
|
else
|
|
{
|
|
sx = 78;
|
|
sy = 46;
|
|
}
|
|
draw = 1;
|
|
}
|
|
else
|
|
if(*r == '#')
|
|
{
|
|
w = 6;
|
|
if(bit_get(CFG_MAXSCREEN))
|
|
{
|
|
sx = 151;
|
|
sy = 94;
|
|
}
|
|
else
|
|
{
|
|
sx = 84;
|
|
sy = 46;
|
|
}
|
|
draw = 1;
|
|
}
|
|
if(draw)
|
|
{
|
|
copy_xpm_area(sx, sy, w, 7, x, y);
|
|
draw = 0;
|
|
}
|
|
x += w;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
draw_rate(unsigned long rx, unsigned long tx, const int gap)
|
|
{
|
|
char rx_buf[8];
|
|
char tx_buf[8];
|
|
|
|
/* clear rate bar */
|
|
copy_xpm_area(100, 86, 58, 7, 3, 54);
|
|
|
|
/* put rx/tx numbers into strings, scaling them */
|
|
scale(rx_buf, tx_buf, rx, tx, gap);
|
|
|
|
/* draw rx/tx strings */
|
|
draw_string(rx_buf, 3, 54);
|
|
draw_string(tx_buf, 32, 54);
|
|
}
|
|
|
|
|
|
void
|
|
draw_max(unsigned long rx, unsigned long tx)
|
|
{
|
|
char rx_buf[16];
|
|
char tx_buf[16];
|
|
|
|
/* clear rate bar */
|
|
copy_xpm_area(100, 86, 58, 7, 3, 11);
|
|
|
|
/* put rx/tx numbers into strings, scaling them. Scale now acceps the median
|
|
* time in microseconds to scale the values correctly; as we don't have the
|
|
* median time for all samples, use a reasonable default */
|
|
scale(rx_buf, tx_buf, rx, tx, wmnd.scroll);
|
|
|
|
/* draw rx/tx strings */
|
|
draw_string(rx_buf, 3, 11);
|
|
draw_string(tx_buf, 32, 11);
|
|
}
|
|
|
|
|
|
void
|
|
draw_stats(struct Devices *ptr, const int gap)
|
|
{
|
|
unsigned int k;
|
|
unsigned long* p;
|
|
unsigned int in, out;
|
|
unsigned long rx_max_his, tx_max_his;
|
|
unsigned long long rx_max, tx_max;
|
|
unsigned int size;
|
|
|
|
if(bit_get(CFG_SHOWMAX))
|
|
size = 35; /* with max bar mode */
|
|
else
|
|
size = 41; /* without max bar */
|
|
|
|
if(bit_get(CFG_MODE))
|
|
{
|
|
/* bytes mode */
|
|
in = 0;
|
|
out = 1;
|
|
rx_max_his = ptr->ib_max_his;
|
|
tx_max_his = ptr->ob_max_his;
|
|
}
|
|
else
|
|
{
|
|
/* packets mode */
|
|
in = 2;
|
|
out = 3;
|
|
rx_max_his = ptr->ip_max_his;
|
|
tx_max_his = ptr->op_max_his;
|
|
}
|
|
|
|
/* find maximum value in screen history */
|
|
rx_max = tx_max = 0;
|
|
p = (unsigned long*)ptr->his;
|
|
for(k = 0; k < 58; k++)
|
|
{
|
|
rx_max = MAX(rx_max, p[in]);
|
|
tx_max = MAX(tx_max, p[out]);
|
|
p += 4;
|
|
}
|
|
|
|
/* draw rx/tx rate */
|
|
p = ptr->avg;
|
|
draw_rate(p[in], p[out], gap);
|
|
|
|
if(bit_get(CFG_MAXSCREEN))
|
|
draw_max(rx_max, tx_max);
|
|
else
|
|
draw_max(rx_max_his, tx_max_his);
|
|
|
|
p = (unsigned long*)ptr->his;
|
|
(*drwFuncs[wmnd.wavemode].funcPtr)(p, in, out, size, rx_max, tx_max);
|
|
|
|
/* copy connection time over the graph */
|
|
if(bit_get(CFG_SHOWTIME) && wmnd.curdev->devstart)
|
|
copy_xpm_area(70, 36, 23, 7, 37, 46);
|
|
}
|
|
|
|
|
|
static void
|
|
led_control(const unsigned char led, const unsigned char mode)
|
|
{
|
|
msg_dbg(__POSITION__, "led: %02x[%02x]", led, mode);
|
|
switch (led)
|
|
{
|
|
case LED_POWER:
|
|
switch (bit_get(CFG_MODE))
|
|
{
|
|
case 1:
|
|
/* bytes */
|
|
if(mode)
|
|
/* on-line */
|
|
copy_xpm_area(116, 65, 5, 7, 55, 4)
|
|
else
|
|
/* off-line */
|
|
copy_xpm_area(122, 65, 5, 7, 55, 4);
|
|
break;
|
|
case 0:
|
|
/* packets */
|
|
if(mode)
|
|
/* on-line */
|
|
copy_xpm_area(128, 65, 5, 7, 55, 4)
|
|
else
|
|
/* off-line */
|
|
copy_xpm_area(134, 65, 5, 7, 55, 4);
|
|
break;
|
|
}
|
|
break;
|
|
case LED_RX:
|
|
if(mode)
|
|
{
|
|
/* turn on */
|
|
if(bit_get(LED_RX))
|
|
{
|
|
msg_dbg(__POSITION__, "RX led already on");
|
|
return;
|
|
}
|
|
copy_xpm_area(86, 70, 5, 4, 41, 4);
|
|
bit_set(LED_RX);
|
|
msg_dbg(__POSITION__, "RX led on");
|
|
}
|
|
else
|
|
{
|
|
/* turn off */
|
|
if(!bit_get(LED_RX))
|
|
{
|
|
msg_dbg(__POSITION__, "RX led already off");
|
|
return;
|
|
}
|
|
copy_xpm_area(92, 70, 5, 4, 41, 4);
|
|
bit_off(LED_RX);
|
|
msg_dbg(__POSITION__, "RX led off");
|
|
}
|
|
break;
|
|
case LED_TX:
|
|
if(mode)
|
|
{
|
|
/* turn on */
|
|
if(bit_get(LED_TX))
|
|
{
|
|
msg_dbg(__POSITION__, "TX led already on");
|
|
return;
|
|
}
|
|
copy_xpm_area(86, 65, 5, 4, 48, 4);
|
|
bit_set(LED_TX);
|
|
msg_dbg(__POSITION__, "TX led on");
|
|
}
|
|
else
|
|
{
|
|
/* turn off */
|
|
if(!bit_get(LED_TX))
|
|
{
|
|
msg_dbg(__POSITION__, "TX led already off");
|
|
return;
|
|
}
|
|
copy_xpm_area(92, 65, 5, 4, 48, 4);
|
|
bit_off(LED_TX);
|
|
msg_dbg(__POSITION__, "TX led off");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
draw_interface(void)
|
|
{
|
|
int i;
|
|
int c;
|
|
int k = 3;
|
|
char temp[7];
|
|
char* cur_name = wmnd.curdev->name;
|
|
int cur_namelen = strlen(cur_name);
|
|
|
|
/* refresh */
|
|
copy_xpm_area(65, 54, 38, 9, 3, 3);
|
|
led_control(LED_POWER, bit_get(RUN_ONLINE));
|
|
|
|
/*
|
|
* little modify to handle names shorter than 4 chars and minor bugfix
|
|
* for names shorter than 6 chars (memory overwrite)
|
|
*/
|
|
if(bit_get(CFG_SHORTNAME))
|
|
{
|
|
strncpy(temp, cur_name, (cur_namelen<4)?cur_namelen:3);
|
|
temp[3] = (cur_namelen < 4)? '\0': cur_name[cur_namelen - 1];
|
|
temp[4] = '\0';
|
|
}
|
|
else
|
|
{
|
|
if(cur_namelen > 6)
|
|
cur_namelen = 6;
|
|
strncpy(temp, cur_name, cur_namelen);
|
|
temp[cur_namelen] = '\0';
|
|
}
|
|
|
|
for(i = 0; temp[i]; i++)
|
|
{
|
|
c = temp[i];
|
|
if(c >= '0' && c <= '9') {
|
|
c -= '0';
|
|
copy_xpm_area(c * 6, 65, 6, 9, k, 3);
|
|
k += 6;
|
|
} else {
|
|
if(c >= 'a' && c <= 'z')
|
|
c = c - 'a' + 'A';
|
|
c -= 'A';
|
|
copy_xpm_area(c * 6, 75, 6, 9, k, 3);
|
|
k += 6;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
reaper(int sig)
|
|
{
|
|
int dummy;
|
|
wait(&dummy);
|
|
}
|
|
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
int cnt = 0;
|
|
|
|
fprintf(stderr,
|
|
"wmnd - WindowMaker Network Devices %s\n\n"
|
|
"usage:\n"
|
|
" -b base 2 scale (no fractions)\n"
|
|
" -c <color> tx color\n"
|
|
" -C <color> rx color\n"
|
|
" -L <color> middle line color\n"
|
|
" -d <display name>\n"
|
|
" -g <geometry>\n"
|
|
" -h this help screen\n"
|
|
" -i <interface name> select this interface at startup\n"
|
|
" -l start using long device names\n"
|
|
" -m start with max values hidden\n"
|
|
" -M use max values from the entire history\n"
|
|
" -f <config> read config instead of ~/.wmndrc\n"
|
|
" -F don't parse ~/.wmndrc\n"
|
|
" -r <rate> refresh rate in microseconds\n"
|
|
" -s <scroll> scroll rate in tenth of seconds\n"
|
|
" -S <samples> number of samples to average for the speed indicator\n"
|
|
" -t start without displaying time\n"
|
|
" -n <name> Use <name> instead of 'wmnd' for the window name\n"
|
|
" -v print the version number\n"
|
|
" -q be less verbose\n"
|
|
" -Q enable verbosity\n"
|
|
" -o <float> smoothing factor (0-1)\n"
|
|
" -a <bps> fixed max scale at <bps>\n"
|
|
" -w <mode> select display mode (see below)\n"
|
|
" -D <driver> specify a driver to use\n"
|
|
" -I <interface name> tell to driver/s the interface to monitor\n\n"
|
|
"builtin drivers: ", WMND_VERSION);
|
|
|
|
/* display builtin drivers */
|
|
while(drivers_table[cnt].driver_name)
|
|
{
|
|
if(drivers_table[cnt+1].driver_name)
|
|
fprintf(stderr, "%s, ", drivers_table[cnt].driver_name);
|
|
else
|
|
fprintf(stderr, "%s.\n", drivers_table[cnt].driver_name);
|
|
cnt++;
|
|
}
|
|
|
|
/* display builtin display modes */
|
|
fprintf(stderr, "builtin display modes: ");
|
|
cnt = 0;
|
|
while(drwFuncs[cnt].funcName)
|
|
{
|
|
if(drwFuncs[cnt + 1].funcName)
|
|
fprintf(stderr,"%s, ",drwFuncs[cnt].funcName);
|
|
else
|
|
fprintf(stderr,"%s.\n",drwFuncs[cnt].funcName);
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
printversion(void)
|
|
{
|
|
printf("%s\n", WMND_VERSION);
|
|
}
|
|
|
|
|
|
int
|
|
devices_init(const char* driver, const char* interface)
|
|
{
|
|
/*
|
|
* devices init, initializes all drivers/devices
|
|
* return: 0 on success, 1 on error
|
|
*/
|
|
|
|
int cnt = -1;
|
|
int devnum;
|
|
int in_loop0;
|
|
struct Devices *prt;
|
|
struct Devices *aftPrt;
|
|
struct Devices *tmpPrt;
|
|
wmnd.nr_devices = 0;
|
|
|
|
/* creating and empy first device */
|
|
msg_dbg(__POSITION__, "initializing devices");
|
|
|
|
devices = (struct Devices*)malloc(sizeof(struct Devices));
|
|
devices->next = NULL;
|
|
prt = devices;
|
|
|
|
/* starting all drivers */
|
|
do
|
|
{
|
|
cnt++;
|
|
|
|
/* check if driver is specified and check devicename */
|
|
if(driver && strcmp(driver, drivers_table[cnt].driver_name))
|
|
continue;
|
|
|
|
/* device matches or driver is NULL, then check if driver is avaible */
|
|
msg_dbg(__POSITION__, "probing %s", drivers_table[cnt].driver_name);
|
|
aftPrt = prt;
|
|
devnum = (*drivers_table[cnt].list_devices)(interface, prt);
|
|
drivers_table[cnt].status = devnum;
|
|
if(!devnum)
|
|
continue;
|
|
|
|
/* driver is avaible, set up all driver's devices */
|
|
for(in_loop0 = 0; in_loop0 < devnum; in_loop0++)
|
|
{
|
|
prt = prt->next;
|
|
prt->devnum = wmnd.nr_devices + in_loop0;
|
|
prt->drvnum = cnt; /* set the driver number */
|
|
if(drivers_table[cnt].init_device &&
|
|
(*drivers_table[cnt].init_device)(prt) == 1) /* init the device */
|
|
{
|
|
msg_err("failed to initialize device %s,%d",
|
|
drivers_table[cnt].driver_name, in_loop0);
|
|
devnum--;
|
|
|
|
/* last element in list */
|
|
if(in_loop0 == devnum-1 || !devnum)
|
|
{
|
|
free(prt);
|
|
prt = aftPrt;
|
|
continue;
|
|
}
|
|
|
|
/* remove device from list */
|
|
tmpPrt = prt->next;
|
|
free(prt);
|
|
prt = tmpPrt;
|
|
|
|
if(in_loop0 == devnum) continue;
|
|
aftPrt = prt;
|
|
in_loop0--;
|
|
}
|
|
else
|
|
{
|
|
/* initialize statistics for this device */
|
|
int cnt;
|
|
unsigned long int ib, ob, ip, op;
|
|
|
|
/* sample some stats to inizialize cleanly the graph */
|
|
(*drivers_table[prt->drvnum].get_stats)(prt, &ip, &op, &ib, &ob);
|
|
|
|
prt->ib_max_his = prt->ob_max_his =
|
|
prt->ip_max_his = prt->op_max_his = 0;
|
|
prt->ib_stat_last = ib;
|
|
prt->ob_stat_last = ob;
|
|
prt->ip_stat_last = ip;
|
|
prt->op_stat_last = op;
|
|
|
|
/* cleaning history */
|
|
for(cnt = 0; cnt < 58; cnt++)
|
|
memset(prt->his, 0, sizeof(prt->his));
|
|
|
|
/* clean average sampling buffers */
|
|
prt->avgBuf[0] = ib;
|
|
prt->avgBuf[1] = ob;
|
|
prt->avgBuf[2] = ip;
|
|
prt->avgBuf[3] = op;
|
|
}
|
|
}
|
|
|
|
/* all new devices are ok */
|
|
wmnd.nr_devices += devnum;
|
|
}
|
|
while(drivers_table[cnt+1].driver_name);
|
|
|
|
/* remove the first null element */
|
|
prt = devices;
|
|
devices = prt->next;
|
|
free(prt);
|
|
|
|
return (!wmnd.nr_devices);
|
|
}
|
|
|
|
|
|
void
|
|
devices_prev(void)
|
|
{
|
|
/*
|
|
* devices_prev: cycles for the previous device.
|
|
*/
|
|
|
|
struct Devices* tmp = devices;
|
|
|
|
while(tmp->next && (wmnd.curdev == devices || tmp->next != wmnd.curdev))
|
|
tmp = tmp->next;
|
|
|
|
wmnd.curdev = tmp;
|
|
}
|
|
|
|
|
|
void
|
|
devices_select(const char* interface)
|
|
{
|
|
/*
|
|
* devices_select: sets wmnd.curdev looking up a name
|
|
* if name is null, cycles for next device
|
|
*/
|
|
|
|
struct Devices* prt = devices;
|
|
|
|
if(interface)
|
|
{
|
|
/* searching for a device */
|
|
wmnd.curdev = devices;
|
|
while(prt)
|
|
{
|
|
if(!strcmp(interface, prt->name))
|
|
{
|
|
/* name matches */
|
|
wmnd.curdev = prt;
|
|
break;
|
|
}
|
|
prt = prt->next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* switching active device */
|
|
if(!wmnd.curdev->next)
|
|
wmnd.curdev = devices; /* last element, back to first */
|
|
else
|
|
wmnd.curdev = wmnd.curdev->next;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
devices_getstat(struct Devices *device, unsigned long* ip, unsigned long* op,
|
|
unsigned long* ib, unsigned long* ob)
|
|
{
|
|
/*
|
|
* devices_getstat: run appropriate get_stats for device
|
|
*/
|
|
|
|
int online =
|
|
(*drivers_table[device->drvnum].get_stats)(device, ip, op, ib, ob);
|
|
if(!online && device->online)
|
|
{
|
|
/* the device retuned back online, update the timestamp */
|
|
time(&device->devstart);
|
|
}
|
|
else
|
|
if(online && !device->online)
|
|
{
|
|
/* device shutdown */
|
|
device->devstart = 0;
|
|
}
|
|
|
|
device->online = online;
|
|
}
|
|
|
|
|
|
void
|
|
devices_destroy(void)
|
|
{
|
|
/*
|
|
* devices_destroy: sends destroy signal to all devices and frees devices
|
|
*/
|
|
|
|
struct Devices *ptr;
|
|
int cnt;
|
|
|
|
while(devices->next)
|
|
{
|
|
ptr = devices;
|
|
devices = devices->next;
|
|
if(drivers_table[ptr->drvnum].terminate_device)
|
|
(*drivers_table[ptr->drvnum].terminate_device)(ptr);
|
|
free(ptr->name);
|
|
free(ptr);
|
|
}
|
|
|
|
for(cnt = 0; drivers_table[cnt].driver_name; ++cnt)
|
|
{
|
|
if(drivers_table[cnt].status && drivers_table[cnt].terminate_driver)
|
|
(*drivers_table[cnt].terminate_driver)();
|
|
}
|
|
}
|