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