ftp://ftp.smr.ru/pub/0/FreeBSD/releases/distfiles/xscreensaver-3.16.tar.gz
[xscreensaver] / hacks / sonar.c
1 /* sonar.c --- Simulate a sonar screen.
2  *
3  * This is an implementation of a general purpose reporting tool in the
4  * format of a Sonar display. It is designed such that a sensor is read
5  * on every movement of a sweep arm and the results of that sensor are
6  * displayed on the screen. The location of the display points (targets) on the
7  * screen are determined by the current localtion of the sweep and a distance
8  * value associated with the target. 
9  *
10  * Currently the only two sensors that are implemented are the simulator
11  * (the default) and the ping sensor. The simulator randomly creates a set
12  * of bogies that move around on the scope while the ping sensor can be
13  * used to display hosts on your network.
14  *
15  * The ping code is only compiled in if you define HAVE_ICMP or HAVE_ICMPHDR,
16  * because, unfortunately, different systems have different ways of creating
17  * these sorts of packets.
18  *
19  * Also: creating an ICMP socket is a privileged operation, so the program
20  * needs to be installed SUID root if you want to use the ping mode.  If you
21  * check the code you will see that this privilige is given up immediately
22  * after the socket is created.
23  *
24  * It should be easy to extend this code to support other sorts of sensors.
25  * Some ideas:
26  *   - search the output of "netstat" for the list of hosts to ping;
27  *   - plot the contents of /proc/interrupts;
28  *   - plot the process table, by process size, cpu usage, or total time;
29  *   - plot the logged on users by idle time or cpu usage.
30  *
31  * Copyright (C) 1998 by Stephen Martin (smartin@canada.com).
32  * Permission to use, copy, modify, distribute, and sell this software and its
33  * documentation for any purpose is hereby granted without fee, provided that
34  * the above copyright notice appear in all copies and that both that
35  * copyright notice and this permission notice appear in supporting
36  * documentation.  No representations are made about the suitability of this
37  * software for any purpose.  It is provided "as is" without express or 
38  * implied warranty.
39  *
40  * $Revision: 1.12 $
41  *
42  * Version 1.0 April 27, 1998.
43  * - Initial version
44  * - Submitted to RedHat Screensaver Contest
45  * 
46  * Version 1.1 November 3, 1998.
47  * - Added simulation mode.
48  * - Added enhancements by Thomas Bahls <thommy@cs.tu-berlin.de>
49  * - Fixed huge memory leak.
50  * - Submitted to xscreensavers
51  * 
52  * Version 1.2
53  * - All ping code is now ifdef-ed by the compile time symbol HAVE_PING;
54  *   use -DHAVE_PING to include it when you compile.
55  * - Sweep now uses gradients.
56  * - Fixed portability problems with icmphdr on some systems.
57  * - removed lowColor option/resource.
58  * - changed copyright notice so that it could be included in the xscreensavers
59  *   collection.
60  *
61  * Version 1.3 November 16, 1998.
62  * - All ping code is now ifdef-ed by the compile time symbol PING use -DPING
63  *   to include it when you compile.
64  * - Sweep now uses gradients.
65  * - Fixed portability problems with icmphdr on some systems.
66  * - removed lowcolour option/resource.
67  * - changed copyright notice so that it could be included in the xscreensavers
68  *   collection.
69  *
70  * Version 1.4 November 18, 1998.
71  * - More ping portability fixes.
72  *
73  * Version 1.5 November 19, 1998.
74  * - Synced up with jwz's changes.
75  * - Now need to define HAVE_PING to compile in the ping stuff.
76  */
77
78 /* These are computed by configure now:
79    #define HAVE_ICMP
80    #define HAVE_ICMPHDR
81  */
82
83
84 /* Include Files */
85
86 #include <stdlib.h>
87 #include <stdio.h>
88 #include <math.h>
89
90 #include "screenhack.h"
91 #include "colors.h"
92 #include "hsv.h"
93
94 #if defined(HAVE_ICMP) || defined(HAVE_ICMPHDR)
95 # include <unistd.h>
96 # include <limits.h>
97 # include <signal.h>
98 # include <fcntl.h>
99 # include <sys/types.h>
100 # include <sys/time.h>
101 # include <sys/ipc.h>
102 # include <sys/shm.h>
103 # include <sys/socket.h>
104 # include <netinet/in_systm.h>
105 # include <netinet/in.h>
106 # include <netinet/ip.h>
107 # include <netinet/ip_icmp.h>
108 # include <netinet/udp.h>
109 # include <arpa/inet.h>
110 # include <netdb.h>
111 #endif /* HAVE_ICMP || HAVE_ICMPHDR */
112
113
114 /* Defines */
115
116 #undef MY_MIN
117 #define MY_MIN(a,b) ((a)<(b)?(a - 50):(b - 10))
118
119 #ifndef LINE_MAX
120 # define LINE_MAX 2048
121 #endif
122
123 /* Frigging icmp */
124
125 #if defined(HAVE_ICMP)
126 # define HAVE_PING
127 # define ICMP             icmp
128 # define ICMP_TYPE(p)     (p)->icmp_type
129 # define ICMP_CODE(p)     (p)->icmp_code
130 # define ICMP_CHECKSUM(p) (p)->icmp_cksum
131 # define ICMP_ID(p)       (p)->icmp_id
132 # define ICMP_SEQ(p)      (p)->icmp_seq
133 #elif defined(HAVE_ICMPHDR)
134 # define HAVE_PING
135 # define ICMP             icmphdr
136 # define ICMP_TYPE(p)     (p)->type
137 # define ICMP_CODE(p)     (p)->code
138 # define ICMP_CHECKSUM(p) (p)->checksum
139 # define ICMP_ID(p)       (p)->un.echo.id
140 # define ICMP_SEQ(p)      (p)->un.echo.sequence
141 #else
142 # undef HAVE_PING
143 #endif
144
145 /* Forward References */
146
147 #ifdef HAVE_PING
148 static u_short checksum(u_short *, int);
149 #endif
150 static long delta(struct timeval *, struct timeval *);
151
152 /* Data Structures */
153
154 /*
155  * The Bogie.
156  *
157  * This represents an object that is visable on the scope.
158  */
159
160 typedef struct Bogie {
161     char *name;                 /* The name of the thing being displayed */
162     int distance;               /* The distance to this thing (0 - 100) */
163     int tick;                   /* The tick that it was found on */
164     int ttl;                    /* The time to live */
165     int age;                    /* How long it's been around */
166     struct Bogie *next;         /* The next one in the list */
167 } Bogie;
168
169 /*
170  * Sonar Information.
171  *
172  * This contains all of the runtime information about the sonar scope.
173  */
174
175 typedef struct {
176     Display *dpy;               /* The X display */
177     Window win;                 /* The window */
178     GC hi,                      /* The leading edge of the sweep */
179         lo,                     /* The trailing part of the sweep */
180         erase,                  /* Used to erase things */
181         grid,                   /* Used to draw the grid */
182         text;                   /* Used to draw text */
183     Colormap cmap;              /* The colormap */
184     XFontStruct *font;          /* The font to use for the labels */
185     int text_steps;             /* How many steps to fade text. */
186     XColor *text_colors;        /* Pixel values used to fade text */
187     int sweep_degrees;          /* How much of the circle the sweep uses */
188     int sweep_segs;             /* How many gradients in the sweep. */
189     XColor *sweep_colors;        /* The sweep pixel values */
190     int width, height;          /* Window dimensions */
191     int minx, miny, maxx, maxy, /* Bounds of the scope */
192         centrex, centrey, radius; /* Parts of the scope circle */
193     Bogie *visable;             /* List of visable objects */
194     int current;                /* Current position of sweep */
195
196     int delay;                  /* how long between each frame of the anim */
197
198 } sonar_info;
199
200 /* 
201  * Variables to support the differnt Sonar modes.
202  */
203
204 Bogie *(*sensor)(sonar_info *, void *); /* The current sensor */
205 void *sensor_info;                      /* Information about the sensor */
206
207 /*
208  * A list of targets to ping.
209  */
210
211 #ifdef HAVE_PING
212 typedef struct ping_target {
213     char *name;                 /* The name of the target */
214     struct sockaddr address;    /* The address of the target */
215     struct ping_target *next;   /* The next one in the list */
216 } ping_target;
217
218 /*
219  * Ping Information.
220  *
221  * This contains the information for the ping sensor.
222  */
223
224 typedef struct {
225     int icmpsock;               /* Socket for sending pings */
226     int pid;                    /* Our process ID */
227     int seq;                    /* Packet sequence number */
228     int timeout;                /* Timeout value for pings */
229     ping_target *targets;       /* List of targets to ping */
230     int numtargets;             /* The number of targets to ping */
231 } ping_info;
232
233 /* Flag to indicate that the timer has expired on us */
234
235 static int timer_expired;
236
237
238 #endif /* HAVE_PING */
239
240 /*
241  * A list of targets for the simulator
242  */
243
244 typedef struct sim_target {
245     char *name;                 /* The name of the target */
246     int nexttick;               /* The next tick that this will be seen */
247     int nextdist;               /* The distance on that tick */
248     int movedlasttick;          /* Flag to indicate we just moved this one */
249 } sim_target;
250
251 /*
252  * Simulator Information.
253  *
254  * This contains the information for the simulator mode.
255  */
256
257 typedef struct {
258     sim_target *teamA;          /* The bogies for the A team */
259     int numA;                   /* The number of bogies in team A */
260     char *teamAID;              /* The identifier for bogies in team A */
261     sim_target *teamB;          /* The bogies for the B team */
262     int numB;                   /* The number of bogies in team B */
263     char *teamBID;              /* The identifier for bogies in team B */
264 } sim_info;
265
266 /* Name of the Screensaver hack */
267
268 char *progclass="sonar";
269
270 /* Application Defaults */
271
272 char *defaults [] = {
273     ".background:      #000000",
274     ".sweepColor:      #00FF00",
275     "*delay:           100000",
276     "*scopeColor:      #003300",
277     "*gridColor:       #00AA00",
278     "*textColor:       #FFFF00",
279     "*ttl:             90",
280     "*mode:            default",
281     "*font:            fixed",
282     "*sweepDegrees:    30",
283
284     "*textSteps:       80",     /* npixels */
285     "*sweepSegments:   80",     /* npixels */
286
287 #ifdef HAVE_PING
288     "*pingTimeout:     3000",
289     "*pingSource:      file",
290     "*pingFile:        /etc/hosts",
291     "*pingList:        localhost",
292 #endif /* HAVE_PING */
293     "*teamAName:       F18",
294     "*teamBName:       MIG",
295     "*teamACount:      4",
296     "*teamBCount:      4",
297     0
298 };
299
300 /* Options passed to this program */
301
302 XrmOptionDescRec options [] = {
303     {"-background",   ".background",   XrmoptionSepArg, 0 },
304     {"-sweep-color",  ".sweepColor",   XrmoptionSepArg, 0 },
305     {"-scope-color",  ".scopeColor",   XrmoptionSepArg, 0 },
306     {"-grid-color",   ".gridColor",    XrmoptionSepArg, 0 },
307     {"-text-color",   ".textColor",    XrmoptionSepArg, 0 },
308     {"-ttl",          ".ttl",          XrmoptionSepArg, 0 },
309     {"-mode",         ".mode",         XrmoptionSepArg, 0 },
310     {"-font",         ".font",         XrmoptionSepArg, 0 },
311 #ifdef HAVE_PING
312     {"-ping-timeout", ".pingTimeout",  XrmoptionSepArg, 0 },
313     {"-ping-source",  ".pingSource",   XrmoptionSepArg, 0 },
314     {"-ping-file",    ".pingFile",     XrmoptionSepArg, 0 },
315     {"-ping-list",    ".pingList",     XrmoptionSepArg, 0 },
316 #endif /* HAVE_PING */
317     {"-team-a-name",   ".teamAName",   XrmoptionSepArg, 0 },
318     {"-team-b-name",   ".teamBName",   XrmoptionSepArg, 0 },
319     {"-team-a-count",  ".teamACount",  XrmoptionSepArg, 0 },
320     {"-team-b-count",  ".teamBCount",  XrmoptionSepArg, 0 },
321     { 0, 0, 0, 0 }
322 };
323
324 /*
325  * The number of ticks that bogies are visable on the screen before they
326  * fade away.
327  */
328
329 static int TTL;
330
331 /*
332  * Create a new Bogie and set some initial values.
333  *
334  * Args:
335  *    name     - The name of the bogie.
336  *    distance - The distance value.
337  *    tick     - The tick value.
338  *    ttl      - The time to live value.
339  *
340  * Returns:
341  *    The newly allocated bogie or null if a memory problem occured.
342  */
343
344 static Bogie *
345 newBogie(char *name, int distance, int tick, int ttl) 
346 {
347
348     /* Local Variables */
349
350     Bogie *new;
351
352     /* Allocate a bogie and initialize it */
353
354     if ((new = (Bogie *) calloc(1, sizeof(Bogie))) == NULL) {
355         fprintf(stderr, "%s: Out of Memory\n", progname);
356         return NULL;
357     }
358     new->name = name;
359     new->distance = distance;
360     new->tick = tick;
361     new->ttl = ttl;
362     new->age = 0;
363     new->next = (Bogie *) 0;
364     return new;
365 }
366
367 /*
368  * Free a Bogie.
369  *
370  * Args:
371  *    b - The bogie to free.
372  */
373
374 static void
375 freeBogie(Bogie *b) 
376 {
377     if (b->name != (char *) 0)
378         free(b->name);
379     free(b);
380 }
381
382 /*
383  * Find a bogie by name in a list.
384  *
385  * This does a simple linear search of the list for a given name.
386  *
387  * Args:
388  *    bl   - The Bogie list to search.
389  *    name - The name to look for.
390  *
391  * Returns:
392  *    The requested Bogie or null if it wasn't found.
393  */
394
395 static Bogie *
396 findNode(Bogie *bl, char *name) 
397 {
398
399     /* Local Variables */
400
401     Bogie *p;
402
403     /* Abort if the list is empty or no name is given */
404
405     if ((name == NULL) || (bl == NULL))
406         return NULL;
407
408     /* Search the list for the desired name */
409
410     p = bl;
411     while (p != NULL) {
412         if (strcmp(p->name, name) == 0)
413             return p;
414         p = p->next;
415     }
416
417     /* Not found */
418
419     return NULL;
420 }
421
422 #ifdef HAVE_PING
423
424 /*
425  * Lookup the address for a ping target;
426  *
427  * Args:
428  *    target - The ping_target fill in the address for.
429  *
430  * Returns:
431  *    1 if the host was successfully resolved, 0 otherwise.
432  */
433
434 static int
435 lookupHost(ping_target *target) 
436 {
437
438     /* Local Variables */
439
440     struct sockaddr_in *iaddr;
441
442     /* Set up the target address we first assume that the name is the
443        IP address as a string */
444
445     iaddr = (struct sockaddr_in *) &(target->address);
446     iaddr->sin_family = AF_INET;
447     if ((iaddr->sin_addr.s_addr = inet_addr(target->name)) == -1) {
448
449         /* Conversion of IP address failed, try to look the host up by name */
450
451         struct hostent *hent = gethostbyname(target->name);
452         if (hent == NULL) {
453             fprintf(stderr, "%s: could not resolve host %s\n",
454                     progname, target->name);
455             return 0;
456         }
457         memcpy(&iaddr->sin_addr, hent->h_addr_list[0],
458                sizeof(iaddr->sin_addr));
459     }
460
461     /* Done */
462
463     return 1;
464 }
465
466 /*
467  * Create a target for a host.
468  *
469  * Args:
470  *    name - The name of the host.
471  *
472  * Returns:
473  *    A newly allocated target or null if the host could not be resolved.
474  */
475
476 static ping_target *
477 newHost(char *name) 
478 {
479
480     /* Local Variables */
481
482     ping_target *target = NULL;
483
484     /* Create the target */
485
486     if ((target = calloc(1, sizeof(ping_target))) == NULL) {
487         fprintf(stderr, "%s: Out of Memory\n", progname);
488         goto target_init_error;
489     }
490     if ((target->name = strdup(name)) == NULL) {
491         fprintf(stderr, "%s: Out of Memory\n", progname);
492         goto target_init_error;
493     }
494
495     /* Lookup the host */
496
497     if (! lookupHost(target))
498         goto target_init_error;
499
500     /* Done */
501
502     return target;
503
504     /* Handle errors here */
505
506 target_init_error:
507     if (target != NULL)
508         free(target);
509     return NULL;
510 }
511
512 /*
513  * Generate a list of ping targets from the entries in a file.
514  *
515  * Args:
516  *    fname - The name of the file. This file is expected to be in the same
517  *            format as /etc/hosts.
518  *
519  * Returns:
520  *    A list of targets to ping or null if an error occured.
521  */
522
523 static ping_target *
524 readPingHostsFile(char *fname) 
525 {
526
527     /* Local Variables */
528
529     FILE *fp;
530     char buf[LINE_MAX];
531     char *p;
532     ping_target *list = NULL;
533     char *addr, *name;
534     ping_target *new;
535
536     /* Make sure we in fact have a file to process */
537
538     if ((fname == NULL) || (fname[0] == '\0')) {
539         fprintf(stderr, "%s: invalid ping host file name\n", progname);
540         return NULL;
541     }
542
543     /* Open the file */
544
545     if ((fp = fopen(fname, "r")) == NULL) {
546         char msg[1024];
547         sprintf(msg, "%s: unable to open host file %s", progname, fname);
548         perror(msg);
549         return NULL;
550     }
551
552     /* Read the file line by line */
553
554     while ((p = fgets(buf, LINE_MAX, fp)) != NULL) {
555
556         /*
557          * Parse the line skipping those that start with '#'.
558          * The rest of the lines in the file should be in the same
559          * format as a /etc/hosts file. We are only concerned with
560          * the first two field, the IP address and the name
561          */
562
563         while ((*p == ' ') || (*p == '\t'))
564             p++;
565         if (*p == '#')
566             continue;
567
568         /* Get the name and address */
569
570         name = addr = NULL;
571         if ((addr = strtok(buf, " \t\n")) != NULL)
572             name = strtok(NULL, " \t\n");
573         else
574             continue;
575
576         /* Check to see if the addr looks like an addr.  If not, assume
577            the addr is a name and there is no addr.  This way, we can
578            handle files whose lines have "xx.xx.xx.xx hostname" as their
579            first two tokens, and also files that have a hostname as their
580            first token (like .ssh/known_hosts and .rhosts.)
581          */
582         {
583           int i; char c;
584           if (4 != sscanf(addr, "%d.%d.%d.%d%c", &i, &i, &i, &i, &c))
585             {
586               name = addr;
587               addr = NULL;
588             }
589         }
590         printf ("\"%s\" \"%s\"\n", name, addr);
591
592         /* Create a new target using first the name then the address */
593
594         new = NULL;
595         if (name != NULL)
596             new = newHost(name);
597         if (new == NULL && addr != NULL)
598             new = newHost(addr);
599
600         /* Add it to the list if we got one */
601
602         if (new != NULL) {
603             new->next = list;
604             list = new;
605         }
606     }
607
608     /* Close the file and return the list */
609
610     fclose(fp);
611     return list;
612 }
613
614 /*
615  * Generate a list of ping targets from the entries in a string.
616  *
617  * Args:
618  *    list - A list of comma separated host names.
619  *
620  * Returns:
621  *    A list of targets to ping or null if an error occured.
622  */
623
624 static ping_target *
625 readPingHostsList(char *list) 
626 {
627
628     /* Local Variables */
629
630     char *host;
631     ping_target *hostlist = NULL;
632     ping_target *new;
633
634     /* Check that there is a list */
635
636     if ((list == NULL) || (list[0] == '\0'))
637         return NULL;
638
639     /* Loop through the hosts and add them to the list to return */
640
641     host = strtok(list, ",");
642     while (host != NULL) {
643         new = newHost(host);
644         if (new != NULL) {
645             new->next = hostlist;
646             hostlist = new;
647         }
648         host = strtok(NULL, ",");
649     }
650
651     /* Done */
652
653     return hostlist;
654 }
655
656 /*
657  * Generate a list ping targets consisting of all of the entries on
658  * the same subnet.
659  *
660  * Returns:
661  *    A list of all of the hosts on this net.
662  */
663
664 static ping_target *
665 subnetHostsList(void) 
666 {
667
668     /* Local Variables */
669
670     char hostname[BUFSIZ];
671     char address[BUFSIZ];
672     struct hostent *hent;
673     char *p;
674     int i;
675     ping_target *new;
676     ping_target *list = NULL;
677
678     /* Get our hostname */
679
680     if (gethostname(hostname, BUFSIZ)) {
681         fprintf(stderr, "%s: unable to get local hostname\n", progname);
682         return NULL;
683     }
684
685     /* Get our IP address and convert it to a string */
686
687     if ((hent = gethostbyname(hostname)) == NULL) {
688         fprintf(stderr, "%s: unable to lookup our IP address\n", progname);
689         return NULL;
690     }
691     strcpy(address, inet_ntoa(*((struct in_addr *)hent->h_addr_list[0])));
692
693     /* Get a pointer to the last "." in the string */
694
695     if ((p = strrchr(address, '.')) == NULL) {
696         fprintf(stderr, "%s: can't parse IP address %s\n", progname, address);
697         return NULL;
698     }
699     p++;
700
701     /* Construct targets for all addresses in this subnet */
702
703     /* #### jwz: actually, this is wrong, since it assumes a
704        netmask of 255.255.255.0.  But I'm not sure how to find
705        the local netmask.
706      */
707     for (i = 254; i > 0; i--) {
708         sprintf(p, "%d", i);
709         new = newHost(address);
710         if (new != NULL) {
711             new->next = list;
712             list = new;
713         }
714     }
715   
716     /* Done */
717
718     return list;
719 }
720
721 /*
722  * Initialize the ping sensor.
723  *
724  * Returns:
725  *    A newly allocated ping_info structure or null if an error occured.
726  */
727
728 static ping_info *
729 init_ping(void) 
730 {
731
732     /* Local Variables */
733
734     ping_info *pi = NULL;               /* The new ping_info struct */
735     char *src;                          /* The source of the ping hosts */
736     ping_target *pt;                    /* Used to count the targets */
737
738     /* Create the ping info structure */
739
740     if ((pi = (ping_info *) calloc(1, sizeof(ping_info))) == NULL) {
741         fprintf(stderr, "%s: Out of memory\n", progname);
742         goto ping_init_error;
743     }
744
745     /* Create the ICMP socket and turn off SUID */
746
747     if ((pi->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
748         char msg[1024];
749         sprintf(msg, "%s: can't create ICMP socket", progname);
750         perror(msg);
751         fprintf(stderr,
752          "%s: this program must be setuid to root for `ping mode' to work.\n",
753                 progname);
754         goto ping_init_error;
755     }
756     setuid(getuid());
757     pi->pid = getpid() & 0xFFFF;
758     pi->seq = 0;
759     pi->timeout = get_integer_resource("pingTimeout", "PingTimeout");
760
761     /* Generate a list of targets */
762
763     src = get_string_resource("pingSource", "PingSource");
764     if (strcmp(src, "file") == 0) {
765
766         /*
767          * The list of ping targets is to come from a file in
768          * /etc/hosts format
769          */
770
771         pi->targets = readPingHostsFile(get_string_resource("pingFile",
772                                                             "PingFile"));
773
774     } else if (strcmp(src, "list") == 0) {
775
776         /* The list of hosts is to come from the pinghostlist resource */
777
778         pi->targets = readPingHostsList(get_string_resource("pingList",
779                                                             "PingList"));
780
781     } else if (strcmp(src, "subnet") == 0) {
782
783         pi->targets = subnetHostsList();
784
785     } else {
786
787         /* Unknown source */
788
789         fprintf(stderr,
790                "%s: pingSource must be `file', `list', or `subnet', not: %s\n",
791                 progname, src);
792         exit (1);
793     }
794
795     /* Make sure there is something to ping */
796
797     if (pi->targets == NULL) {
798         fprintf(stderr, "%s: nothing to ping", progname);
799         goto ping_init_error;
800     }
801
802     /* Count the targets */
803
804     pt = pi->targets;
805     pi->numtargets = 0;
806     while (pt != NULL) {
807         pi->numtargets++;
808         pt = pt->next;
809     }
810
811     /* Done */
812
813     return pi;
814
815     /* Handle initialization errors here */
816
817 ping_init_error:
818     if (pi != NULL)
819         free(pi);
820     return NULL;
821 }
822
823 /*
824  * Ping a host.
825  *
826  * Args:
827  *    pi   - The ping information strcuture.
828  *    host - The name or IP address of the host to ping (in ascii).
829  */
830
831 static void
832 sendping(ping_info *pi, ping_target *pt) 
833 {
834
835     /* Local Variables */
836
837     u_char *packet;
838     struct ICMP *icmph;
839     int result;
840
841     /*
842      * Note, we will send the character name of the host that we are
843      * pinging in the packet so that we don't have to keep track of the
844      * name or do an address lookup when it comes back.
845      */
846
847     int pcktsiz = sizeof(struct ICMP) + sizeof(struct timeval) +
848         strlen(pt->name) + 1;
849
850     /* Create the ICMP packet */
851
852     if ((packet = (u_char *) malloc(pcktsiz)) == (void *) 0)
853         return;  /* Out of memory */
854     icmph = (struct ICMP *) packet;
855     ICMP_TYPE(icmph) = ICMP_ECHO;
856     ICMP_CODE(icmph) = 0;
857     ICMP_CHECKSUM(icmph) = 0;
858     ICMP_ID(icmph) = pi->pid;
859     ICMP_SEQ(icmph) = pi->seq++;
860     gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)],
861                  (struct timezone *) 0);
862     strcpy((char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)],
863            pt->name);
864     ICMP_CHECKSUM(icmph) = checksum((u_short *)packet, pcktsiz);
865
866     /* Send it */
867
868     if ((result = sendto(pi->icmpsock, packet, pcktsiz, 0, 
869                          &pt->address, sizeof(pt->address))) !=  pcktsiz) {
870 #if 0
871         char errbuf[BUFSIZ];
872         sprintf(errbuf, "%s: error sending ping to %s", progname, pt->name);
873         perror(errbuf);
874 #endif
875     }
876 }
877
878 /*
879  * Catch a signal and do nothing.
880  *
881  * Args:
882  *    sig - The signal that was caught.
883  */
884
885 static void
886 sigcatcher(int sig)
887 {
888     timer_expired = 1;
889 }
890
891 /*
892  * Compute the checksum on a ping packet.
893  *
894  * Args:
895  *    packet - A pointer to the packet to compute the checksum for.
896  *    size   - The size of the packet.
897  *
898  * Returns:
899  *    The computed checksum
900  *    
901  */
902
903 static u_short
904 checksum(u_short *packet, int size) 
905 {
906
907     /* Local Variables */
908
909     register int nleft = size;
910     register u_short *w = packet;
911     register int sum = 0;
912     u_short answer = 0;
913
914     /*
915      * Our algorithm is simple, using a 32 bit accumulator (sum), we add
916      * sequential 16 bit words to it, and at the end, fold back all the
917      * carry bits from the top 16 bits into the lower 16 bits.
918      */
919
920     while (nleft > 1)  {
921         sum += *w++;
922         nleft -= 2;
923     }
924
925     /* mop up an odd byte, if necessary */
926
927     if (nleft == 1) {
928         *(u_char *)(&answer) = *(u_char *)w ;
929         *(1 + (u_char *)(&answer)) = 0;
930         sum += answer;
931     }
932
933     /* add back carry outs from top 16 bits to low 16 bits */
934
935     sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
936     sum += (sum >> 16);                     /* add carry */
937     answer = ~sum;                          /* truncate to 16 bits */
938
939     /* Done */
940
941     return(answer);
942 }
943
944 /*
945  * Look for ping replies.
946  *
947  * Retrieve all outstanding ping replies.
948  *
949  * Args:
950  *    si - Information about the sonar.
951  *    pi - Ping information.
952  *    ttl - The time each bogie is to live on the screen
953  *
954  * Returns:
955  *    A Bogie list of all the machines that replied.
956  */
957
958 static Bogie *
959 getping(sonar_info *si, ping_info *pi, int ttl) 
960 {
961
962     /* Local Variables */
963
964     struct sockaddr from;
965     int fromlen;
966     int result;
967     u_char packet[1024];
968     struct timeval now;
969     struct timeval *then;
970     struct ip *ip;
971     int iphdrlen;
972     struct ICMP *icmph;
973     Bogie *bl = NULL;
974     Bogie *new;
975     char *name;
976     struct sigaction sa;
977     struct itimerval it;
978
979     /* Set up a signal to interupt our wait for a packet */
980
981     sigemptyset(&sa.sa_mask);
982     sa.sa_flags = 0;
983     sa.sa_handler = sigcatcher;
984     if (sigaction(SIGALRM, &sa, 0) == -1) {
985         char msg[1024];
986         sprintf(msg, "%s: unable to trap SIGALRM", progname);
987         perror(msg);
988         exit(1);
989     }
990
991     /* Set up a timer to interupt us if we don't get a packet */
992
993     it.it_interval.tv_sec = 0;
994     it.it_interval.tv_usec = 0;
995     it.it_value.tv_sec = 0;
996     it.it_value.tv_usec = pi->timeout;
997     timer_expired = 0;
998     setitimer(ITIMER_REAL, &it, NULL);
999
1000     /* Wait for a result packet */
1001
1002     fromlen = sizeof(from);
1003     while (! timer_expired &&
1004            (result = recvfrom(pi->icmpsock, packet, sizeof(packet),
1005                               0, &from, &fromlen)) > 0) {
1006
1007         /* Check the packet */
1008
1009         gettimeofday(&now, (struct timezone *) 0);
1010         ip = (struct ip *) packet;
1011
1012         iphdrlen = ip->ip_hl << 2;
1013         /* On DEC OSF1 4.0, the preceeding line needs to be
1014            iphdrlen = (ip->ip_vhl & 0x0F) << 2;
1015            but I don't know how to do this portably.  -- jwz.
1016          */
1017
1018         icmph = (struct ICMP *) &packet[iphdrlen];
1019
1020         /* Was the packet a reply?? */
1021
1022         if (ICMP_TYPE(icmph) != ICMP_ECHOREPLY) {
1023             /* Ignore anything but ICMP Replies */
1024             continue; /* Nope */
1025         }
1026
1027         /* Was it for us? */
1028
1029         if (ICMP_ID(icmph) != pi->pid) {
1030             /* Ignore packets not set from us */
1031             continue; /* Nope */
1032         }
1033
1034         /* Copy the name of the bogie */
1035
1036         if ((name =
1037              strdup((char *) &packet[iphdrlen + 
1038                                     + sizeof(struct ICMP)
1039                                     + sizeof(struct timeval)])) == NULL) {
1040             fprintf(stderr, "%s: Out of memory\n", progname);
1041             return bl;
1042         }
1043
1044         /* If the name is an IP addr, try to resolve it. */
1045         {
1046           int iip[4];
1047           char c;
1048           if (4 == sscanf(name, " %d.%d.%d.%d %c",
1049                           &iip[0], &iip[1], &iip[2], &iip[3], &c))
1050             {
1051               unsigned char ip[4];
1052               struct hostent *h;
1053               ip[0] = iip[0]; ip[1] = iip[1]; ip[2] = iip[2]; ip[3] = iip[3];
1054               h = gethostbyaddr ((char *) ip, 4, AF_INET);
1055               if (h && h->h_name && *h->h_name)
1056                 {
1057                   free (name);
1058                   name = strdup (h->h_name);
1059                 }
1060             }
1061         }
1062
1063         /* Create the new Bogie and add it to the list we are building */
1064
1065         if ((new = newBogie(name, 0, si->current, ttl)) == NULL)
1066             return bl;
1067         new->next = bl;
1068         bl = new;
1069
1070         /* Compute the round trip time */
1071
1072         then =  (struct timeval *) &packet[iphdrlen +
1073                                           sizeof(struct ICMP)];
1074         new->distance = delta(then, &now) / 100;
1075         if (new->distance == 0)
1076                 new->distance = 2; /* HACK */
1077     }
1078
1079     /* Done */
1080
1081     return bl;
1082 }
1083
1084 /*
1085  * Ping hosts.
1086  *
1087  * Args:
1088  *    si - Sonar Information.
1089  *    pi - Ping Information.
1090  *
1091  * Returns:
1092  *    A list of hosts that replied to pings or null if there were none.
1093  */
1094
1095 static Bogie *
1096 ping(sonar_info *si, void *vpi) 
1097 {
1098
1099     ping_info *pi = (ping_info *) vpi;
1100     static ping_target *ptr = NULL;
1101
1102     int tick = si->current * -1 + 1;
1103     if ((ptr == NULL) && (tick == 1))
1104         ptr = pi->targets;
1105
1106     if (pi->numtargets <= 90) {
1107         int xdrant = 90 / pi->numtargets;
1108         if ((tick % xdrant) == 0) {
1109             if (ptr != (ping_target *) 0) {
1110                 sendping(pi, ptr);
1111                 ptr = ptr->next;
1112             }
1113         }
1114
1115     } else if (pi->numtargets > 90) {
1116         if (ptr != (ping_target *) 0) {
1117             sendping(pi, ptr);
1118             ptr = ptr->next;
1119         }
1120     }
1121
1122     /* Get the results */
1123
1124     return getping(si, pi, TTL);
1125 }
1126
1127 #endif /* HAVE_PING */
1128
1129 /*
1130  * Calculate the difference between two timevals in microseconds.
1131  *
1132  * Args:
1133  *    then - The older timeval.
1134  *    now  - The newer timeval.
1135  *
1136  * Returns:
1137  *   The difference between the two in microseconds.
1138  */
1139
1140 static long
1141 delta(struct timeval *then, struct timeval *now) 
1142 {
1143     return (((now->tv_sec - then->tv_sec) * 1000000) + 
1144                (now->tv_usec - then->tv_usec));  
1145 }
1146
1147 /*
1148  * Initialize the simulation mode.
1149  */
1150
1151 static sim_info *
1152 init_sim(void) 
1153 {
1154
1155     /* Local Variables */
1156
1157     sim_info *si;
1158     int i;
1159
1160     /* Create the simulation info structure */
1161
1162     if ((si = (sim_info *) calloc(1, sizeof(sim_info))) == NULL) {
1163         fprintf(stderr, "%s: Out of memory\n", progname);
1164         return NULL;
1165     }
1166
1167     /* Team A */
1168
1169     si->numA = get_integer_resource("teamACount", "TeamACount");
1170     if ((si->teamA = (sim_target *)calloc(si->numA, sizeof(sim_target)))
1171         == NULL) {
1172         free(si);
1173         fprintf(stderr, "%s: Out of Memory\n", progname);
1174         return NULL;
1175     }
1176     si->teamAID = get_string_resource("teamAName", "TeamAName");
1177     for (i = 0; i < si->numA; i++) {
1178         if ((si->teamA[i].name = (char *) malloc(strlen(si->teamAID) + 4))
1179             == NULL) {
1180             free(si);
1181             fprintf(stderr, "%s: Out of Memory\n", progname);
1182             return NULL;
1183         }
1184         sprintf(si->teamA[i].name, "%s%03d", si->teamAID, i+1);
1185         si->teamA[i].nexttick = (int) (90.0 * random() / RAND_MAX);
1186         si->teamA[i].nextdist = (int) (100.0 * random() / RAND_MAX);
1187     }
1188
1189     /* Team B */
1190
1191     si->numB = get_integer_resource("teamBCount", "TeamBCount");
1192     if ((si->teamB = (sim_target *)calloc(si->numB, sizeof(sim_target)))
1193         == NULL) {
1194         free(si);
1195         fprintf(stderr, "%s: Out of Memory\n", progname);
1196         return NULL;
1197     }
1198     si->teamBID = get_string_resource("teamBName", "TeamBName");
1199     for (i = 0; i < si->numB; i++) {
1200         if ((si->teamB[i].name = (char *) malloc(strlen(si->teamBID) + 4))
1201             == NULL) {
1202             free(si);
1203             fprintf(stderr, "%s: Out of Memory\n", progname);
1204             return NULL;
1205         }
1206         sprintf(si->teamB[i].name, "%s%03d", si->teamBID, i+1);
1207         si->teamB[i].nexttick = (int) (90.0 * random() / RAND_MAX);
1208         si->teamB[i].nextdist = (int) (100.0 * random() / RAND_MAX);
1209     }
1210
1211     /* Done */
1212
1213     return si;
1214 }
1215
1216 /*
1217  * Initialize the Sonar.
1218  *
1219  * Args:
1220  *    dpy - The X display.
1221  *    win - The X window;
1222  *
1223  * Returns:
1224  *   A sonar_info strcuture or null if memory allocation problems occur.
1225  */
1226
1227 static sonar_info *
1228 init_sonar(Display *dpy, Window win) 
1229 {
1230
1231     /* Local Variables */
1232
1233     XGCValues gcv;
1234     XWindowAttributes xwa;
1235     sonar_info *si;
1236     XColor start, end;
1237     int h1, h2;
1238     double s1, s2, v1, v2;
1239
1240     /* Create the Sonar information structure */
1241
1242     if ((si = (sonar_info *) calloc(1, sizeof(sonar_info))) == NULL) {
1243         fprintf(stderr, "%s: Out of memory\n", progname);
1244         return NULL;
1245     }
1246
1247     /* Initialize the structure for the current environment */
1248
1249     si->dpy = dpy;
1250     si->win = win;
1251     si->visable = NULL;
1252     XGetWindowAttributes(dpy, win, &xwa);
1253     si->cmap = xwa.colormap;
1254     si->width = xwa.width;
1255     si->height = xwa.height;
1256     si->centrex = si->width / 2;
1257     si->centrey = si->height / 2;
1258     si->maxx = si->centrex + MY_MIN(si->centrex, si->centrey) - 10;
1259     si->minx = si->centrex - MY_MIN(si->centrex, si->centrey) + 10;
1260     si->maxy = si->centrey + MY_MIN(si->centrex, si->centrey) - 10;
1261     si->miny = si->centrey - MY_MIN(si->centrex, si->centrey) + 10;
1262     si->radius = si->maxx - si->centrex;
1263     si->current = 0;
1264
1265     /* Get the font */
1266
1267     if (((si->font = XLoadQueryFont(dpy, get_string_resource ("font", "Font")))
1268          == NULL) &&
1269         ((si->font = XLoadQueryFont(dpy, "fixed")) == NULL)) {
1270         fprintf(stderr, "%s: can't load an appropriate font\n", progname);
1271         return NULL;
1272     }
1273
1274     /* Get the delay between animation frames */
1275
1276     si->delay = get_integer_resource ("delay", "Integer");
1277     if (si->delay < 0) si->delay = 0;
1278
1279     /* Create the Graphics Contexts that will be used to draw things */
1280
1281     gcv.foreground = 
1282         get_pixel_resource ("sweepColor", "SweepColor", dpy, si->cmap);
1283     si->hi = XCreateGC(dpy, win, GCForeground, &gcv);
1284     gcv.font = si->font->fid;
1285     si->text = XCreateGC(dpy, win, GCForeground|GCFont, &gcv);
1286     gcv.foreground = get_pixel_resource("scopeColor", "ScopeColor",
1287                                         dpy, si->cmap);
1288     si->erase = XCreateGC (dpy, win, GCForeground, &gcv);
1289     gcv.foreground = get_pixel_resource("gridColor", "GridColor",
1290                                         dpy, si->cmap);
1291     si->grid = XCreateGC (dpy, win, GCForeground, &gcv);
1292
1293     /* Compute pixel values for fading text on the display */
1294
1295     XParseColor(dpy, si->cmap, 
1296                 get_string_resource("textColor", "TextColor"), &start);
1297     XParseColor(dpy, si->cmap, 
1298                 get_string_resource("scopeColor", "ScopeColor"), &end);
1299
1300     rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
1301     rgb_to_hsv (end.red, end.green, end.blue, &h2, &s2, &v2);
1302
1303     si->text_steps = get_integer_resource("textSteps", "TextSteps");
1304     if (si->text_steps < 0 || si->text_steps > 255)
1305       si->text_steps = 10;
1306
1307     si->text_colors = (XColor *) calloc(si->text_steps, sizeof(XColor));
1308     make_color_ramp (dpy, si->cmap,
1309                      h1, s1, v1,
1310                      h2, s2, v2,
1311                      si->text_colors, &si->text_steps,
1312                      False, True, False);
1313
1314     /* Compute the pixel values for the fading sweep */
1315
1316     XParseColor(dpy, si->cmap, 
1317                 get_string_resource("sweepColor", "SweepColor"), &start);
1318
1319     rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
1320
1321     si->sweep_degrees = get_integer_resource("sweepDegrees", "Degrees");
1322     if (si->sweep_degrees <= 0) si->sweep_degrees = 20;
1323     if (si->sweep_degrees > 350) si->sweep_degrees = 350;
1324
1325     si->sweep_segs = get_integer_resource("sweepSegments", "SweepSegments");
1326     if (si->sweep_segs < 1 || si->sweep_segs > 255)
1327       si->sweep_segs = 255;
1328
1329     si->sweep_colors = (XColor *) calloc(si->sweep_segs, sizeof(XColor));
1330     make_color_ramp (dpy, si->cmap,
1331                      h1, s1, v1,
1332                      h2, s2, v2,
1333                      si->sweep_colors, &si->sweep_segs,
1334                      False, True, False);
1335
1336     /* Done */
1337
1338     return si;
1339 }
1340
1341 /*
1342  * Update the location of a simulated bogie.
1343  */
1344
1345 static void
1346 updateLocation(sim_target *t) 
1347 {
1348
1349     int xdist, xtick;
1350
1351     t->movedlasttick = 1;
1352     xtick = (int) (3.0 * random() / RAND_MAX) - 1;
1353     xdist = (int) (11.0 * random() / RAND_MAX) - 5;
1354     if (((t->nexttick + xtick) < 90) && ((t->nexttick + xtick) >= 0))
1355         t->nexttick += xtick;
1356     else
1357         t->nexttick -= xtick;
1358     if (((t->nextdist + xdist) < 100) && ((t->nextdist+xdist) >= 0))
1359         t->nextdist += xdist;
1360     else
1361         t->nextdist -= xdist;
1362 }
1363
1364 /*
1365  * The simulator. This uses information in the sim_info to simulate a bunch
1366  * of bogies flying around on the screen.
1367  */
1368
1369 /*
1370  * TODO: It would be cool to have the two teams chase each other around and
1371  *       shoot it out.
1372  */
1373
1374 static Bogie *
1375 simulator(sonar_info *si, void *vinfo) 
1376 {
1377
1378     /* Local Variables */
1379
1380     int i;
1381     Bogie *list = NULL;
1382     Bogie *new;
1383     sim_target *t;
1384     sim_info *info = (sim_info *) vinfo;
1385
1386     /* Check team A */
1387
1388     for (i = 0; i < info->numA; i++) {
1389         t = &info->teamA[i];
1390         if (!t->movedlasttick && (t->nexttick == (si->current * -1))) {
1391             new = newBogie(strdup(t->name), t->nextdist, si->current, TTL);
1392             if (list != NULL)
1393                 new->next = list;
1394             list = new;
1395             updateLocation(t);
1396         } else
1397             t->movedlasttick = 0;
1398     }
1399
1400     /* Team B */
1401
1402     for (i = 0; i < info->numB; i++) {
1403         t = &info->teamB[i];
1404         if (!t->movedlasttick && (t->nexttick == (si->current * -1))) {
1405             new = newBogie(strdup(t->name), t->nextdist, si->current, TTL);
1406             if (list != NULL)
1407                 new->next = list;
1408             list = new;
1409             t->movedlasttick = 1;
1410             updateLocation(t);
1411         } else
1412             t->movedlasttick = 0;
1413     }
1414
1415     /* Done */
1416
1417     return list;
1418 }
1419
1420 /*
1421  * Compute the X coordinate of the label.
1422  *
1423  * Args:
1424  *    si - The sonar info block.
1425  *    label - The label that will be drawn.
1426  *    x - The x coordinate of the bogie.
1427  *
1428  * Returns:
1429  *    The x coordinate of the start of the label.
1430  */
1431
1432 static int
1433 computeStringX(sonar_info *si, char *label, int x) 
1434 {
1435
1436     int width = XTextWidth(si->font, label, strlen(label));
1437     return x - (width / 2);
1438 }
1439
1440 /*
1441  * Compute the Y coordinate of the label.
1442  *
1443  * Args:
1444  *    si - The sonar information.
1445  *    y - The y coordinate of the bogie.
1446  *
1447  * Returns:
1448  *    The y coordinate of the start of the label.
1449  */
1450
1451 /* TODO: Add smarts to keep label in sonar screen */
1452
1453 static int
1454 computeStringY(sonar_info *si, int y) 
1455 {
1456
1457     int fheight = si->font->ascent + si->font->descent;
1458     return y + 5 + fheight;
1459 }
1460
1461 /*
1462  * Draw a Bogie on the radar screen.
1463  *
1464  * Args:
1465  *    si       - Sonar Information.
1466  *    draw     - A flag to indicate if the bogie should be drawn or erased.
1467  *    name     - The name of the bogie.
1468  *    degrees  - The number of degrees that it should apprear at.
1469  *    distance - The distance the object is from the centre.
1470  *    ttl      - The time this bogie has to live.
1471  *    age      - The time this bogie has been around.
1472  */
1473
1474 static void
1475 DrawBogie(sonar_info *si, int draw, char *name, int degrees, 
1476           int distance, int ttl, int age) 
1477 {
1478
1479     /* Local Variables */
1480
1481     int x, y;
1482     GC gc;
1483     int ox = si->centrex;
1484     int oy = si->centrey;
1485     int index, delta;
1486
1487     /* Compute the coordinates of the object */
1488
1489     distance = (log((double) distance) / 10.0) * si->radius;
1490     x = ox + ((double) distance * cos(4.0 * ((double) degrees)/57.29578));
1491     y = oy - ((double) distance * sin(4.0 * ((double) degrees)/57.29578));
1492
1493     /* Set up the graphics context */
1494
1495     if (draw) {
1496
1497         /* Here we attempt to compute the distance into the total life of
1498          * object that we currently are. This distance is used against
1499          * the total lifetime to compute a fraction which is the index of
1500          * the color to draw the bogie.
1501          */
1502
1503         if (si->current <= degrees)
1504             delta = (si->current - degrees) * -1;
1505         else
1506             delta = 90 + (degrees - si->current);
1507         delta += (age * 90);
1508         index = (si->text_steps - 1) * ((float) delta / (90.0 * (float) ttl));
1509         gc = si->text;
1510         XSetForeground(si->dpy, gc, si->text_colors[index].pixel);
1511
1512     } else
1513         gc = si->erase;
1514
1515   /* Draw (or erase) the Bogie */
1516
1517     XFillArc(si->dpy, si->win, gc, x, y, 5, 5, 0, 360 * 64);
1518     XDrawString(si->dpy, si->win, gc,
1519                 computeStringX(si, name, x),
1520                 computeStringY(si, y), name, strlen(name));
1521 }
1522
1523
1524 /*
1525  * Draw the sonar grid.
1526  *
1527  * Args:
1528  *    si - Sonar information block.
1529  */
1530
1531 static void
1532 drawGrid(sonar_info *si) 
1533 {
1534
1535     /* Local Variables */
1536
1537     int i;
1538     int width = si->maxx - si->minx;
1539     int height = si->maxy - si->miny;
1540   
1541     /* Draw the circles */
1542
1543     XDrawArc(si->dpy, si->win, si->grid, si->minx - 10, si->miny - 10, 
1544              width + 20, height + 20,  0, (360 * 64));
1545
1546     XDrawArc(si->dpy, si->win, si->grid, si->minx, si->miny, 
1547              width, height,  0, (360 * 64));
1548
1549     XDrawArc(si->dpy, si->win, si->grid, 
1550              (int) (si->minx + (.166 * width)), 
1551              (int) (si->miny + (.166 * height)), 
1552              (unsigned int) (.666 * width), (unsigned int)(.666 * height),
1553              0, (360 * 64));
1554
1555     XDrawArc(si->dpy, si->win, si->grid, 
1556              (int) (si->minx + (.333 * width)),
1557              (int) (si->miny + (.333 * height)), 
1558              (unsigned int) (.333 * width), (unsigned int) (.333 * height),
1559              0, (360 * 64));
1560
1561     /* Draw the radial lines */
1562
1563     for (i = 0; i < 360; i += 10)
1564         if (i % 30 == 0)
1565             XDrawLine(si->dpy, si->win, si->grid, si->centrex, si->centrey,
1566                       (int) (si->centrex +
1567                       (si->radius + 10) * (cos((double) i / 57.29578))),
1568                       (int) (si->centrey -
1569                       (si->radius + 10)*(sin((double) i / 57.29578))));
1570         else
1571             XDrawLine(si->dpy, si->win, si->grid, 
1572                       (int) (si->centrex + si->radius *
1573                              (cos((double) i / 57.29578))),
1574                       (int) (si->centrey - si->radius *
1575                              (sin((double) i / 57.29578))),
1576                       (int) (si->centrex +
1577                       (si->radius + 10) * (cos((double) i / 57.29578))),
1578                       (int) (si->centrey - 
1579                       (si->radius + 10) * (sin((double) i / 57.29578))));
1580 }
1581
1582 /*
1583  * Update the Sonar scope.
1584  *
1585  * Args:
1586  *    si - The Sonar information.
1587  *    bl - A list  of bogies to add to the scope.
1588  */
1589
1590 static void
1591 Sonar(sonar_info *si, Bogie *bl) 
1592 {
1593
1594     /* Local Variables */
1595
1596     Bogie *bp, *prev;
1597     int i;
1598
1599     /* Check for expired tagets and remove them from the visable list */
1600
1601     prev = NULL;
1602     for (bp = si->visable; bp != NULL; bp = bp->next) {
1603
1604         /*
1605          * Remove it from the visable list if it's expired or we have
1606          * a new target with the same name.
1607          */
1608
1609         bp->age ++;
1610
1611         if (((bp->tick == si->current) && (++bp->age >= bp->ttl)) ||
1612             (findNode(bl, bp->name) != NULL)) {
1613             DrawBogie(si, 0, bp->name, bp->tick,
1614                       bp->distance, bp->ttl, bp->age);
1615             if (prev == NULL)
1616                 si->visable = bp->next;
1617             else
1618                 prev->next = bp->next;
1619             freeBogie(bp);
1620         } else
1621             prev = bp;
1622     }
1623
1624     /* Draw the sweep */
1625
1626     {
1627       int seg_deg = (si->sweep_degrees * 64) / si->sweep_segs;
1628       int start_deg = si->current * 4 * 64;
1629       if (seg_deg <= 0) seg_deg = 1;
1630       for (i = 0; i < si->sweep_segs; i++) {
1631         XSetForeground(si->dpy, si->hi, si->sweep_colors[i].pixel);
1632         XFillArc(si->dpy, si->win, si->hi, si->minx, si->miny, 
1633                  si->maxx - si->minx, si->maxy - si->miny,
1634                  start_deg + (i * seg_deg),
1635                  seg_deg);
1636       }
1637
1638       /* Remove the trailing wedge the sonar */
1639       XFillArc(si->dpy, si->win, si->erase, si->minx, si->miny, 
1640                si->maxx - si->minx, si->maxy - si->miny, 
1641                start_deg + (i * seg_deg),
1642                (4 * 64));
1643     }
1644
1645     /* Move the new targets to the visable list */
1646
1647     for (bp = bl; bp != (Bogie *) 0; bp = bl) {
1648         bl = bl->next;
1649         bp->next = si->visable;
1650         si->visable = bp;
1651     }
1652
1653     /* Draw the visable targets */
1654
1655     for (bp = si->visable; bp != NULL; bp = bp->next) {
1656         if (bp->age < bp->ttl)          /* grins */
1657            DrawBogie(si, 1, bp->name, bp->tick, bp->distance, bp->ttl,bp->age);
1658     }
1659
1660     /* Redraw the grid */
1661
1662     drawGrid(si);
1663 }
1664
1665 /*
1666  * Main screen saver hack.
1667  *
1668  * Args:
1669  *    dpy - The X display.
1670  *    win - The X window.
1671  */
1672
1673 void 
1674 screenhack(Display *dpy, Window win) 
1675 {
1676
1677     /* Local Variables */
1678
1679     sonar_info *si;
1680     struct timeval start, finish;
1681     Bogie *bl;
1682     long sleeptime;
1683     char *mode;
1684
1685     /* 
1686      * Initialize 
1687      * Adding new sensors would involve supporting more modes other than
1688      * ping and initiailizing the sensor in the same way.
1689      */
1690
1691     mode = get_string_resource("mode", "Mode");
1692
1693     if (!mode || !*mode || !strcmp(mode, "default")) /* Pick a good default. */
1694       {
1695 #ifdef HAVE_PING
1696         if (geteuid() == 0)     /* we're root or setuid -- ping will work. */
1697           mode = "ping";
1698         else
1699 #endif
1700           mode = "simulation";
1701       }
1702
1703 #ifdef HAVE_PING
1704     if (strcmp(mode, "ping") == 0) {
1705         sensor = ping;
1706         if ((sensor_info = (void *) init_ping()) == (void *) 0)
1707           {
1708             fprintf (stderr, "%s: running in `simulation mode' instead.\n",
1709                      progname);
1710             goto SIM;
1711           }
1712     } else
1713 #endif /* HAVE_PING */
1714     if (strcmp(mode, "simulation") == 0) {
1715 #ifdef HAVE_PING
1716     SIM:
1717 #endif
1718         sensor = simulator;
1719         if ((sensor_info = (void *) init_sim()) == NULL)
1720             exit(1);
1721     } else {
1722         fprintf(stderr, "%s: unsupported Sonar mode: %s\n", progname, mode);
1723         fprintf(stderr,
1724                 "\tCurrently supported modes are `ping' and `simulation'\n");
1725         exit(1);
1726     }
1727     if ((si = init_sonar(dpy, win)) == (sonar_info *) 0)
1728         exit(1);
1729   
1730     /* Sonar loop */
1731
1732     TTL = get_integer_resource("ttl", "TTL");
1733
1734     while (1) {
1735
1736         /* Call the sensor and display the results */
1737
1738         gettimeofday(&start, (struct timezone *) 0);
1739         bl = sensor(si, sensor_info);
1740         Sonar(si, bl);
1741
1742         /* Set up and sleep for the next one */
1743
1744         si->current = (si->current - 1) % 90;
1745         XSync (dpy, False);
1746         gettimeofday(&finish, (struct timezone *) 0);
1747         sleeptime = si->delay - delta(&start, &finish);
1748         screenhack_handle_events (dpy);
1749         if (sleeptime > 0L)
1750             usleep(sleeptime);
1751     }
1752 }