1 /* wmnet -- X IP accounting monitor
2 * Copyright 1998 Jesse B. Off
3 * Copyright 2000 Katharine Osborne
5 * $Id: wmnet.c,v 1.28 1998/10/07 03:42:28 joff Exp joff $
7 * This software is released under the GNU Public License agreement.
8 * No warranties, whatever.... you know the usuals.... this is free
9 * software. if you use it, great... if you wanna make a change to it,
10 * great, but please send me the diff.
13 * 3/24/1998 -- First release, wrote this yesterday, so it may have
15 * 3/25/1998 -- added logarithmic scaling.
16 * some touch ups on the updateSpeedometer() to be a
18 * added a little more width to the speedometer display
19 * from a report that the text sometimes was drawn off
20 * the scale (couldnt confirm, is it fixed?)
21 * fixed prob for speeds > 9.9 megabytes per second (I
22 * think... I can't go that fast myself
23 * Made default maxspeed 6000 which I assume is better
25 * 5/11/1998 -- I witnessed the little problem with the speedometer
26 * going off into the main window so I set a clipmask
27 * Still don't understand how it could go off..I'm
28 * using a fixed size font...
29 * 5/13/1998 -- Modified the way it gets its stats from /proc/net/ip_acct
30 * You now have to give it explicit accounting rule numbers
31 * through the -T and -R options or else it defaults to the
32 * fist two in tx, rx order, best just to make sure
33 * you set the -T and -R options.
34 * 5/15/1998 -- Completed interface rewrite, definitely have more graphing
35 * space, but I dont know if I like it yet.
36 * 5/16/1998 -- Added shaded graphs, xload style. Its a lot prettier!
37 * 5/20/1998 -- Fixed afterstep wharfability problem... should also atleast
38 * display in other WM's
39 * 6/16/1998 -- Fixed handling of WM_DELETE_WINDOW, strtol() to strtoul(),
40 * and now uses getopt_long.
41 * 6/16/1998 -- Put in --withdrawn and --normalstate options to explicitly
42 * set the type. Still tries to auto-detect wmaker though.
43 * 6/16/1998 -- Put in --execute and --promisc options.
44 * 6/17/1998 -- Some code clean-ups.
45 * 6/18/1998 -- Implemented a labeling mechanism.
46 * 6/18/1998 -- Slowed down the speedometer display... it was getting annoying.
47 * 6/23/1998 -- Split up to wmnet.c and wmnet.h
48 * 8/5/1998 -- New options --device and --driver
49 * 5/4/2000 -- Support added for OpenBSD
58 #if defined (__FreeBSD__) || defined (__OpenBSD__)
59 # include<sys/socket.h>
70 #include<sys/socket.h>
74 #include<X11/extensions/shape.h>
76 #include "XPM/arrow.xbm"
80 /* Called on exit() from atexit() */
81 void exit_func(void) {
85 /* Generic signal handler, if its a SIGCHLD, do a wait() to remove the zombie */
86 void got_signal(int x
) {
95 /* Does generic setting up of wmnet, (option parsing) and calls setupX() */
96 void setup_wmnet(int argc
, char **argv
) {
99 struct sigaction signal_action
;
100 char *txcolorString
= NULL
, *rxcolorString
= NULL
, *labelfgcolorString
= NULL
, *labelbgcolorString
= NULL
;
103 const struct option long_options
[19] = {
105 const struct option long_options
[17] = {
107 {"device", required_argument
, NULL
, 'W'},
108 {"label", required_argument
, NULL
, 'L'},
109 {"labelfg", required_argument
, NULL
, 'F'},
110 {"labelbg", required_argument
, NULL
, 'B'},
111 {"logscale", no_argument
, NULL
, 'l'},
112 {"help", no_argument
, NULL
, 'h'},
113 {"execute", required_argument
, NULL
, 'e'},
115 {"txrule", required_argument
, NULL
, 'T'},
116 {"rxrule", required_argument
, NULL
, 'R'},
118 {"txcolor", required_argument
, NULL
, 't'},
119 {"rxcolor", required_argument
, NULL
, 'r'},
120 {"maxrate", required_argument
, NULL
, 'x'},
121 {"withdrawn", no_argument
, NULL
, 'w'},
122 {"normalstate", no_argument
, NULL
, 'n'},
123 {"promisc", required_argument
, NULL
, 'p'},
124 {"unpromisc", required_argument
, NULL
, 'u'},
125 {"driver", required_argument
, NULL
, 'D'},
126 {"version", no_argument
, NULL
, 'v'},
134 while((c
= getopt_long(argc
, argv
, "W:F:B:L:vp:u:wnle:R:T:r:t:D:d:x:h", long_options
, NULL
)) != EOF
) {
136 while((c
= getopt_long(argc
, argv
, "W:F:B:L:vp:u:wnle:r:t:D:d:x:h", long_options
, NULL
)) != EOF
) {
140 printf("wmnet 1.06\n"
141 "Copyright (C) 1998, 2000 Jesse B. Off, Katharine Osborne <kaos@digitalkaos.net>\n"
142 "This program is released under the terms of the GNU Public License.\n");
146 device
= strdup(optarg
);
149 parser
= strdup(optarg
);
153 out_rule
= strtoul(optarg
, NULL
, 10);
154 out_rule_string
= strdup(optarg
);
157 in_rule
= strtoul(optarg
, NULL
, 10);
158 in_rule_string
= strdup(optarg
);
162 graphbox_height
= 35;
164 label
= strdup(optarg
);
166 fprintf(stderr
, "wmnet: duplicate --label\n");
171 if (labelbgcolorString
== NULL
) {
172 labelbgcolorString
= (char *)alloca(strlen(optarg
)+1);
173 strncpy(labelbgcolorString
, optarg
, strlen(optarg
)+1);
175 fprintf(stderr
, "wmnet: duplicate --labelbg\n");
180 if (labelfgcolorString
== NULL
) {
181 labelfgcolorString
= (char *)alloca(strlen(optarg
)+1);
182 strncpy(labelfgcolorString
, optarg
, strlen(optarg
)+1);
184 fprintf(stderr
, "wmnet: duplicate --labelfg\n");
192 if (rxcolorString
== NULL
) {
193 rxcolorString
= (char *)alloca(strlen(optarg
)+1);
194 strncpy(rxcolorString
, optarg
, strlen(optarg
)+1);
196 fprintf(stderr
, "wmnet: duplicate --rxcolor\n");
201 if (txcolorString
== NULL
) {
202 txcolorString
= (char *)alloca(strlen(optarg
)+1);
203 strncpy(txcolorString
, optarg
, strlen(optarg
)+1);
205 fprintf(stderr
, "wmnet: duplicate --rxcolor\n");
210 maxRate
= strtoul(optarg
, NULL
, 10);
213 delayTime
= strtoul(optarg
, NULL
, 10);
214 /* Having delayTime > 950000 causes some weirdness */
215 delayTime
= delayTime
> 950000 ? 950000 : delayTime
;
218 if (click_command
== NULL
) {
219 click_command
= strdup(optarg
);
221 fprintf(stderr
, "wmnet: duplicate --execute\n");
226 specified_state
= WithdrawnState
;
229 specified_state
= NormalState
;
235 strncpy(ifr
.ifr_name
, optarg
, IFNAMSIZ
);
236 ifr
.ifr_name
[IFNAMSIZ
-1] = 0;
237 if ((fds
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1 || ioctl(fds
, SIOCGIFFLAGS
, &ifr
) == -1 ) {
241 if ((ifr
.ifr_flags
& IFF_PROMISC
) != 0) { /* Is promiscuous mode not already unset? */
242 ifr
.ifr_flags
&= ~IFF_PROMISC
;
243 if (geteuid() != 0) {
244 fprintf(stderr
, "wmnet: this must be suid or you must be root!\n");
246 if(ioctl(fds
, SIOCSIFFLAGS
, &ifr
) != 0) {
247 fprintf(stderr
, "wmnet: cannot unset promiscuous mode on %s\n", optarg
);
258 strncpy(ifr
.ifr_name
, optarg
, IFNAMSIZ
);
259 ifr
.ifr_name
[IFNAMSIZ
-1] = 0;
260 if ((fds
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1 || ioctl(fds
, SIOCGIFFLAGS
, &ifr
) == -1 ) {
264 if ((ifr
.ifr_flags
& IFF_PROMISC
) == 0) { /* Is promiscuous mode not already set? */
265 ifr
.ifr_flags
|= IFF_PROMISC
;
266 if (geteuid() != 0) {
267 fprintf(stderr
, "wmnet: this must be suid or you must be root!\n");
269 if(ioctl(fds
, SIOCSIFFLAGS
, &ifr
) != 0) {
270 fprintf(stderr
, "wmnet: cannot set promiscuous mode on %s\n", optarg
);
278 printf("wmnet-- v1.06 Katharine Osborne <kaos@digitalkaos.net>\n"
279 "http://www.digitalkaos.net/linux/wmnet/\n"
280 "-----------------------------------------------------\n"
281 " -h, --help this help\n"
282 " -v, --version display version information\n"
283 " -L, --label=LABEL display LABEL on bottom of window\n"
284 " -F, --labelfg=COLOR foreground color for the label\n"
285 " -B, --labelbg=COLOR background color for the label\n"
286 " -e, --execute=COMMAND run COMMAND on click\n"
288 " -T, --txrule=RULE accounting rule number (ipfwadm) or\n"
289 " IP chain name (ipchains) to monitor for tx\n"
290 " -R, --rxrule=RULE accounting rule number (ipfwadm) or\n"
291 " IP chain name (ipchains) to monitor for rx\n"
293 " -W, --device=DEVICE monitor DEVICE for stats (devstats,kmem,pppstats)\n"
294 " -w, --withdrawn start up in withdrawn state\n"
295 " -n, --normalstate start up in normal, shaped state\n"
296 " -t, --txcolor=COLOR color for tx\n"
297 " -r, --rxcolor=COLOR color for rx\n"
298 " -x, --maxrate=BYTES max transfer rate for graph scale (default 120000)\n"
299 " -p, --promisc=DEVICE put DEVICE into promiscuous mode to apply\n"
300 " accounting rules to all network packets\n"
301 " -u, --unpromisc=DEVICE turn off promiscuous mode on DEVICE\n"
302 " -D, --driver=DRIVER use DRIVER to get statistics\n"
303 " -l, --logscale use a logarithmic scale (great for ethernet\n"
304 " connections with -x 10000000)\n"
305 " -d DELAY delay time for polling statistics\n"
306 " in microseconds (default 100000)\n"
308 printf("Compiled in drivers: [%s]\n\n", available_drivers());
309 printf("Report bugs to joff@iastate.edu\n");
313 /* Relinquish suid privileges if there */
316 stat_gather
= setup_driver(parser
);
319 if (txcolorString
== NULL
) txcolorString
= "white";
320 if (rxcolorString
== NULL
) rxcolorString
= "red";
321 if (labelfgcolorString
== NULL
) labelfgcolorString
= "white";
322 if (labelbgcolorString
== NULL
) labelbgcolorString
= "black";
324 /* Change dir to /, security precaution, and common courtesy */
325 if (chdir("/") == -1) {
326 perror("wmnet: chdir()");
331 if ((dpy
= XOpenDisplay(NULL
)) == NULL
) {
332 fprintf(stderr
,"wmnet: doh...can't connect to X server, giving up\n");
336 /* assure ourself for a graceful exit */
337 if (atexit(exit_func
)) {
338 fprintf(stderr
,"wmnet: atexit() failed\n");
342 signal_action
.sa_handler
= got_signal
;
343 sigemptyset(&signal_action
.sa_mask
);
344 signal_action
.sa_flags
= (SA_NOCLDSTOP
|SA_RESTART
);
346 signal_action
.sa_restorer
= NULL
;
348 if ((sigaction(SIGCHLD
, &signal_action
, NULL
) == -1) ||
349 (sigaction(SIGINT
, &signal_action
, NULL
) == -1) ||
350 (sigaction(SIGTERM
, &signal_action
, NULL
) == -1)) {
351 fprintf(stderr
,"wmnet: couldn't set signal handler\n");
356 /* Setup initial foreground color */
358 if(!XParseColor(dpy
, DefaultColormap(dpy
, screen
), rxcolorString
, &thecolor
)) {
359 fprintf(stderr
, "wmnet: what the heck is %s for a color?\n", rxcolorString
);
362 shadesOf(&thecolor
, rx_pixel
);
365 if(!XParseColor(dpy
, DefaultColormap(dpy
, screen
), txcolorString
, &thecolor
)) {
366 fprintf(stderr
, "wmnet: what the heck is %s for a color?\n", txcolorString
);
369 shadesOf(&thecolor
, tx_pixel
);
371 if(labelfgcolorString
) {
372 if(!XParseColor(dpy
, DefaultColormap(dpy
, screen
), labelfgcolorString
, &thecolor
)) {
373 fprintf(stderr
, "wmnet: what the heck is %s for a color?\n", labelfgcolorString
);
376 XAllocColor(dpy
, DefaultColormap(dpy
, screen
), &thecolor
);
377 labelfg_pixel
= thecolor
.pixel
;
379 if(labelbgcolorString
) {
380 if(!XParseColor(dpy
, DefaultColormap(dpy
, screen
), labelbgcolorString
, &thecolor
)) {
381 fprintf(stderr
, "wmnet: what the heck is %s for a color?\n", labelbgcolorString
);
384 XAllocColor(dpy
, DefaultColormap(dpy
, screen
), &thecolor
);
385 labelbg_pixel
= thecolor
.pixel
;
388 /* usleep() in between polls to /proc/net/ip_acct */
389 if (delayTime
<= 0) delayTime
= 25000;
391 /* Setup the X windows, GC's, initial states, etc */
393 XSetCommand(dpy
, main_window
, argv
, argc
);
395 /* Get the initial stats for startup */
400 XMapWindow(dpy
, main_window
);
404 /* Called from setup_wmnet() to initialize the X windows stuff */
411 XRectangle bound
= { 0, 0, 56, 56 };
414 screen
= DefaultScreen(dpy
);
416 delete_atom
= XInternAtom(dpy
, "WM_DELETE_WINDOW", False
);
417 if (delete_atom
== None
) {
418 fprintf(stderr
,"wmnet: I need WindowMaker running! (or at least some window manager)\n");
421 if (XInternAtom(dpy
,"_WINDOWMAKER_WM_FUNCTION", True
) != None
) {
422 if (specified_state
== -1) specified_state
= WithdrawnState
;
424 if (specified_state
== -1) specified_state
= NormalState
;
427 root_window
= DefaultRootWindow(dpy
);
428 createWin(&main_window
);
430 color
.red
= color
.green
= color
.blue
= 12000;
431 XAllocColor(dpy
, DefaultColormap(dpy
, screen
), &color
);
432 darkgrey_pixel
= color
.pixel
;
434 color
.red
= color
.green
= color
.blue
= 32000;
435 XAllocColor(dpy
, DefaultColormap(dpy
, screen
), &color
);
436 grey_pixel
= color
.pixel
;
439 if ((arrow
= XCreateBitmapFromData(dpy
, root_window
, arrow_bits
, arrow_width
, arrow_height
)) == None
) {
440 fprintf(stderr
, "wmnet: unable to create arrow bitmap\n");
443 gcv
.graphics_exposures
= False
;
444 gcv
.foreground
= tx_pixel
[HIGH_INTENSITY
];
445 gcv
.background
= darkgrey_pixel
;
446 gcv
.font
= XLoadFont(dpy
, "5x8");
447 graphics_context
= XCreateGC(dpy
, root_window
, (GCFont
|GCGraphicsExposures
|GCForeground
|GCBackground
), &gcv
);
448 black_pixel
= BlackPixel(dpy
, screen
);
449 white_pixel
= WhitePixel(dpy
, screen
);
451 hints
.window_group
= main_window
;
452 hints
.initial_state
= specified_state
;
453 if (specified_state
== WithdrawnState
) {
454 createWin(&icon_window
);
455 visible_window
= &icon_window
;
456 hints
.icon_window
= icon_window
;
458 visible_window
= &main_window
;
459 hints
.icon_window
= None
;
461 hints
.flags
= WindowGroupHint
| StateHint
| IconWindowHint
;
462 XSetWMHints(dpy
,main_window
,&hints
);
463 XSetWMProtocols(dpy
, main_window
, &delete_atom
, 1);
465 shints
.min_width
= 64;
466 shints
.min_height
= 64;
467 shints
.max_width
= 64;
468 shints
.max_height
= 64;
469 shints
.flags
= PMinSize
| PMaxSize
;
470 XSetWMNormalHints(dpy
, main_window
, &shints
);
473 XStoreName(dpy
, main_window
, "wmnet");
474 XShapeCombineRectangles(dpy
, *visible_window
, ShapeBounding
, 4, 4, &bound
, 1, ShapeBounding
, 0);
475 XSelectInput(dpy
, *visible_window
, (ExposureMask
|ButtonPressMask
));
476 XMapSubwindows(dpy
, *visible_window
);
480 /* Utility function to create a window for setupX() */
481 void createWin(Window
*win
) {
482 XClassHint classHint
;
483 XSetWindowAttributes windowAttrib
;
484 *win
= XCreateSimpleWindow(dpy
, root_window
, 10, 10, 64, 64, 0, 0, 0);
485 classHint
.res_name
= "wmnet";
486 classHint
.res_class
= "WMNET";
487 windowAttrib
.background_pixmap
= ParentRelative
;
488 XChangeWindowAttributes(dpy
, *win
, CWBackPixmap
, &windowAttrib
);
489 XSetClassHint(dpy
, *win
, &classHint
);
494 /* Handles Expose events, repaints the window */
495 void redraw(XExposeEvent
*ee
) {
496 static XRectangle cliprect
= { 4, 51, 56, 9 };
497 XSetForeground(dpy
, graphics_context
, darkgrey_pixel
);
498 /* if (wmaker_present == False) XFillRectangle(dpy, *visible_window, graphics_context, 0, 0, 64, 64); */
499 XFillRectangle(dpy
, *visible_window
, graphics_context
, GRAPHBOX_X
, GRAPHBOX_Y
, GRAPHBOX_WIDTH
, GRAPHBOX_HEIGHT
);
502 XSetForeground(dpy
, graphics_context
, black_pixel
);
503 XFillRectangle(dpy
, *visible_window
, graphics_context
, TOPBOX_X
, TOPBOX_Y
, TOPBOX_WIDTH
, TOPBOX_HEIGHT
);
504 XDrawLine(dpy
, *visible_window
, graphics_context
, GRAPHBOX_X_LEFT
, GRAPHBOX_Y_TOP
, GRAPHBOX_X_LEFT
, GRAPHBOX_Y_BOTTOM
);
505 XDrawPoint(dpy
, *visible_window
, graphics_context
, GRAPHBOX_X_RIGHT
, GRAPHBOX_Y_TOP
);
507 XSetForeground(dpy
, graphics_context
, labelbg_pixel
);
508 XFillRectangle(dpy
, *visible_window
, graphics_context
, LABEL_X
, LABEL_Y
, LABEL_WIDTH
, LABEL_HEIGHT
);
509 XSetClipRectangles(dpy
, graphics_context
, 0, 0, &cliprect
, 1, Unsorted
);
510 XSetForeground(dpy
, graphics_context
, labelfg_pixel
);
511 XDrawString(dpy
, *visible_window
, graphics_context
, 5, 58, label
, strlen(label
));
512 XSetClipMask(dpy
, graphics_context
, None
);
516 XSetForeground(dpy
, graphics_context
, white_pixel
);
517 XDrawLine(dpy
, *visible_window
, graphics_context
, GRAPHBOX_X_RIGHT
, GRAPHBOX_Y_BOTTOM
, GRAPHBOX_X_RIGHT
, (GRAPHBOX_Y_TOP
+ 1));
518 XDrawLine(dpy
, *visible_window
, graphics_context
, GRAPHBOX_X_LEFT
, GRAPHBOX_Y_BOTTOM
, GRAPHBOX_X_RIGHT
, GRAPHBOX_Y_BOTTOM
);
521 XSetForeground(dpy
, graphics_context
, grey_pixel
);
522 XSetBackground(dpy
, graphics_context
, black_pixel
);
523 XCopyPlane(dpy
, arrow
, *visible_window
, graphics_context
, 7, 0, 7, 9, 53, 5, 1);
524 XCopyPlane(dpy
, arrow
, *visible_window
, graphics_context
, 0, 0, 7, 9, 46, 5, 1);
528 /* Main loop that is called every delaytime. This calls stat_gather() and updateSpeedometer() when needed
529 * and takes care of the displaying and scrolling the graph */
533 unsigned long rate_rx
, rate_tx
;
534 double percent_tx
, percent_rx
;
535 /* static array containing the last 8 samples... for use in averaging and smoothing the graph a little */
536 static unsigned long lifo_in
[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
537 static unsigned long lifo_out
[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
538 static unsigned int t
= 0, blank
= 0;
540 if (gettimeofday(&timenow
, NULL
)) {
541 perror("wmnet: gettimeofday()");
544 since
= (timenow
.tv_sec
* 1000000L + timenow
.tv_usec
) - (timelast
.tv_sec
* 1000000L + timelast
.tv_usec
);
545 if (since
> displayDelay
) {
546 lifo_in
[t
] = diffbytes_in
* (1000000L / since
);
547 lifo_out
[t
] = diffbytes_out
* (1000000L / since
);
553 rate_rx
= (lifo_in
[0] + lifo_in
[1] + lifo_in
[2] + lifo_in
[3] + lifo_in
[4] + lifo_in
[5] + lifo_in
[6] + lifo_in
[7]) / (unsigned long)8;
554 if(logscale
) percent_rx
= (log10( ((rate_rx
* 10000/ maxRate
) < 1) ? 1 : ((double)rate_rx
/ (double)maxRate
) * 10000.) / 4.);
555 else percent_rx
= (double)(rate_rx
) / maxRate
;
556 y
= GRAPH_Y_BOTTOM
- (GRAPH_HEIGHT
* percent_rx
) ;
557 y
= y
< GRAPH_Y_UPPER
? GRAPH_Y_UPPER
: y
;
560 rate_tx
= (lifo_out
[0] + lifo_out
[1] + lifo_out
[2] + lifo_out
[3] + lifo_out
[4] + lifo_out
[5] + lifo_out
[6] + lifo_out
[7]) / (unsigned long)8;
561 if(logscale
) percent_tx
= (log10( ((rate_tx
* 10000 / maxRate
) < 1) ? 1 : ((double)rate_tx
/ (double)maxRate
* 10000.)) / 4.);
562 else percent_tx
= (double)(rate_tx
) / maxRate
;
563 yy
= GRAPH_Y_UPPER
+ (GRAPH_HEIGHT
* percent_tx
) ;
564 yy
= yy
> GRAPH_Y_BOTTOM
? GRAPH_Y_BOTTOM
: yy
;
567 /* only update the speedometer every 7th displayDelay */
568 if (t
== 7) updateSpeedometer(rate_rx
, rate_tx
);
570 /* blank var is just for stopping executing the X* funcs when the disp is all black */
571 if ((y
== GRAPH_Y_BOTTOM
&& yy
== GRAPH_Y_UPPER
) && (diffbytes_in
+ diffbytes_out
) == 0) blank
++; else blank
= 0;
572 if (blank
< (GRAPH_WIDTH
+ 1) ) {
573 XCopyArea(dpy
, *visible_window
, *visible_window
, graphics_context
, GRAPH_X
+ 1,
574 GRAPH_Y
, GRAPH_WIDTH
- 1, GRAPH_HEIGHT
, GRAPH_X
, GRAPH_Y
);
575 XSetForeground(dpy
, graphics_context
, darkgrey_pixel
);
576 XDrawLine(dpy
, *visible_window
, graphics_context
, GRAPH_X_RIGHT
, y
, GRAPH_X_RIGHT
, yy
);
577 if (( (yy
== GRAPH_Y_UPPER
&& diffbytes_out
> 0 && rate_rx
> rate_tx
) || (rate_rx
>= rate_tx
&& yy
!= GRAPH_Y_UPPER
)) ) {
578 drawColoredLine(GRAPH_Y_UPPER
, yy
, tx_pixel
);
580 if ( y
!= GRAPH_Y_BOTTOM
|| diffbytes_in
> 0) {
581 drawColoredLine(GRAPH_Y_BOTTOM
, y
, rx_pixel
);
583 if (( (yy
== GRAPH_Y_UPPER
&& diffbytes_out
> 0) || (rate_rx
< rate_tx
&& yy
!= GRAPH_Y_UPPER
)) ) {
584 drawColoredLine(GRAPH_Y_UPPER
, yy
, tx_pixel
);
589 diffbytes_in
= diffbytes_out
= 0;
593 if (!stat_gather()) { /* Anything change? */
596 XSetBackground(dpy
, graphics_context
, black_pixel
);
597 if(current_tx
== True
) {
598 XSetForeground(dpy
, graphics_context
, tx_pixel
[HIGH_INTENSITY
]);
599 XCopyPlane(dpy
, arrow
, *visible_window
, graphics_context
, 7, 0, 7, 9, 53, 5, 1);
600 /* XFillRectangle(dpy, *visible_window, graphics_context, 55, 5, 4, 4); */
603 XSetForeground(dpy
, graphics_context
, grey_pixel
);
604 XCopyPlane(dpy
, arrow
, *visible_window
, graphics_context
, 7, 0, 7, 9, 53, 5, 1);
606 if(current_rx
== True
) {
607 XSetForeground(dpy
, graphics_context
, rx_pixel
[HIGH_INTENSITY
]);
608 XCopyPlane(dpy
, arrow
, *visible_window
, graphics_context
, 0, 0, 7, 9, 46, 5, 1);
609 /* XFillRectangle(dpy, *visible_window, graphics_context, 55, 12, 4, 4); */
612 XSetForeground(dpy
, graphics_context
, grey_pixel
);
613 XCopyPlane(dpy
, arrow
, *visible_window
, graphics_context
, 0, 0, 7, 9, 46, 5, 1);
619 /* paints the speedometer area with whichever is greater, rxRate or txRate */
620 int updateSpeedometer(int rxRate
, int txRate
) {
624 static XRectangle cliprect
= { 4, 5, 37, 8 };
625 static int rxRate_last
= 0 , txRate_last
= 0;
626 static Bool clear
= True
, collectandreturn
= True
;
628 /* This is ugly, I don't like this, but it slows the speedometer down a touch */
629 if (collectandreturn
== True
) {
630 txRate_last
= txRate
;
631 rxRate_last
= rxRate
;
632 collectandreturn
= False
;
635 collectandreturn
= True
;
639 if (txRate
> rxRate
) {
640 rate
= (txRate
+ txRate_last
) / 2000.;
641 color
= tx_pixel
[HIGH_INTENSITY
];
643 rate
= (rxRate
+ rxRate_last
) / 2000.;
644 color
= rx_pixel
[HIGH_INTENSITY
];
649 XSetForeground(dpy
, graphics_context
, black_pixel
);
650 XFillRectangle(dpy
, *visible_window
, graphics_context
, 4, 5, 37, 9);
655 } else if (rate
< 1.) {
656 snprintf(astring
, 10, "%db/s", (unsigned int)(rate
* 1000.));
657 } else if (rate
>= 1. && rate
< 10.)
658 snprintf(astring
, 10, "%1.2fk/s", rate
);
659 else if (rate
>= 10. && rate
< 100.)
660 snprintf(astring
, 10, "%2.1fk/s", rate
);
661 else if (rate
>= 100. && rate
< 1000.)
662 snprintf(astring
, 10, "%dk/s", (unsigned int)rate
);
663 else if (rate
> 1000. && rate
< 10000.)
664 snprintf(astring
, 10, "%1.2fM/s", (rate
/ 1000.));
665 else if (rate
> 10000. && rate
< 100000.)
666 snprintf(astring
, 10, "%2.1fM/s", (rate
/ 1000.));
667 else sprintf(astring
, "XXXX");
669 XSetForeground(dpy
, graphics_context
, color
);
670 XSetClipRectangles(dpy
, graphics_context
, 0, 0, &cliprect
, 1, Unsorted
);
671 XDrawString(dpy
, *visible_window
, graphics_context
, 4, 13, astring
, strlen(astring
));
672 XSetClipMask(dpy
, graphics_context
, None
);
679 /* called from within tock to draw the shaded lines making up our bar-graph */
680 void drawColoredLine(int y1
, int y2
, unsigned long *shadecolor
) {
682 static unsigned int linebreaks
[3] = { 50, 90, 100 };
684 for(i
= 0; i
< 3; i
++) {
685 if (y1
> y2
) subline
[i
+1] = y1
- (((y1
- y2
) * linebreaks
[i
]) / 100);
686 else subline
[i
+1] = y1
+ (((y2
- y1
) * linebreaks
[i
]) / 100);
687 XSetForeground(dpy
, graphics_context
, shadecolor
[i
]);
688 XDrawLine(dpy
, *visible_window
, graphics_context
, GRAPH_X_RIGHT
, subline
[i
], GRAPH_X_RIGHT
, subline
[i
+1]);
693 /* Returns in returnarray a 3 value array containing 3 shades (low, normal, and high) of XColor shade.
694 * Called from setup_wmnet on startup and never called again.
696 void shadesOf(XColor
*shade
, unsigned long *returnarray
) {
697 XAllocColor(dpy
, DefaultColormap(dpy
, screen
), shade
);
698 returnarray
[HIGH_INTENSITY
] = shade
->pixel
;
700 shade
->red
= (8 * shade
->red
/ 10);
701 shade
->green
= (8 * shade
->green
/ 10);
702 shade
->blue
= (8 * shade
->blue
/ 10);
703 XAllocColor(dpy
, DefaultColormap(dpy
, screen
), shade
);
704 returnarray
[NORMAL_INTENSITY
] = shade
->pixel
;
706 shade
->red
= 8 * shade
->red
/ 10;
707 shade
->green
= 8 * shade
->green
/ 10;
708 shade
->blue
= 8 * shade
->blue
/ 10;
709 XAllocColor(dpy
, DefaultColormap(dpy
, screen
), shade
);
710 returnarray
[LOW_INTENSITY
] = shade
->pixel
;
714 /* Here is main, clear at the bottom. Handles the event loop and calls tock() every delayTime milliseconds */
715 int main(int argc
, char ** argv
) {
716 unsigned int done
= False
;
719 setup_wmnet(argc
, argv
);
722 while(XPending(dpy
)) {
723 XNextEvent(dpy
, &event
);
726 redraw((XExposeEvent
*)&event
);
729 if(event
.xclient
.data
.l
[0] == delete_atom
)
733 if(event
.xbutton
.button
== Button1
&& click_command
!= NULL
) {
735 execl("/bin/sh", "sh", "-c", click_command
, NULL
);
736 perror("wmnet: execl()");
744 /* Wait for a bit, updating is done in tock() */