wmclockmon: update change-log
[dockapps.git] / wmnet / wmnet.c
blob987366324da57b95ffd4e1a22cc06032fd4156ff
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.
12 * CHANGELOG:
13 * 3/24/1998 -- First release, wrote this yesterday, so it may have
14 * some bugs.
15 * 3/25/1998 -- added logarithmic scaling.
16 * some touch ups on the updateSpeedometer() to be a
17 * little more helpful
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
24 * for modems.
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
52 #include<stdlib.h>
53 #include<stdio.h>
54 #include<X11/X.h>
55 #include<X11/Xlib.h>
56 #include<X11/Xutil.h>
57 #include<X11/Xatom.h>
58 #if defined (__FreeBSD__) || defined (__OpenBSD__)
59 # include<sys/socket.h>
60 # include"getopt.h"
61 #else
62 # include<getopt.h>
63 #endif
64 #include<net/if.h>
65 #include<signal.h>
66 #include<unistd.h>
67 #include<string.h>
68 #include<math.h>
69 #include<sys/time.h>
70 #include<sys/socket.h>
71 #include<sys/types.h>
72 #include<sys/ioctl.h>
73 #include<sys/wait.h>
74 #include<X11/extensions/shape.h>
76 #include "XPM/arrow.xbm"
77 #include "wmnet.h"
80 /* Called on exit() from atexit() */
81 void exit_func(void) {
82 XCloseDisplay(dpy);
85 /* Generic signal handler, if its a SIGCHLD, do a wait() to remove the zombie */
86 void got_signal(int x) {
87 if(x == SIGCHLD) {
88 wait(NULL);
89 return;
90 } else exit(7);
95 /* Does generic setting up of wmnet, (option parsing) and calls setupX() */
96 void setup_wmnet(int argc, char **argv) {
97 int c;
98 XColor thecolor;
99 struct sigaction signal_action;
100 char *txcolorString = NULL, *rxcolorString = NULL, *labelfgcolorString = NULL, *labelbgcolorString = NULL;
101 char *parser = NULL;
102 #ifdef linux
103 const struct option long_options[19] = {
104 #else
105 const struct option long_options[17] = {
106 #endif
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'},
114 #ifdef linux
115 {"txrule", required_argument, NULL, 'T'},
116 {"rxrule", required_argument, NULL, 'R'},
117 #endif
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'},
127 {0, 0, 0, 0}
132 /* Get options */
133 #ifdef linux
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) {
135 #else
136 while((c = getopt_long(argc, argv, "W:F:B:L:vp:u:wnle:r:t:D:d:x:h", long_options, NULL)) != EOF) {
137 #endif
138 switch(c) {
139 case 'v':
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");
143 exit(14);
144 break;
145 case 'W':
146 device = strdup(optarg);
147 break;
148 case 'D':
149 parser = strdup(optarg);
150 break;
151 #ifdef linux
152 case 'T':
153 out_rule = strtoul(optarg, NULL, 10);
154 out_rule_string = strdup(optarg);
155 break;
156 case 'R':
157 in_rule = strtoul(optarg, NULL, 10);
158 in_rule_string = strdup(optarg);
159 break;
160 #endif
161 case 'L':
162 graphbox_height = 35;
163 if (label == NULL) {
164 label = strdup(optarg);
165 } else {
166 fprintf(stderr, "wmnet: duplicate --label\n");
167 exit(22);
169 break;
170 case 'B':
171 if (labelbgcolorString == NULL) {
172 labelbgcolorString = (char *)alloca(strlen(optarg)+1);
173 strncpy(labelbgcolorString, optarg, strlen(optarg)+1);
174 } else {
175 fprintf(stderr, "wmnet: duplicate --labelbg\n");
176 exit(23);
178 break;
179 case 'F':
180 if (labelfgcolorString == NULL) {
181 labelfgcolorString = (char *)alloca(strlen(optarg)+1);
182 strncpy(labelfgcolorString, optarg, strlen(optarg)+1);
183 } else {
184 fprintf(stderr, "wmnet: duplicate --labelfg\n");
185 exit(23);
187 break;
188 case 'l':
189 logscale = True;
190 break;
191 case 'r':
192 if (rxcolorString == NULL) {
193 rxcolorString = (char *)alloca(strlen(optarg)+1);
194 strncpy(rxcolorString, optarg, strlen(optarg)+1);
195 } else {
196 fprintf(stderr, "wmnet: duplicate --rxcolor\n");
197 exit(18);
199 break;
200 case 't':
201 if (txcolorString == NULL) {
202 txcolorString = (char *)alloca(strlen(optarg)+1);
203 strncpy(txcolorString, optarg, strlen(optarg)+1);
204 } else {
205 fprintf(stderr, "wmnet: duplicate --rxcolor\n");
206 exit(19);
208 break;
209 case 'x':
210 maxRate = strtoul(optarg, NULL, 10);
211 break;
212 case 'd':
213 delayTime = strtoul(optarg, NULL, 10);
214 /* Having delayTime > 950000 causes some weirdness */
215 delayTime = delayTime > 950000 ? 950000 : delayTime;
216 break;
217 case 'e':
218 if (click_command == NULL) {
219 click_command = strdup(optarg);
220 } else {
221 fprintf(stderr, "wmnet: duplicate --execute\n");
222 exit(17);
224 break;
225 case 'w':
226 specified_state = WithdrawnState;
227 break;
228 case 'n':
229 specified_state = NormalState;
230 break;
231 case 'u':
233 int fds;
234 struct ifreq ifr;
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 ) {
238 perror("wmnet");
239 exit(20);
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);
248 exit(21);
251 close(fds);
253 break;
254 case 'p':
256 int fds;
257 struct ifreq ifr;
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 ) {
261 perror("wmnet");
262 exit(16);
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);
271 exit(13);
274 close(fds);
276 break;
277 default:
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"
287 #ifdef linux
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"
292 #endif
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"
307 "\n");
308 printf("Compiled in drivers: [%s]\n\n", available_drivers());
309 printf("Report bugs to joff@iastate.edu\n");
310 exit(3);
313 /* Relinquish suid privileges if there */
314 seteuid(getuid());
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()");
327 exit(16);
330 /* Open X Display */
331 if ((dpy = XOpenDisplay(NULL)) == NULL) {
332 fprintf(stderr,"wmnet: doh...can't connect to X server, giving up\n");
333 exit(1);
336 /* assure ourself for a graceful exit */
337 if (atexit(exit_func)) {
338 fprintf(stderr,"wmnet: atexit() failed\n");
339 exit(6);
342 signal_action.sa_handler = got_signal;
343 sigemptyset(&signal_action.sa_mask);
344 signal_action.sa_flags = (SA_NOCLDSTOP|SA_RESTART);
345 #ifdef linux
346 signal_action.sa_restorer = NULL;
347 #endif
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");
352 exit(8);
356 /* Setup initial foreground color */
357 if(rxcolorString) {
358 if(!XParseColor(dpy, DefaultColormap(dpy, screen), rxcolorString, &thecolor)) {
359 fprintf(stderr, "wmnet: what the heck is %s for a color?\n", rxcolorString);
360 exit(12);
362 shadesOf(&thecolor, rx_pixel);
364 if(txcolorString) {
365 if(!XParseColor(dpy, DefaultColormap(dpy, screen), txcolorString, &thecolor)) {
366 fprintf(stderr, "wmnet: what the heck is %s for a color?\n", txcolorString);
367 exit(5);
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);
374 exit(24);
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);
382 exit(25);
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 */
392 setupX();
393 XSetCommand(dpy, main_window, argv, argc);
395 /* Get the initial stats for startup */
396 stat_gather();
399 /* Rock n Roll */
400 XMapWindow(dpy, main_window);
404 /* Called from setup_wmnet() to initialize the X windows stuff */
405 void setupX(void) {
407 XWMHints hints;
408 XSizeHints shints;
409 XGCValues gcv;
410 XColor color;
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");
419 exit(2);
421 if (XInternAtom(dpy,"_WINDOWMAKER_WM_FUNCTION", True) != None) {
422 if (specified_state == -1) specified_state = WithdrawnState;
423 } else {
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");
441 exit(11);
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;
457 } else {
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);
506 if (label) {
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 */
530 void tock(void) {
531 unsigned long since;
532 int y, yy;
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()");
542 exit(10);
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);
548 t = (t + 1) % 8;
552 /* in */
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;
559 /* out */
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;
590 timelast = timenow;
593 if (!stat_gather()) { /* Anything change? */
594 current_rx = rx;
595 current_tx = tx;
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); */
602 else {
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); */
611 else {
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) {
621 double rate;
622 char astring[10];
623 unsigned long color;
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;
633 return 1;
635 collectandreturn = True;
639 if (txRate > rxRate) {
640 rate = (txRate + txRate_last) / 2000.;
641 color = tx_pixel[HIGH_INTENSITY];
642 } else {
643 rate = (rxRate + rxRate_last) / 2000.;
644 color = rx_pixel[HIGH_INTENSITY];
648 if (!clear) {
649 XSetForeground(dpy, graphics_context, black_pixel);
650 XFillRectangle(dpy, *visible_window, graphics_context, 4, 5, 37, 9);
652 if (rate < .1) {
653 clear = True;
654 return(1);
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);
673 clear = False;
674 return(0);
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) {
681 int subline[4], i;
682 static unsigned int linebreaks[3] = { 50, 90, 100 };
683 subline[0] = y1;
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;
717 XEvent event;
719 setup_wmnet(argc, argv);
721 while(!done) {
722 while(XPending(dpy)) {
723 XNextEvent(dpy, &event);
724 switch(event.type) {
725 case Expose:
726 redraw((XExposeEvent *)&event);
727 break;
728 case ClientMessage:
729 if(event.xclient.data.l[0] == delete_atom)
730 done = True;
731 break;
732 case ButtonPress:
733 if(event.xbutton.button == Button1 && click_command != NULL) {
734 if (fork() == 0) {
735 execl("/bin/sh", "sh", "-c", click_command, NULL);
736 perror("wmnet: execl()");
737 exit(15);
740 break;
744 /* Wait for a bit, updating is done in tock() */
745 usleep(delayTime);
746 tock();
748 return 0;