wmcalc: Use version number from autoconf.
[dockapps.git] / wmmon / wmmon.c
blob737bed632db1da2cec2cf8380be3b49e559806ed
1 /*
2 Code based on wmppp/wmifs
4 [Orig WMPPP comments]
6 This code was mainly put together by looking at the
7 following programs:
9 asclock
10 A neat piece of equip, used to display the date
11 and time on the screen.
12 Comes with every AfterStep installation.
14 Source used:
15 How do I create a not so solid window?
16 How do I open a window?
17 How do I use pixmaps?
19 ------------------------------------------------------------
21 Authors: Martijn Pieterse (pieterse@xs4all.nl)
22 Antoine Nulle (warp@xs4all.nl)
24 This program is distributed under the GPL license.
25 (as were asclock and pppstats)
27 ----
28 Changes:
29 ----
31 17/03/2014 (Pedro Gimeno Fortea)
32 * Fix jiffy counter overflowing long on 32-bit systems.
33 17/06/2012 (Rodolfo García Peñas (kix), <kix@kix.es>)
34 * Code style.
35 13/3/2012 (Barry Kelly (wbk), <coydog@devio.us>)
36 * Fixed get_statistics() I/O features to work with newer
37 /proc/diskstats instead of the old /proc/stat.
38 * Fixes to graph/meter scaling for I/O. Original code let
39 the scaling grow out of control due to inappropriate static
40 data.
41 * Eliminated rounding down relatively low stats in getWidth()
42 and DrawStats_io() by using double and float types instead
43 of ints. We now round up tiny values to prevent the system
44 appearing idle when it's not.
45 * Style/readbility edits.
46 * TODO: Merge in Gentoo and possibly BSD local patches. This
47 should aid in fixing I/O monitoring on non-Linux systems.
48 * TODO: Color swapping. User supplies color values in .rc, and
49 app modifies pixmap in memory on startup. Should be simple.
50 * TODO: address compiler warnings (GCC has gotten pickier over
51 the years).
52 17/10/2009 (Romuald Delavergne, romuald.delavergne@free.fr)
53 * Support SMP processors in realtime CPU stress meter
54 15/05/2004 (Simon Law, sfllaw@debian.org)
55 * Support disabling of mode-cycling
56 23/10/2003 (Simon Law, sfllaw@debian.org)
57 * Eliminated exploitable static buffers
58 * Added -geometry support.
59 * /proc/meminfo support for Linux 2.6
60 18/05/1998 (Antoine Nulle, warp@xs4all.nl)
61 * MEM/SWAP/UPTIME only updated when visible
62 * Using global file descriptors to reduce file
63 system overhead, both updates are based on a diff
64 supplied by Dave Harden (dharden@wisewire.com)
65 15/05/1998 (Antoine Nulle, warp@xs4all.nl)
66 * Fixed memory overflow in the MEM gaugebar
67 * MEM gauge displays now real used mem
68 (buffered + cached mem removed)
69 14/05/1998 (Antoine Nulle, warp@xs4all.nl)
70 * Added -i & -s kludge for selecting startupmode,
71 tijno, don't hate me for this :)
72 12/05/1998 (Antoine Nulle, warp@xs4all.nl)
73 * Finetuned master-xpm, tijno don't worry, no
74 reprogramming needed this time ;-)
75 07/05/1998 (Martijn Pieterse, pieterse@xs4all.nl)
76 * Added disk i/o
77 03/05/1998 (Antoine Nulle, warp@xs4all.nl)
78 * Added new master-xpm which contains the gfx
79 for the upcoming SysInfo part :P
80 02/05/1998 (Martijn Pieterse, pieterse@xs4all.nl)
81 * Removed a lot of code, that was put in wmgeneral
82 23/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
83 * Added zombie destroying code (aka wait :) )
84 18/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
85 * Added CPU-on-screen.
86 * Added -display command line
87 15/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
88 * Fixed a bug in the stats routine
89 (Top 3 bright pixels were not shown when 100% loaded)
90 * Changed xpm to a no-title one.
91 This included the reprogramming of all positions.
92 warpstah, i hate you! ;)
93 05/04/1998 (Martijn Pieterse, pieterse@xs4all.nl)
94 * First Working Version
97 #define _GNU_SOURCE
98 #include <stdlib.h>
99 #include <stdio.h>
100 #include <time.h>
101 #include <string.h>
102 #include <fcntl.h>
103 #include <unistd.h>
105 #include <sys/wait.h>
107 #include <X11/X.h>
108 #include <X11/Xlib.h>
110 #include <libdockapp/wmgeneral.h>
111 #include <libdockapp/misc.h>
113 #include "ulllib.h"
114 #include "wmmon-master.xpm"
115 #include "wmmon-mask.xbm"
117 /***********/
118 /* Defines */
119 /***********/
120 #define HISTORY_ENTRIES 55
121 #define MAX_CPU (10) /* depends on graph height */
122 #define MAX_STAT_DEVICES (4)
124 /********************/
125 /* Global Variables */
126 /********************/
127 int stat_current = 0; /* now global */
128 int mode_cycling = 1; /* Allow mode-cycling */
129 int cpu_avg_max = 0; /* CPU stress meter with average and max for SMP */
130 int show_buffers = 0; /* wbk adding per Gentoo -b enhancement. */
132 FILE *fp_meminfo;
133 FILE *fp_stat;
134 FILE *fp_loadavg;
135 FILE *fp_diskstats; /* wbk new io stats API */
137 /* functions */
138 void usage(char*);
139 void printversion(void);
140 void DrawStats(int *, int, int, int, int);
141 void DrawStats_io(int *, int, int, int, int);
142 void wmmon_routine(int, char **);
144 int main(int argc, char *argv[])
146 int i;
147 char *name = argv[0];
149 /* Parse Command Line */
150 for (i = 1; i < argc; i++) {
151 char *arg = argv[i];
153 if (*arg=='-')
154 switch (arg[1]) {
155 case 'd' :
156 if (strcmp(arg+1, "display")) {
157 usage(name);
158 return 1;
160 break;
161 case 'g' :
162 if (strcmp(arg+1, "geometry")) {
163 usage(name);
164 return 1;
166 case 'l' :
167 mode_cycling = 0;
168 break;
169 case 'c' :
170 cpu_avg_max = 1;
171 break;
172 case 'i' :
173 stat_current = 1;
174 break;
175 case 'b' :
176 show_buffers = 1;
177 break;
178 case 's' :
179 stat_current = 2;
180 break;
181 case 'v' :
182 printversion();
183 return 0;
184 default:
185 usage(name);
186 return 1;
190 wmmon_routine(argc, argv);
191 exit(0);
194 /*******************************************************************************\
195 |* wmmon_routine *|
196 \*******************************************************************************/
198 typedef struct {
199 char name[5]; /* "cpu0..cpuz", eventually.. :) */
200 int his[HISTORY_ENTRIES];
201 int hisaddcnt;
202 long rt_stat;
203 ullong statlast;
204 long rt_idle;
205 ullong idlelast;
206 /* Processors stats */
207 long *cpu_stat;
208 ullong *cpu_last;
209 long *idle_stat;
210 ullong *idle_last;
211 } stat_dev;
213 stat_dev stat_device[MAX_STAT_DEVICES];
215 char *left_action, *right_action, *middle_action;
216 int nb_cpu, cpu_max;
218 int getNbCPU(void);
219 unsigned long getWidth(long, long);
220 int checksysdevs(void);
221 void get_statistics(char *, long *, ullong *, ullong *, ullong *, ullong *);
222 void DrawActive(char *);
224 void update_stat_cpu(stat_dev *, ullong *, ullong *);
225 void update_stat_io(stat_dev *);
226 void update_stat_mem(stat_dev *st, stat_dev *st2);
227 void update_stat_swp(stat_dev *);
229 void wmmon_routine(int argc, char **argv)
231 rckeys wmmon_keys[] = {
232 { "left", &left_action },
233 { "right", &right_action },
234 { "middle", &middle_action },
235 { NULL, NULL }
238 int i, j;
239 long k;
240 XEvent Event;
241 int but_stat = -1;
243 int stat_online;
245 long starttime, curtime, nexttime;
246 ullong istat, idle, *istat2, *idle2;
248 FILE *fp;
249 char *conffile = NULL;
251 int xpm_X = 0, xpm_Y = 0;
253 long online_time = 0;
254 long ref_time = 0;
255 long cnt_time;
258 fp = fopen("/proc/uptime", "r");
259 fp_meminfo = fopen("/proc/meminfo", "r");
260 fp_loadavg = fopen("/proc/loadavg", "r");
261 fp_stat = fopen("/proc/stat", "r");
262 fp_diskstats = fopen("/proc/diskstats", "r");
264 if (fp) {
265 if (fscanf(fp, "%ld", &online_time) == EOF)
266 perror("Error! fscanf() of /proc/uptime failed!\n");
267 ref_time = time(0);
268 fclose(fp);
271 for (i = 0; i < MAX_STAT_DEVICES; i++) {
272 for (j = 0; j < HISTORY_ENTRIES; j++)
273 stat_device[i].his[j] = 0;
275 stat_device[i].hisaddcnt = 0;
278 /* wbk - I don't fully understand this. Probably just a means of providing
279 * test cases. ifdef'ing to clear compiler warnings. TODO: remove. */
280 #ifdef LEFT_ACTION
281 if (LEFT_ACTION)
282 left_action = strdup(LEFT_ACTION);
283 #endif
284 #ifdef RIGHT_ACTION
285 if (RIGHT_ACTION)
286 right_action = strdup(RIGHT_ACTION);
287 #endif
288 #ifdef MIDDLE_ACTION
289 if (MIDDLE_ACTION)
290 middle_action = strdup(MIDDLE_ACTION);
291 #endif
293 /* Scan through the .rc files */
294 if (asprintf(&conffile, "/etc/wmmonrc") >= 0) {
295 parse_rcfile(conffile, wmmon_keys);
296 free(conffile);
299 if (asprintf(&conffile, "%s/.wmmonrc", getenv("HOME")) >= 0) {
300 parse_rcfile(conffile, wmmon_keys);
301 free(conffile);
304 if (asprintf(&conffile, "/etc/wmmonrc.fixed") >= 0) {
305 parse_rcfile(conffile, wmmon_keys);
306 free(conffile);
309 stat_online = checksysdevs();
311 nb_cpu = getNbCPU();
312 stat_device[0].cpu_stat = calloc(nb_cpu, sizeof(long));
313 stat_device[0].cpu_last = calloc(nb_cpu, sizeof(ullong));
314 stat_device[0].idle_stat = calloc(nb_cpu, sizeof(long));
315 stat_device[0].idle_last = calloc(nb_cpu, sizeof(ullong));
316 if (!stat_device[0].cpu_stat ||
317 !stat_device[0].cpu_last ||
318 !stat_device[0].idle_stat ||
319 !stat_device[0].idle_last) {
320 fprintf(stderr, "%s: Unable to alloc memory !\n", argv[0]);
321 exit(1);
324 istat2 = calloc(nb_cpu, sizeof(ullong));
325 idle2 = calloc(nb_cpu, sizeof(ullong));
326 if (!istat2 || !idle2) {
327 fprintf(stderr, "%s: Unable to alloc memory !!\n", argv[0]);
328 exit(1);
331 openXwindow(argc, argv, wmmon_master_xpm, (char *)wmmon_mask_bits,
332 wmmon_mask_width, wmmon_mask_height);
334 /* add mouse region */
335 AddMouseRegion(0, 12, 13, 58, 57);
336 AddMouseRegion(1, 5, 5, 24, 14);
338 starttime = time(0);
339 nexttime = starttime + 10;
341 /* Collect information on each panel */
342 for (i = 0; i < stat_online; i++) {
343 get_statistics(stat_device[i].name, &k, &istat, &idle, istat2, idle2);
344 stat_device[i].statlast = istat;
345 stat_device[i].idlelast = idle;
346 if (i == 0 && nb_cpu > 1) {
347 int cpu;
348 for (cpu = 0; cpu < nb_cpu; cpu++) {
349 stat_device[i].cpu_last[cpu] = istat2[cpu];
350 stat_device[i].idle_last[cpu] = idle2[cpu];
355 /* Set the mask for the current window */
356 switch (stat_current) {
357 case 0:
358 case 1:
359 xpm_X = 0;
360 setMaskXY(0, 0);
361 break;
362 case 2:
363 xpm_X = 64;
364 setMaskXY(-64, 0);
365 default:
366 break;
369 /* Draw statistics */
370 if (stat_current == 0) {
371 DrawStats(stat_device[stat_current].his,
372 HISTORY_ENTRIES-1, 40, 5, 58);
373 } else if (stat_current == 1) {
374 DrawStats_io(stat_device[stat_current].his,
375 HISTORY_ENTRIES, 40, 5, 58);
378 DrawActive(stat_device[stat_current].name);
380 while (1) {
381 curtime = time(NULL);
383 waitpid(0, NULL, WNOHANG);
386 update_stat_cpu(&stat_device[0], istat2, idle2);
387 update_stat_io(&stat_device[1]);
389 if(stat_current == 2)
390 update_stat_mem(&stat_device[2], &stat_device[3]);
392 if (stat_current < 2) {
393 i = stat_current;
395 /* Load ding is 45 pixels hoog */
396 copyXPMArea(0, 64, 32, 12, 28, 4);
398 if (i == 0 && nb_cpu > 1) {
399 if (nb_cpu > MAX_CPU || cpu_avg_max) {
400 /* show average CPU */
401 j = getWidth(stat_device[i].rt_stat, stat_device[i].rt_idle);
402 copyXPMArea(32, 64, j, 6, 28, 4);
403 /* Show max CPU */
404 j = getWidth(stat_device[i].cpu_stat[cpu_max],
405 stat_device[i].idle_stat[cpu_max]);
406 copyXPMArea(32, 70, j, 6, 28, 10);
407 } else {
408 int cpu;
409 for (cpu = 0; cpu < nb_cpu; cpu++) {
410 j = getWidth(stat_device[i].cpu_stat[cpu],
411 stat_device[i].idle_stat[cpu]);
412 copyXPMArea(32, 65, j,
413 MAX_CPU / nb_cpu, 28,
414 5 + (MAX_CPU / nb_cpu) * cpu);
417 } else {
418 j = getWidth(stat_device[i].rt_stat, stat_device[i].rt_idle);
419 copyXPMArea(32, 64, j, 12, 28, 4);
421 } else {
422 /* Nu zal ie wel 3 zijn. */
424 copyXPMArea(0, 64, 32, 12, 28+64, 4);
425 copyXPMArea(0, 64, 32, 12, 28+64, 18);
427 j = stat_device[2].rt_idle;
428 if (j != 0) {
429 j = (stat_device[2].rt_stat * 100) / j;
431 j = j * 0.32;
432 if (j > 32) j = 32;
433 copyXPMArea(32, 64, j, 12, 28+64, 4);
435 /*--------------------- swap? ------------------*/
436 j = stat_device[3].rt_idle;
437 if (j != 0)
438 j = (stat_device[3].rt_stat * 100) / j;
440 j = j * 0.32;
441 if (j > 32) j = 32;
442 copyXPMArea(32, 64, j, 12, 28+64, 18);
444 /*----------- online tijd neerzetten! ----------*/
445 cnt_time = time(0) - ref_time + online_time;
447 /* cnt_time = uptime in seconden */
449 secs = 108,47
450 mins = 89,47
451 uren = 70,47
452 digits = 40,78, 6breed, 9hoog
454 i = cnt_time % 60;
455 cnt_time /= 60;
456 copyXPMArea(40 + (i % 10)*7, 78, 6, 9, 115, 47);
457 copyXPMArea(40 + (i / 10)*7, 78, 6, 9, 108, 47);
459 i = cnt_time % 60;
460 cnt_time /= 60;
461 copyXPMArea(40 + (i % 10)*7, 78, 6, 9, 96, 47);
462 copyXPMArea(40 + (i / 10)*7, 78, 6, 9, 89, 47);
464 i = cnt_time % 24;
465 cnt_time /= 24;
466 copyXPMArea(40 + (i % 10)*7, 78, 6, 9, 77, 47);
467 copyXPMArea(40 + (i / 10)*7, 78, 6, 9, 70, 47);
469 /* De rest is dagen! 5x7*/
470 i = cnt_time;
471 copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 88, 35);
472 i /= 10;
473 copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 82, 35);
474 i /= 10;
475 copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 76, 35);
476 i /= 10;
477 copyXPMArea(66 + (i % 10)*6, 66, 5, 7, 70, 35);
480 if (curtime >= nexttime) {
481 nexttime+=10;
483 if (curtime > nexttime) /* dont let APM suspends make this crazy */
484 nexttime = curtime;
486 for (i=0; i<stat_online; i++) {
487 stat_dev *sd = stat_device + i;
489 if (sd->his[HISTORY_ENTRIES-1])
490 sd->his[HISTORY_ENTRIES-1] /= sd->hisaddcnt;
492 for (j = 1; j < HISTORY_ENTRIES; j++)
493 sd->his[j-1] = sd->his[j];
495 if (i == stat_current) {
496 if (i == 0)
497 DrawStats(sd->his, HISTORY_ENTRIES - 1, 40, 5, 58);
498 else if (i == 1)
499 DrawStats_io(sd->his, HISTORY_ENTRIES - 1, 40, 5, 58);
501 sd->his[HISTORY_ENTRIES-1] = 0;
502 sd->hisaddcnt = 0;
506 RedrawWindowXY(xpm_X, xpm_Y);
508 while (XPending(display)) {
509 XNextEvent(display, &Event);
510 switch (Event.type) {
511 case Expose:
512 RedrawWindowXY(xpm_X, xpm_Y);
513 break;
514 case DestroyNotify:
515 XCloseDisplay(display);
516 exit(0);
517 break;
518 case ButtonPress:
519 but_stat = CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
520 break;
521 case ButtonRelease:
522 i = CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
523 if (but_stat == i && but_stat >= 0) {
524 switch (but_stat) {
525 case 0:
526 switch (Event.xbutton.button) {
527 case 1:
528 if (left_action)
529 execCommand(left_action);
530 break;
531 case 2:
532 if (middle_action)
533 execCommand(middle_action);
534 break;
535 case 3:
536 if (right_action)
537 execCommand(right_action);
538 break;
540 break;
541 case 1:
542 if (mode_cycling) {
543 stat_current++;
544 if (stat_current == stat_online)
545 stat_current = 0;
547 DrawActive(stat_device[stat_current].name);
548 if (stat_current == 0)
549 DrawStats(stat_device[stat_current].his,
550 HISTORY_ENTRIES-1, 40, 5, 58);
552 if (stat_current == 1)
553 DrawStats_io(stat_device[stat_current].his,
554 HISTORY_ENTRIES-1, 40, 5, 58);
556 if (stat_current == 2) {
557 xpm_X = 64;
558 setMaskXY(-64, 0);
559 } else {
560 xpm_X = 0;
561 setMaskXY(0, 0);
563 RedrawWindowXY(xpm_X, xpm_Y);
565 break;
568 break;
571 usleep(250000L);
576 void update_stat_cpu(stat_dev *st, ullong *istat2, ullong *idle2)
578 long k;
579 ullong istat, idle;
581 get_statistics(st->name, &k, &istat, &idle, istat2, idle2);
583 st->rt_idle = ullsub(&idle, &st->idlelast);
584 st->idlelast = idle;
586 st->rt_stat = ullsub(&istat, &st->statlast);
587 st->statlast = istat;
589 if (nb_cpu > 1) {
590 int cpu;
591 unsigned long max, j;
592 cpu_max = 0; max = 0;
593 for (cpu = 0; cpu < nb_cpu; cpu++) {
594 st->idle_stat[cpu] = ullsub(&idle2[cpu], &st->idle_last[cpu]);
595 st->idle_last[cpu] = idle2[cpu];
597 st->cpu_stat[cpu] = ullsub(&istat2[cpu], &st->cpu_last[cpu]);
598 st->cpu_last[cpu] = istat2[cpu];
600 j = st->cpu_stat[cpu] + st->idle_stat[cpu];
602 if (j != 0)
603 j = (st->cpu_stat[cpu] << 7) / j;
605 if (j > max) {
606 max = j;
607 cpu_max = cpu;
612 st->his[HISTORY_ENTRIES-1] += k;
613 st->hisaddcnt += 1;
617 void update_stat_io(stat_dev *st)
619 long j, k;
620 ullong istat, idle;
622 /* Periodically re-sample. Sometimes we get anomalously high readings;
623 * this discards them. */
624 static int stalemax = 300;
625 static long maxdiskio = 0;
626 if (--stalemax <= 0) {
627 maxdiskio = 0;
628 stalemax = 300;
631 get_statistics(st->name, &k, &istat, &idle, NULL, NULL);
633 st->rt_idle = ullsub(&idle, &st->idlelast);
634 st->idlelast = idle;
636 st->rt_stat = ullsub(&istat, &st->statlast);
637 st->statlast = istat;
639 /* remember peak for scaling of upper-right meter. */
640 j = st->rt_stat;
641 if (maxdiskio < j)
642 maxdiskio = j;
644 /* Calculate scaling factor for upper-right meter. "/ 5" will clip
645 * the highest peaks, but makes moderate values more visible. We are
646 * compensating for wild fluctuations which are probably caused by
647 * kernel I/O buffering.
649 st->rt_idle = (maxdiskio - j) / 5;
650 if (j > 0 && st->rt_idle < 1)
651 st->rt_idle = 1; /* scale up tiny values so they are visible */
653 st->his[HISTORY_ENTRIES-1] += st->rt_stat;
654 st->hisaddcnt += 1;
658 void update_stat_mem(stat_dev *st, stat_dev *st2)
660 static char *line = NULL;
661 static size_t line_size = 0;
663 unsigned long swapfree;
664 unsigned long free, shared, buffers, cached;
666 if (freopen("/proc/meminfo", "r", fp_meminfo) == NULL)
667 perror("freopen() of /proc/meminfo failed!)\n");
669 while ((getline(&line, &line_size, fp_meminfo)) > 0) {
670 /* The original format for the first two lines of /proc/meminfo was
671 * Mem: total used free shared buffers cached
672 * Swap: total used free
674 * As of at least 2.5.47 these two lines were removed, so that the
675 * required information has to come from the rest of the lines.
676 * On top of that, used is no longer recorded - you have to work
677 * this out yourself, from total - free.
679 * So, these changes below should work. They should also work with
680 * older kernels, too, since the new format has been available for
681 * ages.
683 if (strstr(line, "MemTotal:"))
684 sscanf(line, "MemTotal: %ld", &st->rt_idle);
685 else if (strstr(line, "MemFree:"))
686 sscanf(line, "MemFree: %lu", &free);
687 else if (strstr(line, "MemShared:"))
688 sscanf(line, "MemShared: %lu", &shared);
689 else if (strstr(line, "Buffers:"))
690 sscanf(line, "Buffers: %lu", &buffers);
691 else if (strstr(line, "Cached:"))
692 sscanf(line, "Cached: %lu", &cached);
693 else if (strstr(line, "SwapTotal:"))
694 sscanf(line, "SwapTotal: %ld", &st2->rt_idle);
695 else if (strstr(line, "SwapFree:"))
696 sscanf(line, "SwapFree: %lu", &swapfree);
699 /* memory use - rt_stat is the amount used, it seems, and this isn't
700 * recorded in current version of /proc/meminfo (as of 2.5.47), so we
701 * calculate it from MemTotal - MemFree
703 st->rt_stat = st->rt_idle - free;
705 /* wbk -b flag (from Gentoo patchkit) */
706 if (!show_buffers)
707 st->rt_stat -= buffers+cached;
708 /* As with the amount of memory used, it's not recorded any more, so
709 * we have to calculate it ourselves.
711 st2->rt_stat = st2->rt_idle - swapfree;
714 void update_stat_swp(stat_dev *st)
716 static char *line = NULL;
717 static size_t line_size = 0;
718 unsigned long swapfree;
720 fseek(fp_meminfo, 0, SEEK_SET);
721 while ((getline(&line, &line_size, fp_meminfo)) > 0) {
722 /* As with update_stat_mem(), the format change to /proc/meminfo has
723 * forced some changes here. */
724 if (strstr(line, "SwapTotal:"))
725 sscanf(line, "SwapTotal: %ld", &st->rt_idle);
726 else if (strstr(line, "SwapFree:"))
727 sscanf(line, "SwapFree: %lu", &swapfree);
729 st->rt_stat = st->rt_idle - swapfree;
732 /*******************************************************************************\
733 |* get_statistics *|
734 \*******************************************************************************/
735 void get_statistics(char *devname, long *is, ullong *ds, ullong *idle, ullong *ds2, ullong *idle2)
737 int i;
738 static char *line = NULL;
739 static size_t line_size = 0;
740 char *p;
741 char *tokens = " \t\n";
742 float f;
743 ullong ulltmp;
745 *is = 0;
746 ullreset(ds);
747 ullreset(idle);
749 if (!strncmp(devname, "cpu", 3)) {
750 fseek(fp_stat, 0, SEEK_SET);
751 while ((getline(&line, &line_size, fp_stat)) > 0) {
752 if (strstr(line, "cpu")) {
753 int cpu = -1; /* by default, cumul stats => average */
754 if (!strstr(line, "cpu ")) {
755 sscanf(line, "cpu%d", &cpu);
756 ullreset(&ds2[cpu]);
757 ullreset(&idle2[cpu]);
759 p = strtok(line, tokens);
760 /* 1..3, 4 == idle, we don't want idle! */
761 for (i=0; i<3; i++) {
762 p = strtok(NULL, tokens);
763 ullparse(&ulltmp, p);
764 if (cpu == -1)
765 ulladd(ds, &ulltmp);
766 else
767 ulladd(&ds2[cpu], &ulltmp);
769 p = strtok(NULL, tokens);
770 if (cpu == -1)
771 ullparse(idle, p);
772 else
773 ullparse(&idle2[cpu], p);
776 if ((fp_loadavg = freopen("/proc/loadavg", "r", fp_loadavg)) == NULL)
777 perror("ger_statistics(): freopen(proc/loadavg) failed!\n");
779 if (fscanf(fp_loadavg, "%f", &f) == EOF)
780 perror("fscanf() failed to read f\n");
781 *is = (long) (100 * f);
784 if (!strncmp(devname, "i/o", 3)) {
785 if (fseek(fp_diskstats, 0, SEEK_SET) == -1)
786 perror("get_statistics() seek failed\n");
788 /* wbk 20120308 These are no longer in /proc/stat. /proc/diskstats
789 * seems to be the closest replacement. Under modern BSD's, /proc is
790 * now deprecated, so iostat() might be the answer.
791 * http://www.gossamer-threads.com/lists/linux/kernel/314618
792 * has good info on this being removed from kernel. Also see
793 * kernel sources Documentation/iostats.txt
795 * TODO: We will end up with doubled values. We are adding the
796 * aggregate to the individual partition, due to device selection
797 * logic. Either grab devices' stats with numbers, or without (sda
798 * OR sda[1..10]. Could use strstr() return plus offset, but would
799 * have to be careful with bounds checking since we're in a
800 * limited buffer. Or just divide by 2 (inefficient). Shouldn't
801 * matter for graphing (we care about proportions, not numbers). */
802 while ((getline(&line, &line_size, fp_diskstats)) > 0) {
803 if (strstr(line, "sd") || strstr(line, "sr")) {
804 p = strtok(line, tokens);
805 /* skip 3 tokens, then use fields from
806 `* linux/Documentation/iostats.txt */
807 for (i = 1; i <= 6; i++)
808 p = strtok(NULL, tokens);
810 ullparse(&ulltmp, p);
811 ulladd(ds, &ulltmp);
812 for (i = 7; i <= 10; i++)
813 p = strtok(NULL, tokens);
815 ullparse(&ulltmp, p);
816 ulladd(ds, &ulltmp);
823 /*******************************************************************************\
824 |* getWidth *|
825 \*******************************************************************************/
826 unsigned long getWidth(long actif, long idle)
828 /* wbk - work with a decimal value so we don't round < 1 down to zero. */
829 double j = 0;
830 unsigned long r = 0;
832 j = (actif + idle);
833 if (j != 0)
834 j = (actif * 100) / j;
836 j = j * 0.32;
838 /* round up very low positive values so they are visible. */
839 if (actif > 0 && j < 2)
840 j = 2;
842 if (j > 32)
843 j = 32;
845 r = (unsigned long) j;
846 return r;
850 /*******************************************************************************\
851 |* getNbCPU *|
852 \*******************************************************************************/
853 int getNbCPU(void)
855 static char *line = NULL;
856 static size_t line_size = 0;
857 int cpu = 0;
859 fseek(fp_stat, 0, SEEK_SET);
860 while ((getline(&line, &line_size, fp_stat)) > 0) {
861 if (strstr(line, "cpu") && !strstr(line, "cpu "))
862 sscanf(line, "cpu%d", &cpu);
865 return cpu+1;
869 /*******************************************************************************\
870 |* checksysdevs *|
871 \*******************************************************************************/
872 int checksysdevs(void) {
873 strcpy(stat_device[0].name, "cpu0");
874 strcpy(stat_device[1].name, "i/o");
875 strcpy(stat_device[2].name, "sys");
877 return 3;
881 /*******************************************************************************\
882 |* void DrawActive(char *) *|
883 \*******************************************************************************/
884 void DrawActive(char *name)
887 /* Alles op X,77
888 CPU: 0
889 I/O: 21
891 20 Breed, 10 hoog
892 Destinatie: 5,5
895 if (name[0] == 'c')
896 copyXPMArea(0, 77, 19, 10, 5, 5);
897 else if (name[0] == 'i')
898 copyXPMArea(19, 77, 19, 10, 5, 5);
902 /*******************************************************************************\
903 |* DrawStats *|
904 \*******************************************************************************/
905 void DrawStats(int *his, int num, int size, int x_left, int y_bottom)
907 int pixels_per_byte, j, k, *p, d, hint_height, hint_color;
909 pixels_per_byte = 100;
910 p = his;
912 for (j=0; j<num; j++) {
913 while (p[0] > pixels_per_byte)
914 pixels_per_byte += 100;
915 p += 1;
918 p = his;
920 for (k=0; k<num; k++) {
921 d = (1.0 * p[0] / pixels_per_byte) * size;
923 for (j=0; j<size; j++) {
924 if (j < d - 3)
925 copyXPMArea(2, 88, 1, 1, k+x_left, y_bottom-j);
926 else if (j < d)
927 copyXPMArea(2, 89, 1, 1, k+x_left, y_bottom-j);
928 else
929 copyXPMArea(2, 90, 1, 1, k+x_left, y_bottom-j);
931 p += 1;
934 /* Nu horizontaal op 100/200/300 etc lijntje trekken! */
935 if (pixels_per_byte > 10000) {
936 hint_height = 10000;
937 hint_color = 93; /* red */
938 } else if (pixels_per_byte > 1000) {
939 hint_height = 1000;
940 hint_color = 92; /* yellow */
941 } else {
942 hint_height = 100;
943 hint_color = 91; /* green */
946 for (j = hint_height; j < pixels_per_byte; j += hint_height) {
947 d = (40.0 / pixels_per_byte) * j - 1;
948 for (k=0; k<num; k++) {
949 copyXPMArea(2, hint_color, 1, 1, k+x_left, y_bottom-d);
955 /*******************************************************************************\
956 |* DrawStats_io *|
957 \*******************************************************************************/
958 void DrawStats_io(int *his, int num, int size, int x_left, int y_bottom)
960 float pixels_per_byte;
961 int j, k, *p;
962 /* wbk - Use a double to avoid rounding values of d < 1 to zero. */
963 double d = 0;
964 int border = 3;
966 /* wbk - this should not be static. No need to track the scale, since
967 * we always calculate it on the fly anyway. This static variable did
968 * not get re-initialized when we entered this function, so the scale
969 * would always grow and never shrink.
971 /*static int global_io_scale = 1;*/
972 int io_scale = 1;
974 p = his;
975 for (j=0; j<num; j++)
976 if (p[j] > io_scale) io_scale = p[j];
978 pixels_per_byte = 1.0 * io_scale / size;
979 if (pixels_per_byte == 0)
980 pixels_per_byte = 1;
982 for (k=0; k<num; k++) {
983 d = (1.0 * p[0] / pixels_per_byte);
985 /* graph values too low for graph resolution */
986 if (d > 0 && d < 1) {
987 d = 3;
988 border = 2;
989 } else {
990 border = 3;
993 for (j=0; j<size; j++) {
994 if (j < d - border)
995 copyXPMArea(2, 88, 1, 1, k+x_left, y_bottom-j);
996 else if (j < d )
997 copyXPMArea(2, 89, 1, 1, k+x_left, y_bottom-j);
998 else
999 copyXPMArea(2, 90, 1, 1, k+x_left, y_bottom-j);
1001 p += 1; /* beware... */
1006 /*******************************************************************************\
1007 |* usage *|
1008 \*******************************************************************************/
1009 void usage(char *name)
1011 printf("Usage: %s [OPTION]...\n", name);
1012 printf("WindowMaker dockapp that displays system information.\n");
1013 printf("\n");
1014 printf(" -display DISPLAY contact the DISPLAY X server\n");
1015 printf(" -geometry GEOMETRY position the clock at GEOMETRY\n");
1016 printf(" -l locked view - cannot cycle modes\n");
1017 printf(" -c show average and max CPU for SMP machine.\n");
1018 printf(" default if there is more than %d processors\n", MAX_CPU);
1019 printf(" -i start in Disk I/O mode\n");
1020 printf(" -s start in System Info mode\n");
1021 printf(" -b include buffers and cache in memory usage\n");
1022 printf(" -h display this help and exit\n");
1023 printf(" -v output version information and exit\n");
1027 /*******************************************************************************\
1028 |* printversion *|
1029 \*******************************************************************************/
1030 void printversion(void)
1032 printf("WMMon version %s\n", PACKAGE_VERSION);
1034 /* vim: sw=4 ts=4 columns=82