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