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