wmcore: use wmgeneral functions in libdockapp instead of the private copy.
[dockapps.git] / wmtop / wmtop.c
blob2f648ee3b1d4a26fe68abe281c9dc0daa3884cb1
1 /******************************************/
2 /* WMTOP - Mini top in a dock app */
3 /******************************************/
5 /*
6 * wmtop.c -- WindowMaker process view dock app
7 * Derived by Dan Piponi dan@tanelorn.demon.co.uk
8 * http://www.tanelorn.demon.co.uk
9 * http://wmtop.sourceforge.net
10 * from code originally contained in wmsysmon by Dave Clark (clarkd@skynet.ca)
11 * This software is licensed through the GNU General Public License.
15 * Ensure there's an operating system defined. There is *no* default
16 * because every OS has it's own way of revealing CPU/memory usage.
18 #if defined(FREEBSD)
19 #define OS_DEFINED
20 #endif /* defined(FREEBSD) */
22 #if defined(LINUX)
23 #define OS_DEFINED
24 #endif /* defined(LINUX) */
26 #if !defined(OS_DEFINED)
27 #error No operating system selected
28 #endif /* !defined(OS_DEFINED) */
30 #define _BSD_SOURCE
32 /******************************************/
33 /* Includes */
34 /******************************************/
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <time.h>
39 #include <dirent.h>
40 #include <string.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <ctype.h>
44 #include <math.h>
45 #include <limits.h>
46 #include <errno.h>
47 #include <signal.h>
49 #if defined(PARANOID)
50 #include <assert.h>
51 #endif /* defined(PARANOID) */
53 #include <sys/wait.h>
54 #include <sys/stat.h>
55 #include <sys/param.h>
56 #include <sys/types.h>
57 #include <sys/ioctl.h>
58 #include <sys/time.h>
60 #include <X11/Xlib.h>
61 #include <X11/xpm.h>
62 #include <X11/extensions/shape.h>
63 #include <X11/keysym.h>
65 #include <regex.h>
67 #include <libdockapp/wmgeneral.h>
68 #include <libdockapp/misc.h>
69 #include "xpm/wmtop-default.xpm"
70 #include "xpm/wmtop-lcd.xpm"
71 #include "xpm/wmtop-neon1.xpm"
72 #include "xpm/wmtop-neon2.xpm"
73 #include "xpm/wmtop-rainbow.xpm"
75 /******************************************/
76 /* Defines */
77 /******************************************/
80 * XXX: I shouldn't really use this WMTOP_BUFLENGTH variable but scanf is so
81 * lame and it'll take me a while to write a replacement.
83 #define WMTOP_BUFLENGTH 1024
85 #if defined(LINUX)
86 #define PROCFS_TEMPLATE "/proc/%d/stat"
87 #define PROCFS_CMDLINE_TEMPLATE "/proc/%d/cmdline"
88 #endif /* defined(LINUX) */
90 #if defined(FREEBSD)
91 #define PROCFS_TEMPLATE "/proc/%d/status"
92 #endif /* defined(FREEBSD) */
94 /******************************************/
95 /* Globals */
96 /******************************************/
98 regex_t *exclusion_expression = 0;
99 uid_t user = (uid_t) -1;
100 char *process_command = 0;
102 * Default mode: zero=cpu one=memory
104 int mode = 0;
107 * Number and default artistic styles.
109 int nstyles = 5;
110 int style = 0;
112 char wmtop_mask_bits[64*64];
113 int wmtop_mask_width = 64;
114 int wmtop_mask_height = 64;
116 int update_rate = 1000000;
117 int refresh_rate = 100000;
119 extern char **environ;
121 char *ProgName;
123 /******************************************/
124 /* Debug */
125 /******************************************/
127 #if defined(DEBUG)
129 * Memory handler
131 int g_malloced = 0;
133 void *wmtop_malloc(int n) {
134 int *p = (int *)malloc(sizeof(int)+n);
135 p[0] = n;
136 g_malloced += n;
137 return (void *)(p+1);
140 void wmtop_free(void *n) {
141 int *p = (int *)n;
142 g_malloced -= p[-1];
143 free(p-1);
146 void show_memory() {
147 fprintf(stderr,"%d bytes allocated\n",g_malloced);
149 #else /* defined(DEBUG) */
150 #define wmtop_malloc malloc
151 #define wmtop_free free
152 #endif /* defined(DEBUG) */
154 char *wmtop_strdup(const char *s) {
155 return strcpy((char *)wmtop_malloc(strlen(s)+1),s);
158 /******************************************/
159 /* Structures */
160 /******************************************/
162 struct {
163 char **pixmap;
164 char *description;
165 } styles[] = {
166 { wmtop_default_xpm, "Light emitting diode (default)" },
167 { wmtop_lcd_xpm, "Liquid crystal display" },
168 { wmtop_rainbow_xpm, "Rainbow display" },
169 { wmtop_neon1_xpm, "Neon lights" },
170 { wmtop_neon2_xpm, "More neon lights" },
173 struct process {
174 #if defined(PARANOID)
175 long id;
176 #endif /* defined(PARANOID) */
178 * Store processes in a doubly linked list
180 struct process *next;
181 struct process *previous;
183 pid_t pid;
184 char *name;
185 float amount;
186 unsigned long user_time;
187 unsigned long kernel_time;
188 unsigned long previous_user_time;
189 unsigned long previous_kernel_time;
190 unsigned long vsize;
191 long rss;
192 int time_stamp;
193 int counted;
196 /******************************************/
197 /* Process class */
198 /******************************************/
201 * Global pointer to head of process list
203 struct process *first_process = 0;
205 int g_time = 0;
207 struct process *find_process(pid_t pid) {
208 struct process *p = first_process;
209 while (p) {
210 if (p->pid==pid)
211 return p;
212 p = p->next;
214 return 0;
218 * Create a new process object and insert it into the process list
220 struct process *new_process(int p) {
221 struct process *process;
222 process = wmtop_malloc(sizeof(struct process));
224 #if defined(PARANOID)
225 process->id = 0x0badfeed;
226 #endif /* defined(PARANOID) */
229 * Do stitching necessary for doubly linked list
231 process->name = 0;
232 process->previous = 0;
233 process->next = first_process;
234 if (process->next)
235 process->next->previous = process;
236 first_process = process;
238 process->pid = p;
239 process->time_stamp = 0;
240 process->previous_user_time = ULONG_MAX;
241 process->previous_kernel_time = ULONG_MAX;
242 process->counted = 1;
244 /* process_find_name(process);*/
246 return process;
249 /******************************************/
250 /* Functions */
251 /******************************************/
253 void wmtop_routine(int, char **);
254 int process_parse_procfs(struct process *);
255 int update_process_table(void);
256 int calculate_cpu(struct process *);
257 void process_cleanup(void);
258 void delete_process(struct process *);
259 void draw_processes(void);
260 unsigned long calc_cpu_total(void);
261 void calc_cpu_each(unsigned long total);
262 #if defined(LINUX)
263 unsigned long calc_mem_total(void);
264 void calc_mem_each(unsigned long total);
265 #endif
266 int process_find_top_three(struct process **);
267 void draw_bar(int, int, int, int, float, int, int);
268 void blit_string(char *, int, int);
269 void usage(void);
270 void printversion(void);
272 /******************************************/
273 /* Main */
274 /******************************************/
276 int main(int argc, char *argv[]) {
277 int i;
278 struct stat sbuf;
281 * Make sure we have a /proc filesystem. No point in continuing if we
282 * haven't!
284 if (stat("/proc",&sbuf)<0) {
285 fprintf(stderr,
286 "No /proc filesystem present. Unable to obtain processor info.\n");
287 exit(1);
291 * Parse Command Line
294 ProgName = argv[0];
295 if (strlen(ProgName) >= 5)
296 ProgName += strlen(ProgName) - 5;
298 for (i = 1; i<argc; i++) {
299 char *arg = argv[i];
301 if (*arg=='-') {
302 switch (arg[1]) {
303 case 'x' :
304 if (argc>i+1) {
305 static regex_t reg;
306 exclusion_expression = &reg;
307 regcomp(exclusion_expression,argv[i+1],REG_EXTENDED);
308 i++;
309 } else {
310 usage();
311 exit(1);
313 break;
314 case 'c' :
315 if (argc>i+1) {
316 process_command = argv[i+1];
317 i++;
318 break;
319 } else {
320 usage();
321 exit(1);
323 #if defined(LINUX)
324 case 'm':
326 * Display memory
328 mode = 1;
329 break;
330 #endif /* defined(LINUX) */
331 case 'd' :
332 if (strcmp(arg+1, "display")) {
333 usage();
334 exit(1);
336 break;
337 case 'g' :
338 if (strcmp(arg+1, "geometry")) {
339 usage();
340 exit(1);
342 break;
343 case 'v' :
344 printversion();
345 exit(0);
346 break;
347 case 'U' :
348 user = getuid();
349 break;
350 case 's':
351 if (argc > (i+1)) {
352 update_rate = (atoi(argv[i+1]) * 1000);
353 i++;
355 break;
356 case 'r':
357 if (argc > (i+1)) {
358 refresh_rate = (atoi(argv[i+1]) * 1000);
359 i++;
361 break;
362 case 'a':
363 if (argc > (i+1)) {
364 if (atoi(argv[i+1]) < 1 || atoi(argv[i+1]) > nstyles) {
365 usage();
366 exit(1);
368 style = atoi(argv[i+1]) - 1;
369 i++;
371 break;
372 default:
373 usage();
374 exit(0);
375 break;
380 wmtop_routine(argc, argv);
382 return 0;
385 /******************************************/
386 /* Main routine */
387 /******************************************/
389 void wmtop_routine(int argc, char **argv) {
390 XEvent Event;
391 struct timeval tv={0,0};
392 struct timeval last={0,0};
393 int count = update_rate;
395 createXBMfromXPM(wmtop_mask_bits, styles[style].pixmap, wmtop_mask_width, wmtop_mask_height);
397 openXwindow(argc, argv, styles[style].pixmap, wmtop_mask_bits, wmtop_mask_width, wmtop_mask_height);
400 while (1) {
402 waitpid(0, NULL, WNOHANG);
404 if (count>=update_rate) {
405 memcpy(&last,&tv,sizeof(tv));
408 * Update display
410 draw_processes();
412 RedrawWindow();
413 count = 0;
417 * X Events
419 while (XPending(display)) {
420 XNextEvent(display, &Event);
421 switch (Event.type) {
422 case Expose:
423 RedrawWindow();
424 break;
425 case DestroyNotify:
426 XCloseDisplay(display);
427 exit(0);
428 case ButtonPress:
429 #if defined(LINUX)
430 if (Event.xbutton.button==1)
431 mode = !mode;
432 #endif
433 if (Event.xbutton.button==2) {
434 if (user==(uid_t)-1)
435 user=getuid();
436 else
437 user=-1;
439 if (Event.xbutton.button==3 && process_command)
440 execCommand(process_command);
441 break;
444 usleep(refresh_rate);
445 count = count + refresh_rate;
449 /******************************************/
450 /* Extract information from /proc */
451 /******************************************/
454 * These are the guts that extract information out of /proc.
455 * Anyone hoping to port wmtop should look here first.
457 int process_parse_procfs(struct process *process) {
458 char line[WMTOP_BUFLENGTH],filename[WMTOP_BUFLENGTH],procname[WMTOP_BUFLENGTH];
459 int ps;
460 struct stat sbuf;
461 unsigned long user_time,kernel_time;
462 int rc;
463 #if defined(LINUX)
464 char *r,*q;
465 char deparenthesised_name[WMTOP_BUFLENGTH];
466 int endl;
467 #endif /* defined(LINUX) */
468 #if defined(FREEBSD)
469 /* TODO: needs analysis. Probably needs same data type fix as LINUX (use
470 * long types). Need to check FreeBSD docs and test. -wbk */
471 int us,um,ks,km;
472 #endif /* defined(FREEBSD) */
474 #if defined(PARANOID)
475 assert(process->id==0x0badfeed);
476 #endif /* defined(PARANOID) */
478 sprintf(filename,PROCFS_TEMPLATE,process->pid);
481 * Permissions of /proc filesystem are permissions of process too
483 if (user!=(uid_t)-1) {
484 stat(filename,&sbuf);
485 if (sbuf.st_uid!=user)
486 return 1;
489 ps = open(filename,O_RDONLY);
490 if (ps<0)
492 * The process must have finished in the last few jiffies!
494 return 1;
497 * Mark process as up-to-date.
499 process->time_stamp = g_time;
501 rc = read(ps,line,sizeof(line));
502 close(ps);
503 if (rc<0)
504 return 1;
506 #if defined(LINUX)
508 * Extract cpu times from data in /proc filesystem.
509 * For conversion types see man proc(5).
511 rc = sscanf(line,"%*s (%[^)]) %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lu %lu %*s %*s %*s %*s %*s %*s %*s %lu %ld",
512 procname,
513 &process->user_time,&process->kernel_time,
514 &process->vsize,&process->rss);
515 if (rc<5)
516 return 1;
517 r = procname;
518 /* remove any "kdeinit: " */
519 if (r == strstr(r, "kdeinit"))
521 sprintf(filename,PROCFS_CMDLINE_TEMPLATE,process->pid);
524 * Permissions of /proc filesystem are permissions of process too
526 if (user!=(uid_t)-1) {
527 stat(filename,&sbuf);
528 if (sbuf.st_uid!=user)
529 return 1;
532 ps = open(filename,O_RDONLY);
533 if (ps<0)
535 * The process must have finished in the last few jiffies!
537 return 1;
539 endl = read(ps,line,sizeof(line));
540 close(ps);
542 /* null terminate the input */
543 line[endl]=0;
544 /* account for "kdeinit: " */
545 if ((char*)line == strstr(line, "kdeinit: "))
546 r = ((char*)line)+9;
547 else
548 r = (char*)line;
550 q = deparenthesised_name;
551 /* stop at space */
552 while (*r && *r!=' ')
553 *q++ = *r++;
554 *q = 0;
556 else
558 q = deparenthesised_name;
559 while (*r)
560 *q++ = *r++;
561 *q = 0;
564 if (process->name)
565 wmtop_free(process->name);
566 process->name = wmtop_strdup(deparenthesised_name);
567 #endif /* defined(LINUX) */
569 #if defined(FREEBSD)
571 * Extract cpu times from data in /proc/<pid>/stat
572 * XXX: Process name extractor for FreeBSD is untested right now.
574 * [TODO: FREEBSD code probably needs similar data type changes to
575 * those made for LINUX above. Need to check docs. -wbk]
577 rc = sscanf(line,"%s %*s %*s %*s %*s %*s %*s %*s %d,%d %d,%d",
578 procname,
579 &us,&um,&ks,&km);
580 if (rc<5)
581 return 1;
582 if (process->name)
583 wmtop_free(process->name);
584 process->name = wmtop_strdup(procname);
585 process->user_time = us*1000+um/1000;
586 process->kernel_time = ks*1000+km/1000;
587 #endif /* defined(FREEBSD) */
589 /* not portable (especially unsuitable for redistributable executables.
590 * On some systems, getpagesize() is a preprocessor macro).
592 process->rss *= getpagesize();
594 if (process->previous_user_time==ULONG_MAX)
595 process->previous_user_time = process->user_time;
596 if (process->previous_kernel_time==ULONG_MAX)
597 process->previous_kernel_time = process->kernel_time;
599 user_time = process->user_time-process->previous_user_time;
600 kernel_time = process->kernel_time-process->previous_kernel_time;
602 process->previous_user_time = process->user_time;
603 process->previous_kernel_time = process->kernel_time;
605 process->user_time = user_time;
606 process->kernel_time = kernel_time;
608 return 0;
611 /******************************************/
612 /* Update process table */
613 /******************************************/
615 int update_process_table() {
616 DIR *dir;
617 struct dirent *entry;
619 if (!(dir = opendir("/proc")))
620 return 1;
623 * Get list of processes from /proc directory
625 while ((entry = readdir(dir))) {
626 pid_t pid;
628 if (!entry) {
630 * Problem reading list of processes
632 closedir(dir);
633 return 1;
636 if (sscanf(entry->d_name,"%d",&pid)>0) {
637 struct process *p;
638 p = find_process(pid);
639 if (!p)
640 p = new_process(pid);
642 calculate_cpu(p);
646 closedir(dir);
648 return 0;
651 /******************************************/
652 /* Get process structure for process pid */
653 /******************************************/
656 * This function seems to hog all of the CPU time. I can't figure out why - it
657 * doesn't do much.
659 int calculate_cpu(struct process *process) {
660 int rc;
662 #if defined(PARANOID)
663 assert(process->id==0x0badfeed);
664 #endif /* defined(PARANOID) */
666 rc = process_parse_procfs(process);
667 if (rc)
668 return 1;
671 * Check name against the exclusion list
673 if (process->counted && exclusion_expression && !regexec(exclusion_expression,process->name,0,0,0))
674 process->counted = 0;
676 return 0;
679 /******************************************/
680 /* Strip dead process entries */
681 /******************************************/
683 void process_cleanup() {
685 struct process *p = first_process;
686 while (p) {
687 struct process *current = p;
689 #if defined(PARANOID)
690 assert(p->id==0x0badfeed);
691 #endif /* defined(PARANOID) */
693 p = p->next;
695 * Delete processes that have died
697 if (current->time_stamp!=g_time)
698 delete_process(current);
702 /******************************************/
703 /* Destroy and remove a process */
704 /******************************************/
706 void delete_process(struct process *p) {
707 #if defined(PARANOID)
708 assert(p->id==0x0badfeed);
711 * Ensure that deleted processes aren't reused.
713 p->id = 0x007babe;
714 #endif /* defined(PARANOID) */
717 * Maintain doubly linked list.
719 if (p->next)
720 p->next->previous = p->previous;
721 if (p->previous)
722 p->previous->next = p->next;
723 else
724 first_process = p->next;
726 if (p->name)
727 wmtop_free(p->name);
728 wmtop_free(p);
731 /******************************************/
732 /* Generate display */
733 /******************************************/
735 void draw_processes() {
736 int i,n;
737 struct process *best[3] = { 0, 0, 0 };
738 unsigned long total;
741 * Invalidate time stamps
743 ++g_time;
745 update_process_table();
747 switch (mode) {
748 case 0:
749 total = calc_cpu_total();
750 calc_cpu_each(total);
751 break;
752 #if defined(LINUX)
753 case 1:
754 total = calc_mem_total();
755 calc_mem_each(total);
756 break;
757 #endif
760 process_cleanup();
763 * Find the top three!
765 n = process_find_top_three(best);
767 for (i = 0; i<3; ++i) {
768 int j;
769 char s[10];
770 strcpy(s," ");
771 if (i<n) {
772 for (j = 0; j<9; ++j) {
773 char c;
774 c = best[i]->name[j];
775 if (c)
776 s[j] = c;
777 else
778 break;
780 draw_bar(0, 97, 55, 6, best[i]->amount, 4, 13+i*20);
781 } else
782 draw_bar(0, 97, 55, 6, 0, 4, 13+i*20);
783 blit_string(s,4,4+i*20);
786 #if defined(DEBUG)
787 show_memory();
788 #endif
791 /******************************************/
792 /* Calculate cpu total */
793 /******************************************/
795 unsigned long calc_cpu_total() {
796 unsigned long total,t;
797 static unsigned long previous_total = ULONG_MAX;
798 #if defined(LINUX)
799 int rc;
800 int ps;
801 char line[WMTOP_BUFLENGTH];
802 unsigned long cpu,nice,system,idle;
804 ps = open("/proc/stat",O_RDONLY);
805 rc = read(ps,line,sizeof(line));
806 close(ps);
807 if (rc<0)
808 return 0;
809 sscanf(line,"%*s %lu %lu %lu %lu",&cpu,&nice,&system,&idle);
810 total = cpu+nice+system+idle;
811 #endif /* defined(LINUX) */
813 #if defined(FREEBSD)
814 struct timeval tv;
816 gettimeofday(&tv,0);
817 total = tv.tv_sec*1000+tv.tv_usec/1000;
818 #endif /* defined(FREEBSD) */
820 t = total-previous_total;
821 previous_total = total;
822 if (t<0)
823 t = 0;
825 return t;
828 /******************************************/
829 /* Calculate each processes cpu */
830 /******************************************/
832 void calc_cpu_each(unsigned long total) {
833 struct process *p = first_process;
834 while (p) {
836 #if defined(PARANOID)
837 assert(p->id==0x0badfeed);
838 #endif /* defined(PARANOID) */
840 p->amount = total ? 100*(float)(p->user_time+p->kernel_time)/total : 0;
841 p = p->next;
845 /******************************************/
846 /* Calculate total memory */
847 /******************************************/
849 #if defined(LINUX)
850 /* INT_MAX won't always hold total system RAM, especially on a 64 bit system. */
851 unsigned long calc_mem_total() {
852 int ps;
853 char line[1024];
854 char *ptr;
855 int rc;
857 ps = open("/proc/meminfo",O_RDONLY);
858 rc = read(ps,line,sizeof(line));
859 close(ps);
860 if (rc<0)
861 return 0;
863 if ((ptr = strstr(line, "Mem:")) != NULL) {
864 ptr += 4;
865 return atoi(ptr);
866 } else if ((ptr = strstr(line, "MemTotal:")) != NULL) {
867 /* The "Mem:" line has been removed in Linux 2.6 */
868 ptr += 9;
869 return atoi(ptr) << 10; /* MemTotal is given in kiB */
870 } else {
871 return 0;
874 #endif /* defined(LINUX) */
876 /******************************************/
877 /* Calculate each processes memory */
878 /******************************************/
880 #if defined(LINUX)
881 void calc_mem_each(unsigned long total) {
882 struct process *p = first_process;
883 while (p) {
884 p->amount = 100*(double)p->rss/total;
885 p = p->next;
888 #endif /* defined(LINUX) */
890 /******************************************/
891 /* Find the top three processes */
892 /******************************************/
895 * Result is stored in decreasing order in best[0-2].
897 int process_find_top_three(struct process **best) {
898 struct process *p = first_process;
899 int n = 0;
902 * Insertion sort approach to skim top 3
904 while (p) {
905 if (p->counted && p->amount>0 && (!best[0] || p->amount>best[0]->amount)) {
906 best[2] = best[1];
907 best[1] = best[0];
908 best[0] = p;
909 ++n;
910 } else if (p->counted && p->amount>0 && (!best[1] || p->amount>best[1]->amount)) {
911 best[2] = best[1];
912 best[1] = p;
913 ++n;
914 } else if (p->counted && p->amount>0 && (!best[2] || p->amount>best[2]->amount)) {
915 ++n;
916 best[2] = p;
919 p = p->next;
922 return n>3 ? 3 : n;
925 /******************************************/
926 /* Blit bar at co-ordinates */
927 /******************************************/
929 void draw_bar(int sx, int sy, int w, int h, float percent, int dx, int dy) {
930 int tx;
932 if (percent<=100)
933 tx = w * (float)percent / 100;
934 else
935 tx = w;
937 if (tx>0)
938 copyXPMArea(sx, sy, tx, h, dx, dy);
939 if (tx<w)
940 copyXPMArea(sx+tx, sy+h, w-tx, h, dx+tx, dy);
943 /******************************************/
944 /* Blit string at co-ordinates */
945 /******************************************/
947 void blit_string(char *name, int x, int y) {
948 int i;
949 int c;
950 int k;
952 k = x;
953 for ( i = 0; name[i]; i++) {
954 c = toupper(name[i]);
955 if (c >= 'A' && c <= 'J') {
956 c -= 'A';
957 copyXPMArea(c*6,73,6,7,k,y);
958 } else if (c>='K' && c<='T') {
959 c -= 'K';
960 copyXPMArea(c*6,81,6,7,k,y);
961 } else if (c>='U' && c<='Z') {
962 c -= 'U';
963 copyXPMArea(c*6,89,6,7,k,y);
964 } else if (c>='0' && c<='9') {
965 c -= '0';
966 copyXPMArea(c*6,65,6,7,k,y);
967 } else {
968 copyXPMArea(36,89,6,7,k,y);
970 k += 6;
974 /******************************************/
975 /* Usage */
976 /******************************************/
978 void usage(void) {
979 int i;
980 fprintf(stderr,"\nWMtop - Dan Piponi <dan@tanelorn.demon.co.uk> http://www.tanelorn.demon.co.uk\n\n");
981 fprintf(stderr,"usage:\n");
982 fprintf(stderr," -display <display name>\n");
983 fprintf(stderr," -geometry +XPOS+YPOS initial window position\n");
984 fprintf(stderr," -s <...> sample rate in milliseconds (default:%d)\n", update_rate/1000);
985 fprintf(stderr," -r <...> refresh rate in milliseconds (default:%d)\n", refresh_rate/1000);
986 fprintf(stderr," -U display user processes only\n");
987 fprintf(stderr," -x <...> exclude matching processes\n");
988 fprintf(stderr," -c <...> command\n");
989 #if defined(LINUX)
990 fprintf(stderr," -m display memory usage\n");
991 #endif /* defined(LINUX) */
992 fprintf(stderr," -v print version number\n");
993 fprintf(stderr," -a <1..%d> select artistic style\n", nstyles);
994 fprintf(stderr,"\n");
995 fprintf(stderr,"The artistic style is one of:\n");
996 for (i = 0; i<nstyles; ++i)
997 fprintf(stderr," %d - %s\n",i+1,styles[i].description);
1000 /******************************************/
1001 /* Print version */
1002 /******************************************/
1004 void printversion(void) {
1005 fprintf(stderr, "wmtop v%s\n",PACKAGE_VERSION);