1 /******************************************/
2 /* WMTOP - Mini top in a dock app */
3 /******************************************/
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.
20 #endif /* defined(FREEBSD) */
24 #endif /* defined(LINUX) */
26 #if !defined(OS_DEFINED)
27 #error No operating system selected
28 #endif /* !defined(OS_DEFINED) */
30 /******************************************/
32 /******************************************/
49 #endif /* defined(PARANOID) */
53 #include <sys/param.h>
54 #include <sys/types.h>
55 #include <sys/ioctl.h>
60 #include <X11/extensions/shape.h>
61 #include <X11/keysym.h>
65 #include "wmgeneral/wmgeneral.h"
66 #include "wmgeneral/misc.h"
67 #include "xpm/wmtop-default.xpm"
68 #include "xpm/wmtop-lcd.xpm"
69 #include "xpm/wmtop-neon1.xpm"
70 #include "xpm/wmtop-neon2.xpm"
71 #include "xpm/wmtop-rainbow.xpm"
73 /******************************************/
75 /******************************************/
77 #define WMTOP_VERSION "0.9"
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
86 #define PROCFS_TEMPLATE "/proc/%d/stat"
87 #define PROCFS_CMDLINE_TEMPLATE "/proc/%d/cmdline"
88 #endif /* defined(LINUX) */
91 #define PROCFS_TEMPLATE "/proc/%d/status"
92 #endif /* defined(FREEBSD) */
94 /******************************************/
96 /******************************************/
98 regex_t
*exclusion_expression
= 0;
100 char *process_command
= 0;
102 * Default mode: zero=cpu one=memory
107 * Number and default artistic styles.
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
;
123 /******************************************/
125 /******************************************/
133 void *wmtop_malloc(int n
) {
134 int *p
= (int *)malloc(sizeof(int)+n
);
137 return (void *)(p
+1);
140 void wmtop_free(void *n
) {
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 /******************************************/
160 /******************************************/
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" },
174 #if defined(PARANOID)
176 #endif /* defined(PARANOID) */
178 * Store processes in a doubly linked list
180 struct process
*next
;
181 struct process
*previous
;
188 int previous_user_time
;
189 int previous_kernel_time
;
196 /******************************************/
198 /******************************************/
201 * Global pointer to head of process list
203 struct process
*first_process
= 0;
207 struct process
*find_process(pid_t pid
) {
208 struct process
*p
= first_process
;
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
232 process
->previous
= 0;
233 process
->next
= first_process
;
235 process
->next
->previous
= process
;
236 first_process
= process
;
239 process
->time_stamp
= 0;
240 process
->previous_user_time
= INT_MAX
;
241 process
->previous_kernel_time
= INT_MAX
;
242 process
->counted
= 1;
244 /* process_find_name(process);*/
249 /******************************************/
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 inline void draw_processes(void);
260 int calc_cpu_total(void);
261 void calc_cpu_each(int);
263 int calc_mem_total(void);
264 void calc_mem_each(int);
266 int process_find_top_three(struct process
**);
267 void draw_bar(int, int, int, int, float, int, int);
268 inline void blit_string(char *, int, int);
270 inline void printversion(void);
272 /******************************************/
274 /******************************************/
276 int main(int argc
, char *argv
[]) {
281 * Make sure we have a /proc filesystem. No point in continuing if we
284 if (stat("/proc",&sbuf
)<0) {
286 "No /proc filesystem present. Unable to obtain processor info.\n");
295 if (strlen(ProgName
) >= 5)
296 ProgName
+= strlen(ProgName
) - 5;
298 for (i
= 1; i
<argc
; i
++) {
306 exclusion_expression
= ®
;
307 regcomp(exclusion_expression
,argv
[i
+1],REG_EXTENDED
);
316 process_command
= argv
[i
+1];
330 #endif /* defined(LINUX) */
332 if (strcmp(arg
+1, "display")) {
338 if (strcmp(arg
+1, "geometry")) {
352 update_rate
= (atoi(argv
[i
+1]) * 1000);
358 refresh_rate
= (atoi(argv
[i
+1]) * 1000);
364 if (atoi(argv
[i
+1]) < 1 || atoi(argv
[i
+1]) > nstyles
) {
368 style
= atoi(argv
[i
+1]) - 1;
380 wmtop_routine(argc
, argv
);
385 /******************************************/
387 /******************************************/
389 void wmtop_routine(int argc
, char **argv
) {
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
);
402 waitpid(0, NULL
, WNOHANG
);
404 if (count
>=update_rate
) {
405 memcpy(&last
,&tv
,sizeof(tv
));
419 while (XPending(display
)) {
420 XNextEvent(display
, &Event
);
421 switch (Event
.type
) {
426 XCloseDisplay(display
);
430 if (Event
.xbutton
.button
==1)
433 if (Event
.xbutton
.button
==2) {
439 if (Event
.xbutton
.button
==3 && process_command
)
440 execCommand(process_command
);
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
];
461 int user_time
,kernel_time
;
465 char deparenthesised_name
[WMTOP_BUFLENGTH
];
467 #endif /* defined(LINUX) */
470 #endif /* defined(FREEBSD) */
472 #if defined(PARANOID)
473 assert(process
->id
==0x0badfeed);
474 #endif /* defined(PARANOID) */
476 sprintf(filename
,PROCFS_TEMPLATE
,process
->pid
);
479 * Permissions of /proc filesystem are permissions of process too
482 stat(filename
,&sbuf
);
483 if (sbuf
.st_uid
!=user
)
487 ps
= open(filename
,O_RDONLY
);
490 * The process must have finished in the last few jiffies!
495 * Mark process as up-to-date.
497 process
->time_stamp
= g_time
;
499 rc
= read(ps
,line
,sizeof(line
));
506 * Extract cpu times from data in /proc filesystem
508 rc
= sscanf(line
,"%*s %s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %d %d %*s %*s %*s %*s %*s %*s %*s %d %d",
510 &process
->user_time
,&process
->kernel_time
,
511 &process
->vsize
,&process
->rss
);
515 * Remove parentheses from the process name stored in /proc/ under Linux...
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
527 stat(filename
,&sbuf
);
528 if (sbuf
.st_uid
!=user
)
532 ps
= open(filename
,O_RDONLY
);
535 * The process must have finished in the last few jiffies!
539 endl
= read(ps
,line
,sizeof(line
));
542 /* null terminate the input */
544 /* account for "kdeinit: " */
545 if ((char*)line
== strstr(line
, "kdeinit: "))
550 q
= deparenthesised_name
;
552 while (*r
&& *r
!=' ')
558 q
= deparenthesised_name
;
559 while (*r
&& *r
!=')')
565 wmtop_free(process
->name
);
566 process
->name
= wmtop_strdup(deparenthesised_name
);
567 #endif /* defined(LINUX) */
571 * Extract cpu times from data in /proc/<pid>/stat
572 * XXX: Process name extractor for FreeBSD is untested right now.
574 rc
= sscanf(line
,"%s %*s %*s %*s %*s %*s %*s %*s %d,%d %d,%d",
580 wmtop_free(process
->name
);
581 process
->name
= wmtop_strdup(procname
);
582 process
->user_time
= us
*1000+um
/1000;
583 process
->kernel_time
= ks
*1000+km
/1000;
584 #endif /* defined(FREEBSD) */
586 process
->rss
*= getpagesize();
588 if (process
->previous_user_time
==INT_MAX
)
589 process
->previous_user_time
= process
->user_time
;
590 if (process
->previous_kernel_time
==INT_MAX
)
591 process
->previous_kernel_time
= process
->kernel_time
;
593 user_time
= process
->user_time
-process
->previous_user_time
;
594 kernel_time
= process
->kernel_time
-process
->previous_kernel_time
;
596 process
->previous_user_time
= process
->user_time
;
597 process
->previous_kernel_time
= process
->kernel_time
;
599 process
->user_time
= user_time
;
600 process
->kernel_time
= kernel_time
;
605 /******************************************/
606 /* Update process table */
607 /******************************************/
609 int update_process_table() {
611 struct dirent
*entry
;
613 if (!(dir
= opendir("/proc")))
617 * Get list of processes from /proc directory
619 while ((entry
= readdir(dir
))) {
624 * Problem reading list of processes
630 if (sscanf(entry
->d_name
,"%d",&pid
)>0) {
632 p
= find_process(pid
);
634 p
= new_process(pid
);
645 /******************************************/
646 /* Get process structure for process pid */
647 /******************************************/
650 * This function seems to hog all of the CPU time. I can't figure out why - it
653 int calculate_cpu(struct process
*process
) {
656 #if defined(PARANOID)
657 assert(process
->id
==0x0badfeed);
658 #endif /* defined(PARANOID) */
660 rc
= process_parse_procfs(process
);
665 * Check name against the exclusion list
667 if (process
->counted
&& exclusion_expression
&& !regexec(exclusion_expression
,process
->name
,0,0,0))
668 process
->counted
= 0;
673 /******************************************/
674 /* Strip dead process entries */
675 /******************************************/
677 void process_cleanup() {
679 struct process
*p
= first_process
;
681 struct process
*current
= p
;
683 #if defined(PARANOID)
684 assert(p
->id
==0x0badfeed);
685 #endif /* defined(PARANOID) */
689 * Delete processes that have died
691 if (current
->time_stamp
!=g_time
)
692 delete_process(current
);
696 /******************************************/
697 /* Destroy and remove a process */
698 /******************************************/
700 void delete_process(struct process
*p
) {
701 #if defined(PARANOID)
702 assert(p
->id
==0x0badfeed);
705 * Ensure that deleted processes aren't reused.
708 #endif /* defined(PARANOID) */
711 * Maintain doubly linked list.
714 p
->next
->previous
= p
->previous
;
716 p
->previous
->next
= p
->next
;
718 first_process
= p
->next
;
725 /******************************************/
726 /* Generate display */
727 /******************************************/
729 void draw_processes() {
731 struct process
*best
[3] = { 0, 0, 0 };
735 * Invalidate time stamps
739 update_process_table();
743 total
= calc_cpu_total();
744 calc_cpu_each(total
);
748 total
= calc_mem_total();
749 calc_mem_each(total
);
757 * Find the top three!
759 n
= process_find_top_three(best
);
761 for (i
= 0; i
<3; ++i
) {
766 for (j
= 0; j
<9; ++j
) {
768 c
= best
[i
]->name
[j
];
774 draw_bar(0, 97, 55, 6, best
[i
]->amount
, 4, 13+i
*20);
776 draw_bar(0, 97, 55, 6, 0, 4, 13+i
*20);
777 blit_string(s
,4,4+i
*20);
785 /******************************************/
786 /* Calculate cpu total */
787 /******************************************/
789 int calc_cpu_total() {
791 static int previous_total
= INT_MAX
;
795 char line
[WMTOP_BUFLENGTH
];
796 int cpu
,nice
,system
,idle
;
798 ps
= open("/proc/stat",O_RDONLY
);
799 rc
= read(ps
,line
,sizeof(line
));
803 sscanf(line
,"%*s %d %d %d %d",&cpu
,&nice
,&system
,&idle
);
804 total
= cpu
+nice
+system
+idle
;
805 #endif /* defined(LINUX) */
811 total
= tv
.tv_sec
*1000+tv
.tv_usec
/1000;
812 #endif /* defined(FREEBSD) */
814 t
= total
-previous_total
;
815 previous_total
= total
;
822 /******************************************/
823 /* Calculate each processes cpu */
824 /******************************************/
826 void calc_cpu_each(int total
) {
827 struct process
*p
= first_process
;
830 #if defined(PARANOID)
831 assert(p
->id
==0x0badfeed);
832 #endif /* defined(PARANOID) */
834 p
->amount
= total
? 100*(float)(p
->user_time
+p
->kernel_time
)/total
: 0;
839 /******************************************/
840 /* Calculate total memory */
841 /******************************************/
844 int calc_mem_total() {
850 ps
= open("/proc/meminfo",O_RDONLY
);
851 rc
= read(ps
,line
,sizeof(line
));
856 if ((ptr
= strstr(line
, "Mem:")) == NULL
) {
864 #endif /* defined(LINUX) */
866 /******************************************/
867 /* Calculate each processes memory */
868 /******************************************/
871 void calc_mem_each(int total
) {
872 struct process
*p
= first_process
;
874 p
->amount
= 100*(float)p
->rss
/total
;
878 #endif /* defined(LINUX) */
880 /******************************************/
881 /* Find the top three processes */
882 /******************************************/
885 * Result is stored in decreasing order in best[0-2].
887 int process_find_top_three(struct process
**best
) {
888 struct process
*p
= first_process
;
892 * Insertion sort approach to skim top 3
895 if (p
->counted
&& p
->amount
>0 && (!best
[0] || p
->amount
>best
[0]->amount
)) {
900 } else if (p
->counted
&& p
->amount
>0 && (!best
[1] || p
->amount
>best
[1]->amount
)) {
904 } else if (p
->counted
&& p
->amount
>0 && (!best
[2] || p
->amount
>best
[2]->amount
)) {
915 /******************************************/
916 /* Blit bar at co-ordinates */
917 /******************************************/
919 void draw_bar(int sx
, int sy
, int w
, int h
, float percent
, int dx
, int dy
) {
923 tx
= w
* (float)percent
/ 100;
928 copyXPMArea(sx
, sy
, tx
, h
, dx
, dy
);
930 copyXPMArea(sx
+tx
, sy
+h
, w
-tx
, h
, dx
+tx
, dy
);
933 /******************************************/
934 /* Blit string at co-ordinates */
935 /******************************************/
937 void blit_string(char *name
, int x
, int y
) {
943 for ( i
= 0; name
[i
]; i
++) {
944 c
= toupper(name
[i
]);
945 if (c
>= 'A' && c
<= 'J') {
947 copyXPMArea(c
*6,73,6,7,k
,y
);
948 } else if (c
>='K' && c
<='T') {
950 copyXPMArea(c
*6,81,6,7,k
,y
);
951 } else if (c
>='U' && c
<='Z') {
953 copyXPMArea(c
*6,89,6,7,k
,y
);
954 } else if (c
>='0' && c
<='9') {
956 copyXPMArea(c
*6,65,6,7,k
,y
);
958 copyXPMArea(36,89,6,7,k
,y
);
964 /******************************************/
966 /******************************************/
970 fprintf(stderr
,"\nWMtop - Dan Piponi <dan@tanelorn.demon.co.uk> http://www.tanelorn.demon.co.uk\n\n");
971 fprintf(stderr
,"usage:\n");
972 fprintf(stderr
," -display <display name>\n");
973 fprintf(stderr
," -geometry +XPOS+YPOS initial window position\n");
974 fprintf(stderr
," -s <...> sample rate in milliseconds (default:%d)\n", update_rate
/1000);
975 fprintf(stderr
," -r <...> refresh rate in milliseconds (default:%d)\n", refresh_rate
/1000);
976 fprintf(stderr
," -U display user processes only\n");
977 fprintf(stderr
," -x <...> exclude matching processes\n");
978 fprintf(stderr
," -c <...> command\n");
980 fprintf(stderr
," -m display memory usage\n");
981 #endif /* defined(LINUX) */
982 fprintf(stderr
," -v print version number\n");
983 fprintf(stderr
," -a <1..%d> select artistic style\n", nstyles
);
984 fprintf(stderr
,"\n");
985 fprintf(stderr
,"The artistic style is one of:\n");
986 for (i
= 0; i
<nstyles
; ++i
)
987 fprintf(stderr
," %d - %s\n",i
+1,styles
[i
].description
);
990 /******************************************/
992 /******************************************/
994 void printversion(void) {
995 fprintf(stderr
, "wmtop v%s\n",WMTOP_VERSION
);