wmbiff: Fix segfault when -display or -geometry argument is missing.
[dockapps.git] / wmsupermon / wmsupermon.c
blob3b6d30004d3fd12614baeb7ae59efa0b22f36b31
1 /* Copyright (C) 2006 Sergei Golubchik, Nicolas Chauvat
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License version 2
5 as published by the Free Software Foundation
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
17 originally based on
18 WMgMon - Window Maker Generic Monitor
19 by Nicolas Chauvat <nico@caesium.fr>
20 which was based on
21 WMMon by Antoine Nulle and Martijn
24 #define Wmsupermon_VERSION "1.2.2"
25 #define Wmsupermon_VERSION_DATE "2007/06/23"
27 #include <unistd.h>
28 #include <time.h>
29 #include <ctype.h>
30 #include <regex.h>
31 #include <assert.h>
33 #include "dockapp.h"
34 #include "wmsupermon-master.xpm"
35 #include "expr.h"
36 #include "stat_dev.h"
37 #include "panes.h"
39 /* define layout constants */
40 #define ROW_HEIGHT 14
41 #define LABEL_WIDTH 25
43 /******************************************************************************/
44 /* master xpm coordinates */
45 /******************************************************************************/
47 #define X_ALPHA 1
48 #define Y_ALPHA 1
49 #define X_NUM 1
50 #define Y_NUM 11
52 #define X_LABEL 1
53 #define Y_LABEL 21
54 #define W_LABEL 56
55 #define H_LABEL (ROW_HEIGHT-1)
57 #define X_NOLABEL 1
58 #define Y_NOLABEL 77
59 #define W_NOLABEL W_LABEL
60 #define H_NOLABEL H_LABEL
62 #define X_DIAG 1
63 #define Y_DIAG 34
64 #define W_DIAG W_LABEL
65 #define H_SDIAG (ROW_HEIGHT*1)
66 #define H_MDIAG (ROW_HEIGHT*2)
67 #define H_DIAG (ROW_HEIGHT*3)
68 #define H_BDIAG (ROW_HEIGHT*4)
70 #define X_BLANK_SBAR (118-2)
71 #define Y_BLANK_SBAR (127-50)
72 #define X_SBAR (118-2)
73 #define Y_SBAR (138-50)
74 #define W_SBAR 30
75 #define H_SBAR 10
76 #define X_BLANK_LBAR (62-2)
77 #define Y_BLANK_LBAR (127-50)
78 #define X_LBAR (62-2)
79 #define Y_LBAR (138-50)
80 #define W_LBAR 54
81 #define H_LBAR H_SBAR
83 #define X_DOT 150
84 #define COLOR_DOT 27
85 #define BRIGHT_DOT 28
86 #define BACK_DOT 29
87 #define LINE_DOT 30
89 /******************************************************************************/
90 /* global variables */
91 /******************************************************************************/
93 Pixmap work_pixmap, master_pixmap;
94 GC gc;
96 char *displayName="", *config=0;
98 static DAProgramOption options[]={
99 {"-d", "--display", "display to use", DOString, False, {&displayName} },
100 {"-c", "--config", "path to config file", DOString, False, {&config} }
103 #define WIND_MAX 4
104 wind_desc winds[WIND_MAX];
106 /* define panes */
107 #define PANE_MAX (8*WIND_MAX)
108 pane_desc pane[PANE_MAX];
110 int rotate_interval=4;
111 int next_rotate=0;
112 int current_time=0;
113 int wind_num=0;
115 #define STAT_DEV_MAX 20
116 stat_dev stat_device[STAT_DEV_MAX];
118 #define APP_WIDTH 56
119 #define APP_HEIGHT 56
121 /******************************************************************************/
122 /* prototypes */
123 /******************************************************************************/
125 void draw_label(const char *, int, int);
126 void draw_bar(pane_part *, int, int);
127 void draw_graph(pane_part *, int, int, int, int);
128 void draw_current_frame(pane_part *);
129 void draw_current_pane(pane_part *);
131 static void click(Window w, int button, int state, int x, int y);
133 char mask_bits[APP_HEIGHT*APP_WIDTH];
135 /******************************************************************************/
136 /* main */
137 /******************************************************************************/
139 int main (int argc, char *argv[])
141 unsigned w, h;
142 int i, device_num=0, pane_num=0;
143 DACallbacks callbacks={ NULL, &click, NULL, NULL, NULL, NULL, NULL };
145 DAParseArguments(argc, argv, options,
146 sizeof(options)/sizeof(DAProgramOption),
147 "wmsupermon", Wmsupermon_VERSION);
149 /* init structures */
150 for (i=0; i<STAT_DEV_MAX; i++)
151 stat_dev_init(&stat_device[i]);
153 /* read config file and populate structures */
154 i=read_config_file(pane, &pane_num, PANE_MAX,
155 stat_device, &device_num, STAT_DEV_MAX,
156 winds, &wind_num, WIND_MAX);
157 if (i < 0) /* error in config file */
158 exit(1);
159 if (i > 0) /* no config file */
160 exit(2); /* TODO: setup default panes */
162 /* init timing stuff */
163 next_rotate=time(0) + rotate_interval;
165 for (i=0; i<device_num; i++)
166 stat_dev_initstat(&stat_device[i]);
169 for (i=0; i < wind_num; i++) {
170 DAInitialize(displayName, winds[i].name, APP_WIDTH, APP_HEIGHT, argc, argv, winds[i].w);
171 DASetCallbacks(winds[i].w, &callbacks);
173 winds[i].pixmap=work_pixmap=DAMakePixmap(winds[i].w);
174 DAMakePixmapFromData(winds[i].w, wmsupermon_master_xpm, &master_pixmap, 0, &w, &h);
176 DAShow(winds[i].w);
177 winds[i].cur_pane=0;
180 gc=DefaultGC(DADisplay, DefaultScreen(DADisplay));
182 XSetForeground(DADisplay, gc, BlackPixel(DADisplay, DefaultScreen(DADisplay)));
184 for (i=0; i < wind_num; i++) {
185 XFillRectangle(DADisplay, work_pixmap=winds[i].pixmap, gc, 0, 0, APP_WIDTH, APP_HEIGHT);
186 draw_current_frame(winds[i].panes[winds[i].cur_pane]);
187 winds[i].mask=XCreateBitmapFromData(DADisplay, winds[i].w[0], mask_bits, APP_WIDTH, APP_HEIGHT);
188 DASetShape(winds[i].w, winds[i].mask);
191 /* mainloop */
192 while (1) {
193 XEvent ev;
195 current_time=time(0);
197 /* change pane ? */
198 if (current_time > next_rotate) {
199 next_rotate=current_time+rotate_interval;
200 for (i=0; i < wind_num; i++) {
201 if (winds[i].num_panes > 1) {
202 winds[i].cur_pane++;
203 if (winds[i].cur_pane >= winds[i].num_panes)
204 winds[i].cur_pane=0;
205 /* redraw frame */
206 work_pixmap=winds[i].pixmap;
207 draw_current_frame(winds[i].panes[winds[i].cur_pane]);
208 XFreePixmap(DADisplay, winds[i].mask);
209 winds[i].mask=XCreateBitmapFromData(DADisplay, winds[i].w[0], mask_bits, APP_WIDTH, APP_HEIGHT);
210 DASetShape(winds[i].w, winds[i].mask);
215 for (i=0; i < device_num; i++) {
216 stat_dev *st=&stat_device[i];
217 /* if time has come, update stats */
218 if (current_time >= st->next_update) {
219 int j;
220 history **h=st->hist;
221 update_stat(st);
222 if (st->smooth) {
223 double v=st->smooth[0];
224 for (j=0; j < SMOOTH_SIZE-2; j++)
225 v+=(st->smooth[j]=st->smooth[j+1]);
226 v+=(st->smooth[j]=st->value[0]);
227 st->value[1]=v/SMOOTH_SIZE;
229 for (j=0; j <= P_HIST; j++, h++) {
230 if (!*h) continue;
231 (*h)->data[HIST_LAST]+= st->value[j & P_SMOOTH];
232 (*h)->count++;
234 st->next_update=current_time+st->update_interval;
236 /* if time has come, update history */
237 if (current_time > st->hist_next_update) {
238 stat_dev_update_history(st);
239 if (st->hist_next_update < current_time)
240 st->hist_next_update=current_time;
241 st->hist_next_update+=st->hist_update_interval;
245 /* update view */
246 for (i=0; i < wind_num; i++) {
247 work_pixmap=winds[i].pixmap;
248 draw_current_pane(winds[i].panes[winds[i].cur_pane]);
249 XCopyArea(DADisplay, work_pixmap, winds[i].w[0], gc, 0, 0,
250 APP_WIDTH, APP_HEIGHT, 0, 0);
253 /* handle all pending X events */
254 while (XPending(DADisplay)) {
255 XNextEvent(DADisplay, &ev);
256 for (i=0; i < wind_num; i++)
257 DAProcessEvent(winds[i].w, &ev);
260 /* short sleep */
261 usleep(250000L);
265 return 0;
268 /******************************************************************************/
269 /* mouse events / callbacks */
270 /******************************************************************************/
272 static void click(Window w, int button, int state, int x, int y)
274 pane_part *pane;
275 int p=y, i;
276 for (i=0; i < wind_num; i++) {
277 if (winds[i].w[0] != w) continue;
278 pane=winds[i].panes[winds[i].cur_pane];
280 for (p=0; pane[p].stat && p < PANE_PARTS; p++)
281 if ((y-= ROW_HEIGHT*pane[p].height) < 0) break;
282 if (y < 0 && pane[p].stat->action)
283 if (!fork()) {
284 system(pane[p].stat->action);
285 exit(0);
287 break;
291 /******************************************************************************/
292 /* draw functions */
293 /******************************************************************************/
295 void copy_area(int src_x, int src_y, int width, int height, int d_x, int d_y)
297 XCopyArea(DADisplay, master_pixmap, work_pixmap, gc,
298 src_x, src_y, width, height, d_x, d_y);
301 void draw_current_frame(pane_part *pane)
303 int i, j;
304 int row=0;
306 XSetForeground(DADisplay, gc, BlackPixel(DADisplay, DefaultScreen(DADisplay)));
307 XFillRectangle(DADisplay, work_pixmap, gc, 0, 0, APP_WIDTH, APP_HEIGHT);
309 for (j=0; j < sizeof(mask_bits); j++) mask_bits[j]=0xff;
310 for (i=0; (row<PANE_PARTS) && (i<PANE_MAX) && pane[i].stat; i++) {
311 switch(pane[i].height) {
312 case 1:
313 if (pane[i].flags & P_LABEL) {
314 copy_area (X_LABEL, Y_LABEL, W_LABEL, H_LABEL, 0, row*ROW_HEIGHT);
315 draw_label(pane[i].stat->name, 1, 2+row*ROW_HEIGHT);
316 for (j=0; j < ROW_HEIGHT; j++)
317 mask_bits[(row*ROW_HEIGHT+j)*W_LABEL/8+(LABEL_WIDTH-3)/8] &=
318 ~(1 << (LABEL_WIDTH-3) % 8);
319 } else
320 copy_area (X_NOLABEL, Y_NOLABEL, W_NOLABEL, H_NOLABEL, 0, row*ROW_HEIGHT);
321 break;
322 case 2:
323 copy_area (X_DIAG, Y_DIAG, W_DIAG, H_MDIAG-5, 0, row*ROW_HEIGHT);
324 copy_area (X_DIAG, Y_DIAG+H_DIAG-H_MDIAG+5, W_DIAG, H_MDIAG-5, 0, row*ROW_HEIGHT+5);
325 break;
326 case 3:
327 copy_area (X_DIAG, Y_DIAG, W_DIAG, H_DIAG, 0, row*ROW_HEIGHT);
328 break;
329 case 4:
330 copy_area (X_DIAG, Y_DIAG, W_DIAG, H_DIAG-5, 0, row*ROW_HEIGHT);
331 copy_area (X_DIAG, Y_DIAG+5, W_DIAG, H_DIAG-5, 0, row*ROW_HEIGHT+H_BDIAG-H_DIAG+5);
332 break;
333 default:
334 assert(0);
336 row+=pane[i].height;
337 for (j=0; j < W_LABEL/8; j++) mask_bits[(row*ROW_HEIGHT-1)*W_LABEL/8+j]=0;
341 static struct { int w,h; } graph_box[]=
343 {0,0},
344 {W_DIAG-2, H_SDIAG-2},
345 {W_DIAG-2, H_MDIAG-2},
346 {W_DIAG-2, H_DIAG-2},
347 {W_DIAG-2, H_BDIAG-2}
350 void draw_current_pane(pane_part *pane)
352 int i;
353 int row=0, label_width;
354 char str[6]={0};
356 for (i=0; (row<PANE_PARTS) && pane->stat; pane++, i++) {
357 label_width=pane->flags & P_LABEL && pane->height == 1 ?
358 LABEL_WIDTH-1 : 0;
359 switch(pane->type) {
360 case PTBar:
361 draw_bar(pane, label_width+1, 1+row*ROW_HEIGHT);
362 break;
363 case PTPercent:
364 sprintf(str, " %*.*f%%", label_width ? 2 : 5, label_width ? 0 : 2,
365 100*pane->stat->value[pane->flags & P_SMOOTH]);
366 draw_label(str, label_width+4, 2+row*ROW_HEIGHT);
367 break;
368 case PTNumber:
369 sprintf(str,
370 pane->flags & P_FLOAT ? "%2$*1$.*1$g" : "%2$*1$.0f",
371 label_width ? 4 : 6, pane->stat->value[pane->flags & P_SMOOTH]);
372 draw_label(str, label_width+4, 2+row*ROW_HEIGHT);
373 break;
374 case PTGraph:
375 draw_graph(pane, label_width+1, 1+row*ROW_HEIGHT,
376 graph_box[pane->height].w-label_width,
377 graph_box[pane->height].h);
378 if (pane->flags & P_LABEL && pane->height > 1)
379 draw_label(pane->stat->name, 1, 1+row*ROW_HEIGHT);
380 break;
381 default:
382 assert(0);
383 break;
385 row+=pane->height;
389 void draw_label(const char *str, int x, int y)
391 int i, c;
393 for (i=0; str[i] != '\0'; i++) {
394 if (isalnum(str[i])) {
395 c=toupper(str[i]);
396 if (c >= 'A' && c <= 'Z') {
397 c -= 'A';
398 copy_area(X_ALPHA+c*6, Y_ALPHA, 6, 8, x+i*6, y);
400 else {
401 c -= '0';
402 copy_area(X_NUM+c*6, Y_NUM, 6, 8, x+i*6, y);
405 else {
406 switch(str[i]) {
407 case '/' :
408 copy_area(72-2, 61-50, 6, 8, x+i*6, y);
409 break;
410 case '.' :
411 copy_area(84-2, 61-50, 6, 8, x+i*6, y);
412 break;
413 case '%' :
414 copy_area(66-2, 61-50, 6, 8, x+i*6, y);
415 break;
416 case '-' :
417 copy_area(89-2, 61-50, 6, 8, x+i*6, y);
418 break;
419 default:
420 copy_area(78-2, 61-50, 6, 8, x+i*6, y);
426 static const struct {
427 int x_blank, y_blank, x, y, h, w;
428 } bar_coords[]={
429 {X_BLANK_SBAR, Y_BLANK_SBAR, X_SBAR, Y_SBAR, H_SBAR, W_SBAR},
430 {X_BLANK_LBAR, Y_BLANK_LBAR, X_LBAR, Y_LBAR, H_LBAR, W_LBAR}
433 void draw_bar (pane_part *widget, int x, int y)
435 stat_dev *st=widget->stat;
436 int value;
437 int label= x==1;
439 /* copy blank bar */
440 copy_area(bar_coords[label].x_blank, bar_coords[label].y_blank,
441 bar_coords[label].w, bar_coords[label].h, x, y);
443 /* compute value */
444 if (st->max)
445 value=(st->value[widget->flags & P_SMOOTH]-st->min)/st->max *
446 bar_coords[label].w;
447 else
448 value=st->value[widget->flags & P_SMOOTH] * bar_coords[label].w;
449 if (value > bar_coords[label].w)
450 value=bar_coords[label].w;
451 if (value < 0)
452 value=0;
454 /* copy part of color bar */
455 copy_area(bar_coords[label].x, bar_coords[label].y,
456 value, bar_coords[label].h, x, y);
459 /* Below: (0) - to disable, (st->max) to enable */
460 #define USER_RANGE (st->max)
461 void draw_graph (pane_part *widget, int x, int y, int width, int height)
463 stat_dev *st=widget->stat;
464 int row, column, top, x0, y0;
465 history *hist=st->hist[widget->flags & P_HIST];
467 x0=width - HIST_LAST + x;
468 y0=y + height - 1;
470 if (!USER_RANGE) {
471 /* compute scale */
472 if (widget->flags & P_SCALEDOWN) {
473 double hist_max=0;
474 for (column=HIST_LAST-width; column < HIST_LAST; column++) {
475 if (hist->data[column] > hist_max)
476 hist_max=hist->data[column];
478 if (hist_max > 0)
479 hist->max=hist_max;
481 else if (hist->data[HIST_LAST-1] > hist->max)
482 hist->max=hist->data[HIST_LAST-1];
484 /* draw bars (top is lighter) */
485 for (column=HIST_LAST-width; column < HIST_LAST; column++) {
486 if (USER_RANGE)
487 top=(hist->data[column] - st->min) * height / st->max;
488 else
489 top=hist->data[column] * height / hist->max;
491 for (row=0; row < height; row++) {
492 if (row < top - 3)
493 copy_area (X_DOT, COLOR_DOT, 1, 1, x0+column, y0-row);
494 else if (row < top)
495 copy_area (X_DOT, BRIGHT_DOT, 1, 1, x0+column, y0-row);
496 else
497 copy_area (X_DOT, BACK_DOT, 1, 1, x0+column, y0-row);
501 #undef USER_RANGE