dockapps/wmsupermon/wmsupermon.c
2014-10-05 19:18:49 +01:00

503 lines
15 KiB
C

/* Copyright (C) 2006 Sergei Golubchik, Nicolas Chauvat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
/*
originally based on
WMgMon - Window Maker Generic Monitor
by Nicolas Chauvat <nico@caesium.fr>
which was based on
WMMon by Antoine Nulle and Martijn
*/
#define Wmsupermon_VERSION "1.2.2"
#define Wmsupermon_VERSION_DATE "2007/06/23"
#include <unistd.h>
#include <time.h>
#include <ctype.h>
#include <regex.h>
#include <assert.h>
#include "dockapp.h"
#include "wmsupermon-master.xpm"
#include "expr.h"
#include "stat_dev.h"
#include "panes.h"
/* define layout constants */
#define ROW_HEIGHT 14
#define LABEL_WIDTH 25
/******************************************************************************/
/* master xpm coordinates */
/******************************************************************************/
#define X_ALPHA 1
#define Y_ALPHA 1
#define X_NUM 1
#define Y_NUM 11
#define X_LABEL 1
#define Y_LABEL 21
#define W_LABEL 56
#define H_LABEL (ROW_HEIGHT-1)
#define X_NOLABEL 1
#define Y_NOLABEL 77
#define W_NOLABEL W_LABEL
#define H_NOLABEL H_LABEL
#define X_DIAG 1
#define Y_DIAG 34
#define W_DIAG W_LABEL
#define H_SDIAG (ROW_HEIGHT*1)
#define H_MDIAG (ROW_HEIGHT*2)
#define H_DIAG (ROW_HEIGHT*3)
#define H_BDIAG (ROW_HEIGHT*4)
#define X_BLANK_SBAR (118-2)
#define Y_BLANK_SBAR (127-50)
#define X_SBAR (118-2)
#define Y_SBAR (138-50)
#define W_SBAR 30
#define H_SBAR 10
#define X_BLANK_LBAR (62-2)
#define Y_BLANK_LBAR (127-50)
#define X_LBAR (62-2)
#define Y_LBAR (138-50)
#define W_LBAR 54
#define H_LBAR H_SBAR
#define X_DOT 150
#define COLOR_DOT 27
#define BRIGHT_DOT 28
#define BACK_DOT 29
#define LINE_DOT 30
/******************************************************************************/
/* global variables */
/******************************************************************************/
Pixmap work_pixmap, master_pixmap;
GC gc;
char *displayName="", *config=0;
static DAProgramOption options[]={
{"-d", "--display", "display to use", DOString, False, {&displayName} },
{"-c", "--config", "path to config file", DOString, False, {&config} }
};
#define WIND_MAX 4
wind_desc winds[WIND_MAX];
/* define panes */
#define PANE_MAX (8*WIND_MAX)
pane_desc pane[PANE_MAX];
int rotate_interval=4;
int next_rotate=0;
int current_time=0;
int wind_num=0;
#define STAT_DEV_MAX 20
stat_dev stat_device[STAT_DEV_MAX];
#define APP_WIDTH 56
#define APP_HEIGHT 56
/******************************************************************************/
/* prototypes */
/******************************************************************************/
void draw_label(const char *, int, int);
void draw_bar(pane_part *, int, int);
void draw_graph(pane_part *, int, int, int, int);
void draw_current_frame(pane_part *);
void draw_current_pane(pane_part *);
static void click(Window w, int button, int state, int x, int y);
char mask_bits[APP_HEIGHT*APP_WIDTH];
/******************************************************************************/
/* main */
/******************************************************************************/
int main (int argc, char *argv[])
{
unsigned w, h;
int i, device_num=0, pane_num=0;
DACallbacks callbacks={ NULL, &click, NULL, NULL, NULL, NULL, NULL };
DAParseArguments(argc, argv, options,
sizeof(options)/sizeof(DAProgramOption),
"wmsupermon", Wmsupermon_VERSION);
/* init structures */
for (i=0; i<STAT_DEV_MAX; i++)
stat_dev_init(&stat_device[i]);
/* read config file and populate structures */
i=read_config_file(pane, &pane_num, PANE_MAX,
stat_device, &device_num, STAT_DEV_MAX,
winds, &wind_num, WIND_MAX);
if (i < 0) /* error in config file */
exit(1);
if (i > 0) /* no config file */
exit(2); /* TODO: setup default panes */
/* init timing stuff */
next_rotate=time(0) + rotate_interval;
for (i=0; i<device_num; i++)
stat_dev_initstat(&stat_device[i]);
for (i=0; i < wind_num; i++) {
DAInitialize(displayName, winds[i].name, APP_WIDTH, APP_HEIGHT, argc, argv, winds[i].w);
DASetCallbacks(winds[i].w, &callbacks);
winds[i].pixmap=work_pixmap=DAMakePixmap(winds[i].w);
DAMakePixmapFromData(winds[i].w, wmsupermon_master_xpm, &master_pixmap, 0, &w, &h);
DAShow(winds[i].w);
winds[i].cur_pane=0;
}
gc=DefaultGC(DADisplay, DefaultScreen(DADisplay));
XSetForeground(DADisplay, gc, BlackPixel(DADisplay, DefaultScreen(DADisplay)));
for (i=0; i < wind_num; i++) {
XFillRectangle(DADisplay, work_pixmap=winds[i].pixmap, gc, 0, 0, APP_WIDTH, APP_HEIGHT);
draw_current_frame(winds[i].panes[winds[i].cur_pane]);
winds[i].mask=XCreateBitmapFromData(DADisplay, winds[i].w[0], mask_bits, APP_WIDTH, APP_HEIGHT);
DASetShape(winds[i].w, winds[i].mask);
}
/* mainloop */
while (1) {
XEvent ev;
current_time=time(0);
/* change pane ? */
if (current_time > next_rotate) {
next_rotate=current_time+rotate_interval;
for (i=0; i < wind_num; i++) {
if (winds[i].num_panes > 1) {
winds[i].cur_pane++;
if (winds[i].cur_pane >= winds[i].num_panes)
winds[i].cur_pane=0;
/* redraw frame */
work_pixmap=winds[i].pixmap;
draw_current_frame(winds[i].panes[winds[i].cur_pane]);
XFreePixmap(DADisplay, winds[i].mask);
winds[i].mask=XCreateBitmapFromData(DADisplay, winds[i].w[0], mask_bits, APP_WIDTH, APP_HEIGHT);
DASetShape(winds[i].w, winds[i].mask);
}
}
}
for (i=0; i < device_num; i++) {
stat_dev *st=&stat_device[i];
/* if time has come, update stats */
if (current_time >= st->next_update) {
int j;
history **h=st->hist;
update_stat(st);
if (st->smooth) {
double v=st->smooth[0];
for (j=0; j < SMOOTH_SIZE-2; j++)
v+=(st->smooth[j]=st->smooth[j+1]);
v+=(st->smooth[j]=st->value[0]);
st->value[1]=v/SMOOTH_SIZE;
}
for (j=0; j <= P_HIST; j++, h++) {
if (!*h) continue;
(*h)->data[HIST_LAST]+= st->value[j & P_SMOOTH];
(*h)->count++;
}
st->next_update=current_time+st->update_interval;
}
/* if time has come, update history */
if (current_time > st->hist_next_update) {
stat_dev_update_history(st);
if (st->hist_next_update < current_time)
st->hist_next_update=current_time;
st->hist_next_update+=st->hist_update_interval;
}
}
/* update view */
for (i=0; i < wind_num; i++) {
work_pixmap=winds[i].pixmap;
draw_current_pane(winds[i].panes[winds[i].cur_pane]);
XCopyArea(DADisplay, work_pixmap, winds[i].w[0], gc, 0, 0,
APP_WIDTH, APP_HEIGHT, 0, 0);
}
/* handle all pending X events */
while (XPending(DADisplay)) {
XNextEvent(DADisplay, &ev);
for (i=0; i < wind_num; i++)
DAProcessEvent(winds[i].w, &ev);
}
/* short sleep */
usleep(250000L);
}
return 0;
}
/******************************************************************************/
/* mouse events / callbacks */
/******************************************************************************/
static void click(Window w, int button, int state, int x, int y)
{
pane_part *pane;
int p=y, i;
for (i=0; i < wind_num; i++) {
if (winds[i].w[0] != w) continue;
pane=winds[i].panes[winds[i].cur_pane];
for (p=0; pane[p].stat && p < PANE_PARTS; p++)
if ((y-= ROW_HEIGHT*pane[p].height) < 0) break;
if (y < 0 && pane[p].stat->action)
if (!fork()) {
system(pane[p].stat->action);
exit(0);
}
break;
}
}
/******************************************************************************/
/* draw functions */
/******************************************************************************/
void copy_area(int src_x, int src_y, int width, int height, int d_x, int d_y)
{
XCopyArea(DADisplay, master_pixmap, work_pixmap, gc,
src_x, src_y, width, height, d_x, d_y);
}
void draw_current_frame(pane_part *pane)
{
int i, j;
int row=0;
XSetForeground(DADisplay, gc, BlackPixel(DADisplay, DefaultScreen(DADisplay)));
XFillRectangle(DADisplay, work_pixmap, gc, 0, 0, APP_WIDTH, APP_HEIGHT);
for (j=0; j < sizeof(mask_bits); j++) mask_bits[j]=0xff;
for (i=0; (row<PANE_PARTS) && (i<PANE_MAX) && pane[i].stat; i++) {
switch(pane[i].height) {
case 1:
if (pane[i].flags & P_LABEL) {
copy_area (X_LABEL, Y_LABEL, W_LABEL, H_LABEL, 0, row*ROW_HEIGHT);
draw_label(pane[i].stat->name, 1, 2+row*ROW_HEIGHT);
for (j=0; j < ROW_HEIGHT; j++)
mask_bits[(row*ROW_HEIGHT+j)*W_LABEL/8+(LABEL_WIDTH-3)/8] &=
~(1 << (LABEL_WIDTH-3) % 8);
} else
copy_area (X_NOLABEL, Y_NOLABEL, W_NOLABEL, H_NOLABEL, 0, row*ROW_HEIGHT);
break;
case 2:
copy_area (X_DIAG, Y_DIAG, W_DIAG, H_MDIAG-5, 0, row*ROW_HEIGHT);
copy_area (X_DIAG, Y_DIAG+H_DIAG-H_MDIAG+5, W_DIAG, H_MDIAG-5, 0, row*ROW_HEIGHT+5);
break;
case 3:
copy_area (X_DIAG, Y_DIAG, W_DIAG, H_DIAG, 0, row*ROW_HEIGHT);
break;
case 4:
copy_area (X_DIAG, Y_DIAG, W_DIAG, H_DIAG-5, 0, row*ROW_HEIGHT);
copy_area (X_DIAG, Y_DIAG+5, W_DIAG, H_DIAG-5, 0, row*ROW_HEIGHT+H_BDIAG-H_DIAG+5);
break;
default:
assert(0);
}
row+=pane[i].height;
for (j=0; j < W_LABEL/8; j++) mask_bits[(row*ROW_HEIGHT-1)*W_LABEL/8+j]=0;
}
}
static struct { int w,h; } graph_box[]=
{
{0,0},
{W_DIAG-2, H_SDIAG-2},
{W_DIAG-2, H_MDIAG-2},
{W_DIAG-2, H_DIAG-2},
{W_DIAG-2, H_BDIAG-2}
};
void draw_current_pane(pane_part *pane)
{
int i;
int row=0, label_width;
char str[6]={0};
for (i=0; (row<PANE_PARTS) && pane->stat; pane++, i++) {
label_width=pane->flags & P_LABEL && pane->height == 1 ?
LABEL_WIDTH-1 : 0;
switch(pane->type) {
case PTBar:
draw_bar(pane, label_width+1, 1+row*ROW_HEIGHT);
break;
case PTPercent:
sprintf(str, " %*.*f%%", label_width ? 2 : 5, label_width ? 0 : 2,
100*pane->stat->value[pane->flags & P_SMOOTH]);
draw_label(str, label_width+4, 2+row*ROW_HEIGHT);
break;
case PTNumber:
sprintf(str,
pane->flags & P_FLOAT ? "%2$*1$.*1$g" : "%2$*1$.0f",
label_width ? 4 : 6, pane->stat->value[pane->flags & P_SMOOTH]);
draw_label(str, label_width+4, 2+row*ROW_HEIGHT);
break;
case PTGraph:
draw_graph(pane, label_width+1, 1+row*ROW_HEIGHT,
graph_box[pane->height].w-label_width,
graph_box[pane->height].h);
if (pane->flags & P_LABEL && pane->height > 1)
draw_label(pane->stat->name, 1, 1+row*ROW_HEIGHT);
break;
default:
assert(0);
break;
}
row+=pane->height;
}
}
void draw_label(const char *str, int x, int y)
{
int i, c;
for (i=0; str[i] != '\0'; i++) {
if (isalnum(str[i])) {
c=toupper(str[i]);
if (c >= 'A' && c <= 'Z') {
c -= 'A';
copy_area(X_ALPHA+c*6, Y_ALPHA, 6, 8, x+i*6, y);
}
else {
c -= '0';
copy_area(X_NUM+c*6, Y_NUM, 6, 8, x+i*6, y);
}
}
else {
switch(str[i]) {
case '/' :
copy_area(72-2, 61-50, 6, 8, x+i*6, y);
break;
case '.' :
copy_area(84-2, 61-50, 6, 8, x+i*6, y);
break;
case '%' :
copy_area(66-2, 61-50, 6, 8, x+i*6, y);
break;
case '-' :
copy_area(89-2, 61-50, 6, 8, x+i*6, y);
break;
default:
copy_area(78-2, 61-50, 6, 8, x+i*6, y);
}
}
}
}
static const struct {
int x_blank, y_blank, x, y, h, w;
} bar_coords[]={
{X_BLANK_SBAR, Y_BLANK_SBAR, X_SBAR, Y_SBAR, H_SBAR, W_SBAR},
{X_BLANK_LBAR, Y_BLANK_LBAR, X_LBAR, Y_LBAR, H_LBAR, W_LBAR}
};
void draw_bar (pane_part *widget, int x, int y)
{
stat_dev *st=widget->stat;
int value;
int label= x==1;
/* copy blank bar */
copy_area(bar_coords[label].x_blank, bar_coords[label].y_blank,
bar_coords[label].w, bar_coords[label].h, x, y);
/* compute value */
if (st->max)
value=(st->value[widget->flags & P_SMOOTH]-st->min)/st->max *
bar_coords[label].w;
else
value=st->value[widget->flags & P_SMOOTH] * bar_coords[label].w;
if (value > bar_coords[label].w)
value=bar_coords[label].w;
if (value < 0)
value=0;
/* copy part of color bar */
copy_area(bar_coords[label].x, bar_coords[label].y,
value, bar_coords[label].h, x, y);
}
/* Below: (0) - to disable, (st->max) to enable */
#define USER_RANGE (st->max)
void draw_graph (pane_part *widget, int x, int y, int width, int height)
{
stat_dev *st=widget->stat;
int row, column, top, x0, y0;
history *hist=st->hist[widget->flags & P_HIST];
x0=width - HIST_LAST + x;
y0=y + height - 1;
if (!USER_RANGE) {
/* compute scale */
if (widget->flags & P_SCALEDOWN) {
double hist_max=0;
for (column=HIST_LAST-width; column < HIST_LAST; column++) {
if (hist->data[column] > hist_max)
hist_max=hist->data[column];
}
if (hist_max > 0)
hist->max=hist_max;
}
else if (hist->data[HIST_LAST-1] > hist->max)
hist->max=hist->data[HIST_LAST-1];
}
/* draw bars (top is lighter) */
for (column=HIST_LAST-width; column < HIST_LAST; column++) {
if (USER_RANGE)
top=(hist->data[column] - st->min) * height / st->max;
else
top=hist->data[column] * height / hist->max;
for (row=0; row < height; row++) {
if (row < top - 3)
copy_area (X_DOT, COLOR_DOT, 1, 1, x0+column, y0-row);
else if (row < top)
copy_area (X_DOT, BRIGHT_DOT, 1, 1, x0+column, y0-row);
else
copy_area (X_DOT, BACK_DOT, 1, 1, x0+column, y0-row);
}
}
}
#undef USER_RANGE