1 /* sonar.c --- Simulate a sonar screen.
2 * Copyright (C) 1998-2006 by Stephen Martin and Jamie Zawinski
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * This is an implementation of a general purpose reporting tool in the
12 * format of a Sonar display. It is designed such that a sensor is read
13 * on every movement of a sweep arm and the results of that sensor are
14 * displayed on the screen. The location of the display points (targets) on the
15 * screen are determined by the current localtion of the sweep and a distance
16 * value associated with the target.
18 * Currently the only two sensors that are implemented are the simulator
19 * (the default) and the ping sensor. The simulator randomly creates a set
20 * of bogies that move around on the scope while the ping sensor can be
21 * used to display hosts on your network.
23 * The ping code is only compiled in if you define HAVE_ICMP or HAVE_ICMPHDR,
24 * because, unfortunately, different systems have different ways of creating
25 * these sorts of packets.
27 * In order to use the ping sensor on most systems, this program must be
28 * installed as setuid root, so that it can create an ICMP RAW socket. Root
29 * privileges are disavowed shortly after startup (just after connecting to
30 * the X server and reading the resource database) so this is *believed* to
31 * be a safe thing to do, but it is usually recommended that you have as few
32 * setuid programs around as possible, on general principles.
34 * It is not necessary to make it setuid on MacOS systems, because on those
35 * systems, unprivileged programs can ping by using ICMP DGRAM sockets
36 * instead of ICMP RAW.
38 * It should be easy to extend this code to support other sorts of sensors.
40 * - search the output of "netstat" for the list of hosts to ping;
41 * - plot the contents of /proc/interrupts;
42 * - plot the process table, by process size, cpu usage, or total time;
43 * - plot the logged on users by idle time or cpu usage.
47 * Version 1.0 April 27, 1998.
48 * - Initial version, by Stephen Martin <smartin@vanderfleet-martin.net>
49 * - Submitted to RedHat Screensaver Contest
51 * Version 1.1 November 3, 1998.
52 * - Added simulation mode.
53 * - Added enhancements by Thomas Bahls <thommy@cs.tu-berlin.de>
54 * - Fixed huge memory leak.
55 * - Submitted to xscreensavers
58 * - All ping code is now ifdef-ed by the compile time symbol HAVE_PING;
59 * use -DHAVE_PING to include it when you compile.
60 * - Sweep now uses gradients.
61 * - Fixed portability problems with icmphdr on some systems.
62 * - removed lowColor option/resource.
63 * - changed copyright notice so that it could be included in the xscreensavers
66 * Version 1.3 November 16, 1998.
67 * - All ping code is now ifdef-ed by the compile time symbol PING use -DPING
68 * to include it when you compile.
69 * - Sweep now uses gradients.
70 * - Fixed portability problems with icmphdr on some systems.
71 * - removed lowcolour option/resource.
72 * - changed copyright notice so that it could be included in the xscreensavers
75 * Version 1.4 November 18, 1998.
76 * - More ping portability fixes.
78 * Version 1.5 November 19, 1998.
79 * - Synced up with jwz's changes.
80 * - Now need to define HAVE_PING to compile in the ping stuff.
84 /* These are computed by configure now:
97 #include "screenhack.h"
101 #undef usleep /* conflicts with unistd.h on OSX */
103 #if defined(HAVE_ICMP) || defined(HAVE_ICMPHDR)
108 # include <sys/types.h>
109 # include <sys/time.h>
110 # include <sys/ipc.h>
111 # include <sys/shm.h>
112 # include <sys/socket.h>
113 # include <netinet/in_systm.h>
114 # include <netinet/in.h>
115 # include <netinet/ip.h>
116 # include <netinet/ip_icmp.h>
117 # include <netinet/udp.h>
118 # include <arpa/inet.h>
120 #endif /* HAVE_ICMP || HAVE_ICMPHDR */
126 #define MY_MIN(a,b) ((a)<(b)?(a - 50):(b - 10))
129 # define LINE_MAX 2048
134 #if defined(HAVE_ICMP)
137 # define ICMP_TYPE(p) (p)->icmp_type
138 # define ICMP_CODE(p) (p)->icmp_code
139 # define ICMP_CHECKSUM(p) (p)->icmp_cksum
140 # define ICMP_ID(p) (p)->icmp_id
141 # define ICMP_SEQ(p) (p)->icmp_seq
142 #elif defined(HAVE_ICMPHDR)
144 # define ICMP icmphdr
145 # define ICMP_TYPE(p) (p)->type
146 # define ICMP_CODE(p) (p)->code
147 # define ICMP_CHECKSUM(p) (p)->checksum
148 # define ICMP_ID(p) (p)->un.echo.id
149 # define ICMP_SEQ(p) (p)->un.echo.sequence
156 # if defined(__DECC) || defined(_IP_VHL)
157 /* This is how you do it on DEC C, and possibly some BSD systems. */
158 # define IP_HDRLEN(ip) ((ip)->ip_vhl & 0x0F)
160 /* This is how you do it on everything else. */
161 # define IP_HDRLEN(ip) ((ip)->ip_hl)
163 #endif /* HAVE_PING */
166 /* Forward References */
169 static u_short checksum(u_short *, int);
171 static long delta(struct timeval *, struct timeval *);
174 /* Data Structures */
179 * This represents an object that is visible on the scope.
182 typedef struct Bogie {
183 char *name; /* The name of the thing being displayed */
184 char *desc; /* Beneath the name (e.g., ping time) */
185 int distance; /* The distance to this thing (0 - 100) */
186 int tick; /* The tick that it was found on */
187 int ttl; /* The time to live */
188 int age; /* How long it's been around */
189 struct Bogie *next; /* The next one in the list */
195 * This contains all of the runtime information about the sonar scope.
198 typedef struct ping_target ping_target;
200 typedef struct sonar_info sonar_info;
202 Display *dpy; /* The X display */
203 Window win; /* The window */
204 GC hi, /* The leading edge of the sweep */
205 lo, /* The trailing part of the sweep */
206 erase, /* Used to erase things */
207 grid, /* Used to draw the grid */
208 text; /* Used to draw text */
209 Colormap cmap; /* The colormap */
210 XFontStruct *font; /* The font to use for the labels */
211 int text_steps; /* How many steps to fade text. */
212 XColor *text_colors; /* Pixel values used to fade text */
213 int sweep_degrees; /* How much of the circle the sweep uses */
214 int sweep_segs; /* How many gradients in the sweep. */
215 XColor *sweep_colors; /* The sweep pixel values */
216 int width, height; /* Window dimensions */
217 int minx, miny, maxx, maxy, /* Bounds of the scope */
218 centrex, centrey, radius; /* Parts of the scope circle */
219 Bogie *visible; /* List of visible objects */
220 int current; /* Current position of sweep */
221 int sweepnum; /* The current id of the sweep */
222 int delay; /* how long between each frame of the anim */
224 int TTL; /* The number of ticks that bogies are visible
225 on the screen before they fade away. */
227 ping_target *last_ptr;
229 Bogie *(*sensor)(sonar_info *, void *); /* The current sensor */
230 void *sensor_info; /* Information about the sensor */
234 static Bool debug_p = False;
235 static Bool resolve_p = True;
236 static Bool times_p = True;
240 * A list of targets to ping.
244 char *name; /* The name of the target */
246 struct sockaddr address; /* The address of the target */
247 #endif /* HAVE_PING */
248 struct ping_target *next; /* The next one in the list */
256 * This contains the information for the ping sensor.
260 int icmpsock; /* Socket for sending pings */
261 int pid; /* Our process ID */
262 int seq; /* Packet sequence number */
263 int timeout; /* Timeout value for pings */
264 ping_target *targets; /* List of targets to ping */
265 int numtargets; /* The number of targets to ping */
268 /* Flag to indicate that the timer has expired on us */
270 static int timer_expired;
272 #endif /* HAVE_PING */
275 * A list of targets for the simulator
278 typedef struct sim_target {
279 char *name; /* The name of the target */
280 int nexttick; /* The next tick that this will be seen */
281 int nextdist; /* The distance on that tick */
282 int movedonsweep; /* The number of the sweep this last moved */
286 * Simulator Information.
288 * This contains the information for the simulator mode.
292 sim_target *teamA; /* The bogies for the A team */
293 int numA; /* The number of bogies in team A */
294 char *teamAID; /* The identifier for bogies in team A */
295 sim_target *teamB; /* The bogies for the B team */
296 int numB; /* The number of bogies in team B */
297 char *teamBID; /* The identifier for bogies in team B */
303 * Create a new Bogie and set some initial values.
306 * name - The name of the bogie.
307 * distance - The distance value.
308 * tick - The tick value.
309 * ttl - The time to live value.
312 * The newly allocated bogie or null if a memory problem occured.
316 newBogie(char *name, int distance, int tick, int ttl)
319 /* Local Variables */
324 /* Allocate a bogie and initialize it */
326 if ((new = (Bogie *) calloc(1, sizeof(Bogie))) == NULL) {
327 fprintf(stderr, "%s: Out of Memory\n", progname);
331 new->distance = distance;
335 new->next = (Bogie *) 0;
343 * b - The bogie to free.
350 if (b->name != (char *) 0)
356 * Find a bogie by name in a list.
358 * This does a simple linear search of the list for a given name.
361 * bl - The Bogie list to search.
362 * name - The name to look for.
365 * The requested Bogie or null if it wasn't found.
369 findNode(Bogie *bl, char *name)
372 /* Local Variables */
376 /* Abort if the list is empty or no name is given */
378 if ((name == NULL) || (bl == NULL))
381 /* Search the list for the desired name */
385 if (strcmp(p->name, name) == 0)
397 /* Packs an IP address quad into bigendian network order. */
399 pack_addr (unsigned int a, unsigned int b, unsigned int c, unsigned int d)
401 unsigned long i = (((a & 255) << 24) |
408 /* Unpacks an IP address quad from bigendian network order. */
410 unpack_addr (unsigned long addr,
411 unsigned int *a, unsigned int *b,
412 unsigned int *c, unsigned int *d)
415 *a = (addr >> 24) & 255;
416 *b = (addr >> 16) & 255;
417 *c = (addr >> 8) & 255;
423 * Lookup the address for a ping target;
426 * target - The ping_target fill in the address for.
429 * 1 if the host was successfully resolved, 0 otherwise.
433 lookupHost(ping_target *target)
435 struct hostent *hent;
436 struct sockaddr_in *iaddr;
441 iaddr = (struct sockaddr_in *) &(target->address);
442 iaddr->sin_family = AF_INET;
444 if (4 == sscanf (target->name, " %u.%u.%u.%u %c",
445 &ip[0], &ip[1], &ip[2], &ip[3], &c))
447 /* It's an IP address.
452 fprintf (stderr, "%s: ignoring bogus IP %s\n",
453 progname, target->name);
457 iaddr->sin_addr.s_addr = pack_addr (ip[0], ip[1], ip[2], ip[3]);
459 hent = gethostbyaddr ((const char *) &iaddr->sin_addr.s_addr,
460 sizeof(iaddr->sin_addr.s_addr),
466 fprintf (stderr, "%s: %s => %s\n",
467 progname, target->name,
468 ((hent && hent->h_name && *hent->h_name)
469 ? hent->h_name : "<unknown>"));
471 if (hent && hent->h_name && *hent->h_name)
472 target->name = strdup (hent->h_name);
480 /* don't waste time being confused by non-hostname tokens
481 in .ssh/known_hosts */
482 if (!strcmp (target->name, "ssh-rsa") ||
483 !strcmp (target->name, "ssh-dsa") ||
484 !strcmp (target->name, "ssh-dss") ||
485 strlen (target->name) >= 80)
488 hent = gethostbyname (target->name);
492 fprintf (stderr, "%s: could not resolve host: %s\n",
493 progname, target->name);
497 memcpy (&iaddr->sin_addr, hent->h_addr_list[0],
498 sizeof(iaddr->sin_addr));
502 unsigned int a, b, c, d;
503 unpack_addr (iaddr->sin_addr.s_addr, &a, &b, &c, &d);
504 fprintf (stderr, "%s: %s => %d.%d.%d.%d\n",
505 progname, target->name, a, b, c, d);
513 print_host (FILE *out, unsigned long ip, const char *name)
516 unsigned int a, b, c, d;
517 unpack_addr (ip, &a, &b, &c, &d); /* ip is in network order */
518 sprintf (ips, "%u.%u.%u.%u", a, b, c, d);
519 if (!name || !*name) name = "<unknown>";
520 fprintf (out, "%-16s %s\n", ips, name);
525 * Create a target for a host.
528 * name - The name of the host.
531 * A newly allocated target or null if the host could not be resolved.
538 /* Local Variables */
540 ping_target *target = NULL;
542 /* Create the target */
544 if ((target = calloc(1, sizeof(ping_target))) == NULL) {
545 fprintf(stderr, "%s: Out of Memory\n", progname);
546 goto target_init_error;
548 if ((target->name = strdup(name)) == NULL) {
549 fprintf(stderr, "%s: Out of Memory\n", progname);
550 goto target_init_error;
553 /* Lookup the host */
555 if (! lookupHost(target))
556 goto target_init_error;
558 /* Don't ever use loopback (127.0.0.x) hosts */
560 struct sockaddr_in *iaddr = (struct sockaddr_in *) &(target->address);
561 unsigned long ip = iaddr->sin_addr.s_addr;
563 if ((ntohl (ip) & 0xFFFFFF00L) == 0x7f000000L) /* 127.0.0.x */
566 fprintf (stderr, "%s: ignoring loopback host %s\n",
567 progname, target->name);
568 goto target_init_error;
572 /* Don't ever use broadcast (255.x.x.x) hosts */
574 struct sockaddr_in *iaddr = (struct sockaddr_in *) &(target->address);
575 unsigned long ip = iaddr->sin_addr.s_addr;
576 if ((ntohl (ip) & 0xFF000000L) == 0xFF000000L) /* 255.x.x.x */
579 fprintf (stderr, "%s: ignoring broadcast host %s\n",
580 progname, target->name);
581 goto target_init_error;
589 struct sockaddr_in *iaddr = (struct sockaddr_in *) &(target->address);
590 unsigned long ip = iaddr->sin_addr.s_addr;
591 fprintf (stderr, "%s: added ", progname);
592 print_host (stderr, ip, target->name);
597 /* Handle errors here */
606 * Generate a list of ping targets from the entries in a file.
609 * fname - The name of the file. This file is expected to be in the same
610 * format as /etc/hosts.
613 * A list of targets to ping or null if an error occured.
617 readPingHostsFile(char *fname)
619 /* Local Variables */
624 ping_target *list = NULL;
628 /* Make sure we in fact have a file to process */
630 if ((fname == NULL) || (fname[0] == '\0')) {
631 fprintf(stderr, "%s: invalid ping host file name\n", progname);
635 /* Kludge: on OSX, variables have not been expanded in the command
636 line arguments, so as a special case, allow the string to begin
637 with literal "$HOME/" or "~/".
639 This is so that the "Known Hosts" menu item in sonar.xml works.
641 if (!strncmp(fname, "~/", 2) || !strncmp(fname, "$HOME/", 6)) {
642 char *s = strchr (fname, '/');
643 strcpy (buf, getenv("HOME"));
650 if ((fp = fopen(fname, "r")) == NULL) {
652 sprintf(msg, "%s: unable to open host file %s", progname, fname);
654 if (debug_p) /* on OSX don't syslog this */
661 fprintf (stderr, "%s: reading file %s\n", progname, fname);
663 /* Read the file line by line */
665 while ((p = fgets(buf, LINE_MAX, fp)) != NULL) {
668 * Parse the line skipping those that start with '#'.
669 * The rest of the lines in the file should be in the same
670 * format as a /etc/hosts file. We are only concerned with
671 * the first two field, the IP address and the name
674 while ((*p == ' ') || (*p == '\t'))
679 /* Get the name and address */
682 if ((addr = strtok(buf, " ,;\t\n")) != NULL)
683 name = strtok(NULL, " ,;\t\n");
687 /* Check to see if the addr looks like an addr. If not, assume
688 the addr is a name and there is no addr. This way, we can
689 handle files whose lines have "xx.xx.xx.xx hostname" as their
690 first two tokens, and also files that have a hostname as their
691 first token (like .ssh/known_hosts and .rhosts.)
695 if (4 != sscanf(addr, "%d.%d.%d.%d%c", &i, &i, &i, &i, &c))
702 /* If the name is all digits, it's not a name. */
706 for (s = name; *s; s++)
707 if (*s < '0' || *s > '9')
712 fprintf (stderr, "%s: skipping bogus name \"%s\" (%s)\n",
713 progname, name, addr);
718 /* Create a new target using first the name then the address */
723 if (new == NULL && addr != NULL)
726 /* Add it to the list if we got one */
734 /* Close the file and return the list */
742 delete_duplicate_hosts (ping_target *list)
744 ping_target *head = list;
747 for (rest = head; rest; rest = rest->next)
749 struct sockaddr_in *i1 = (struct sockaddr_in *) &(rest->address);
750 unsigned long ip1 = i1->sin_addr.s_addr;
753 for (rest2 = rest; rest2; rest2 = rest2->next)
755 if (rest2 && rest2->next)
757 struct sockaddr_in *i2 = (struct sockaddr_in *)
758 &(rest2->next->address);
759 unsigned long ip2 = i2->sin_addr.s_addr;
765 fprintf (stderr, "%s: deleted duplicate: ", progname);
766 print_host (stderr, ip2, rest2->next->name);
768 rest2->next = rest2->next->next;
781 * Generate a list ping targets consisting of all of the entries on
782 * the same subnet. 'base' ip is in network order; 0 means localhost.
785 * A list of all of the hosts on this net.
789 subnetHostsList(unsigned long n_base, int subnet_width)
791 unsigned long h_mask; /* host order */
792 unsigned long h_base; /* host order */
794 /* Local Variables */
796 char hostname[BUFSIZ];
797 char address[BUFSIZ];
798 struct hostent *hent;
802 ping_target *list = NULL;
804 if (subnet_width < 24)
807 "%s: pinging %lu hosts is a bad idea; please use a subnet mask of 24 bits\n"
808 " or more (255 hosts max.)\n",
809 progname, (unsigned long) (1L << (32 - subnet_width)) - 1);
812 else if (subnet_width > 30)
814 fprintf (stderr, "%s: a subnet of %d bits doesn't make sense:"
815 " try \"subnet/24\" or \"subnet/29\".\n",
816 progname, subnet_width);
822 fprintf (stderr, "%s: adding %d-bit subnet\n", progname, subnet_width);
824 /* Get our hostname */
826 if (gethostname(hostname, BUFSIZ)) {
827 fprintf(stderr, "%s: unable to get local hostname\n", progname);
831 /* Get our IP address and convert it to a string */
833 if ((hent = gethostbyname(hostname)) == NULL) {
834 fprintf(stderr, "%s: unable to lookup our IP address\n", progname);
837 strcpy(address, inet_ntoa(*((struct in_addr *)hent->h_addr_list[0])));
839 /* Construct targets for all addresses in this subnet */
842 for (i = 0; i < subnet_width; i++)
843 h_mask |= (1L << (31-i));
845 /* If no base IP specified, assume localhost. */
847 n_base = pack_addr (hent->h_addr_list[0][0],
848 hent->h_addr_list[0][1],
849 hent->h_addr_list[0][2],
850 hent->h_addr_list[0][3]);
851 h_base = ntohl (n_base);
853 if (h_base == 0x7F000001L) /* 127.0.0.1 in host order */
855 unsigned int a, b, c, d;
856 unpack_addr (n_base, &a, &b, &c, &d);
858 "%s: unable to determine local subnet address: \"%s\"\n"
859 " resolves to loopback address %u.%u.%u.%u.\n",
860 progname, hostname, a, b, c, d);
864 for (i = 255; i >= 0; i--) {
865 unsigned int a, b, c, d;
866 int ip = (h_base & 0xFFFFFF00L) | i; /* host order */
868 if ((ip & h_mask) != (h_base & h_mask)) /* not in mask range at all */
870 if ((ip & ~h_mask) == 0) /* broadcast address */
872 if ((ip & ~h_mask) == ~h_mask) /* broadcast address */
875 unpack_addr (htonl (ip), &a, &b, &c, &d);
876 sprintf (address, "%u.%u.%u.%u", a, b, c, d);
880 unsigned int aa, ab, ac, ad;
881 unsigned int ma, mb, mc, md;
882 unpack_addr (htonl (h_base & h_mask), &aa, &ab, &ac, &ad);
883 unpack_addr (htonl (h_mask), &ma, &mb, &mc, &md);
885 "%s: subnet: %s (%u.%u.%u.%u & %u.%u.%u.%u / %d)\n",
892 p = address + strlen(address) + 1;
895 new = newHost(address);
908 * Initialize the ping sensor.
911 * A newly allocated ping_info structure or null if an error occured.
914 static ping_target *parse_mode (sonar_info *, Bool ping_works_p);
916 /* yes, there is only one, even when multiple savers are running in the
917 same address space - since we can only open this socket before dropping
920 static int global_icmpsock = 0;
923 init_ping(sonar_info *si)
926 Bool socket_initted_p = False;
927 Bool socket_raw_p = False;
929 /* Local Variables */
931 ping_info *pi = NULL; /* The new ping_info struct */
932 ping_target *pt; /* Used to count the targets */
934 /* Create the ping info structure */
936 if ((pi = (ping_info *) calloc(1, sizeof(ping_info))) == NULL) {
937 fprintf(stderr, "%s: Out of memory\n", progname);
938 goto ping_init_error;
941 /* Create the ICMP socket. Do this before dropping privs.
943 Raw sockets can only be opened by root (or setuid root), so we
944 only try to do this when the effective uid is 0.
946 We used to just always try, and notice the failure. But apparently
947 that causes "SELinux" to log spurious warnings when running with the
948 "strict" policy. So to avoid that, we just don't try unless we
951 On MacOS X, we can avoid the whole problem by using a
952 non-privileged datagram instead of a raw socket.
954 if (global_icmpsock) {
955 pi->icmpsock = global_icmpsock;
956 socket_initted_p = True;
958 fprintf (stderr, "%s: re-using icmp socket\n", progname);
960 } else if ((pi->icmpsock =
961 socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) >= 0) {
962 socket_initted_p = True;
964 } else if (geteuid() == 0 &&
965 (pi->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) >= 0) {
966 socket_initted_p = True;
970 if (socket_initted_p) {
971 global_icmpsock = pi->icmpsock;
972 socket_initted_p = True;
974 fprintf (stderr, "%s: opened %s icmp socket\n", progname,
975 (socket_raw_p ? "raw" : "dgram"));
977 fprintf (stderr, "%s: unable to open icmp socket\n", progname);
984 pi->pid = getpid() & 0xFFFF;
986 pi->timeout = get_integer_resource(si->dpy, "pingTimeout", "PingTimeout");
988 /* Generate a list of targets */
990 pi->targets = parse_mode (si, socket_initted_p);
991 pi->targets = delete_duplicate_hosts (pi->targets);
997 fprintf (stderr, "%s: Target list:\n", progname);
998 for (t = pi->targets; t; t = t->next)
1000 struct sockaddr_in *iaddr = (struct sockaddr_in *) &(t->address);
1001 unsigned long ip = iaddr->sin_addr.s_addr;
1002 fprintf (stderr, "%s: ", progname);
1003 print_host (stderr, ip, t->name);
1007 /* Make sure there is something to ping */
1009 if (pi->targets == NULL) {
1010 goto ping_init_error;
1013 /* Count the targets */
1017 while (pt != NULL) {
1026 /* Handle initialization errors here */
1039 * pi - The ping information strcuture.
1040 * host - The name or IP address of the host to ping (in ascii).
1044 sendping(ping_info *pi, ping_target *pt)
1047 /* Local Variables */
1054 * Note, we will send the character name of the host that we are
1055 * pinging in the packet so that we don't have to keep track of the
1056 * name or do an address lookup when it comes back.
1059 int pcktsiz = sizeof(struct ICMP) + sizeof(struct timeval) +
1060 strlen(pt->name) + 1;
1062 /* Create the ICMP packet */
1064 if ((packet = (u_char *) malloc(pcktsiz)) == (void *) 0)
1065 return; /* Out of memory */
1066 icmph = (struct ICMP *) packet;
1067 ICMP_TYPE(icmph) = ICMP_ECHO;
1068 ICMP_CODE(icmph) = 0;
1069 ICMP_CHECKSUM(icmph) = 0;
1070 ICMP_ID(icmph) = pi->pid;
1071 ICMP_SEQ(icmph) = pi->seq++;
1072 # ifdef GETTIMEOFDAY_TWO_ARGS
1073 gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)],
1074 (struct timezone *) 0);
1076 gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)]);
1079 strcpy((char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)],
1081 ICMP_CHECKSUM(icmph) = checksum((u_short *)packet, pcktsiz);
1085 if ((result = sendto(pi->icmpsock, packet, pcktsiz, 0,
1086 &pt->address, sizeof(pt->address))) != pcktsiz) {
1088 char errbuf[BUFSIZ];
1089 sprintf(errbuf, "%s: error sending ping to %s", progname, pt->name);
1096 * Catch a signal and do nothing.
1099 * sig - The signal that was caught.
1109 * Compute the checksum on a ping packet.
1112 * packet - A pointer to the packet to compute the checksum for.
1113 * size - The size of the packet.
1116 * The computed checksum
1121 checksum(u_short *packet, int size)
1124 /* Local Variables */
1126 register int nleft = size;
1127 register u_short *w = packet;
1128 register int sum = 0;
1132 * Our algorithm is simple, using a 32 bit accumulator (sum), we add
1133 * sequential 16 bit words to it, and at the end, fold back all the
1134 * carry bits from the top 16 bits into the lower 16 bits.
1142 /* mop up an odd byte, if necessary */
1145 *(u_char *)(&answer) = *(u_char *)w ;
1146 *(1 + (u_char *)(&answer)) = 0;
1150 /* add back carry outs from top 16 bits to low 16 bits */
1152 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
1153 sum += (sum >> 16); /* add carry */
1154 answer = ~sum; /* truncate to 16 bits */
1162 * Look for ping replies.
1164 * Retrieve all outstanding ping replies.
1167 * si - Information about the sonar.
1168 * pi - Ping information.
1169 * ttl - The time each bogie is to live on the screen
1172 * A Bogie list of all the machines that replied.
1176 getping(sonar_info *si, ping_info *pi)
1179 /* Local Variables */
1181 struct sockaddr from;
1182 unsigned int fromlen; /* Posix says socklen_t, but that's not portable */
1184 u_char packet[1024];
1186 struct timeval *then;
1193 struct sigaction sa;
1194 struct itimerval it;
1198 /* Set up a signal to interupt our wait for a packet */
1200 sigemptyset(&sa.sa_mask);
1202 sa.sa_handler = sigcatcher;
1203 if (sigaction(SIGALRM, &sa, 0) == -1) {
1205 sprintf(msg, "%s: unable to trap SIGALRM", progname);
1210 /* Set up a timer to interupt us if we don't get a packet */
1212 it.it_interval.tv_sec = 0;
1213 it.it_interval.tv_usec = 0;
1214 it.it_value.tv_sec = 0;
1215 it.it_value.tv_usec = pi->timeout;
1217 setitimer(ITIMER_REAL, &it, NULL);
1219 /* Wait for a result packet */
1221 fromlen = sizeof(from);
1222 while (! timer_expired) {
1223 tv.tv_usec=pi->timeout;
1226 /* This breaks on BSD, which uses bzero() in the definition of FD_ZERO */
1229 memset (&rfds, 0, sizeof(rfds));
1231 FD_SET(pi->icmpsock,&rfds);
1232 /* only wait a little while, in case we raced with the timer expiration.
1233 From Valentijn Sessink <valentyn@openoffice.nl> */
1234 if (select(pi->icmpsock+1, &rfds, NULL, NULL, &tv) >0) {
1235 result = recvfrom(pi->icmpsock, packet, sizeof(packet),
1236 0, &from, &fromlen);
1238 /* Check the packet */
1240 # ifdef GETTIMEOFDAY_TWO_ARGS
1241 gettimeofday(&now, (struct timezone *) 0);
1245 ip = (struct ip *) packet;
1246 iphdrlen = IP_HDRLEN(ip) << 2;
1247 icmph = (struct ICMP *) &packet[iphdrlen];
1248 then = (struct timeval *) &packet[iphdrlen + sizeof(struct ICMP)];
1251 /* Was the packet a reply?? */
1253 if (ICMP_TYPE(icmph) != ICMP_ECHOREPLY) {
1254 /* Ignore anything but ICMP Replies */
1255 continue; /* Nope */
1258 /* Was it for us? */
1260 if (ICMP_ID(icmph) != pi->pid) {
1261 /* Ignore packets not set from us */
1262 continue; /* Nope */
1265 /* Copy the name of the bogie */
1268 strdup((char *) &packet[iphdrlen +
1269 + sizeof(struct ICMP)
1270 + sizeof(struct timeval)])) == NULL) {
1271 fprintf(stderr, "%s: Out of memory\n", progname);
1275 # if 0 /* Don't need to do this -- the host names are already as
1276 resolved as they're going to get. (We stored the resolved
1277 name in the outgoing ping packet, so that same string just
1281 /* If the name is an IP addr, try to resolve it. */
1285 if (4 == sscanf(name, " %d.%d.%d.%d %c",
1286 &iip[0], &iip[1], &iip[2], &iip[3], &c))
1288 struct sockaddr_in iaddr;
1290 iaddr.sin_addr.s_addr = pack_addr (iip[0],iip[1],iip[2],iip[3]);
1292 h = gethostbyaddr ((const char *) &iaddr.sin_addr.s_addr,
1293 sizeof(iaddr.sin_addr.s_addr),
1298 if (h && h->h_name && *h->h_name)
1301 name = strdup (h->h_name);
1307 /* Create the new Bogie and add it to the list we are building */
1309 if ((new = newBogie(name, 0, si->current, si->TTL)) == NULL)
1315 float msec = delta(then, &now) / 1000.0;
1319 if (new->desc) free (new->desc);
1320 new->desc = (char *) malloc (30);
1321 if (msec > 99) sprintf (new->desc, " %.0f ms ", msec);
1322 else if (msec > 9) sprintf (new->desc, " %.1f ms ", msec);
1323 else if (msec > 1) sprintf (new->desc, " %.2f ms ", msec);
1324 else sprintf (new->desc, " %.3f ms ", msec);
1327 if (debug_p && times_p) /* print ping-like stuff to stdout */
1329 struct sockaddr_in *iaddr = (struct sockaddr_in *) &from;
1330 unsigned int a, b, c, d;
1332 char *s = strdup (new->desc);
1333 char *s2 = s, *s3 = s;
1334 while (*s2 == ' ') s2++;
1335 s3 = strchr (s2, ' ');
1338 unpack_addr (iaddr->sin_addr.s_addr, &a, &b, &c, &d);
1339 sprintf (ipstr, "%d.%d.%d.%d", a, b, c, d);
1342 "%3d bytes from %28s: "
1343 "icmp_seq=%-4d ttl=%d time=%s ms\n",
1347 ICMP_SEQ(icmph), si->TTL, s2);
1351 /* Don't put anyone *too* close to the center of the screen. */
1354 new->distance = msec * 10;
1368 * si - Sonar Information.
1369 * pi - Ping Information.
1372 * A list of hosts that replied to pings or null if there were none.
1376 ping(sonar_info *si, void *vpi)
1380 * This tries to distribute the targets evely around the field of the
1384 ping_info *pi = (ping_info *) vpi;
1386 int tick = si->current * -1 + 1;
1387 if ((si->last_ptr == NULL) && (tick == 1))
1388 si->last_ptr = pi->targets;
1390 if (pi->numtargets <= 90) {
1391 int xdrant = 90 / pi->numtargets;
1392 if ((tick % xdrant) == 0) {
1393 if (si->last_ptr != (ping_target *) 0) {
1394 sendping(pi, si->last_ptr);
1395 si->last_ptr = si->last_ptr->next;
1399 } else if (pi->numtargets > 90) {
1400 if (si->last_ptr != (ping_target *) 0) {
1401 sendping(pi, si->last_ptr);
1402 si->last_ptr = si->last_ptr->next;
1406 /* Get the results */
1408 return getping(si, pi);
1411 #endif /* HAVE_PING */
1414 * Calculate the difference between two timevals in microseconds.
1417 * then - The older timeval.
1418 * now - The newer timeval.
1421 * The difference between the two in microseconds.
1425 delta(struct timeval *then, struct timeval *now)
1427 return (((now->tv_sec - then->tv_sec) * 1000000) +
1428 (now->tv_usec - then->tv_usec));
1432 * Initialize the simulation mode.
1435 #define BELLRAND(x) (((random()%(x)) + (random()%(x)) + (random()%(x)))/3)
1438 init_sim(Display *dpy)
1440 /* Local Variables */
1445 int maxdist = 20; /* larger than this is off the (log) display */
1447 /* Create the simulation info structure */
1449 if ((si = (sim_info *) calloc(1, sizeof(sim_info))) == NULL) {
1450 fprintf(stderr, "%s: Out of memory\n", progname);
1456 si->numA = get_integer_resource(dpy, "teamACount", "TeamACount");
1457 if ((si->teamA = (sim_target *)calloc(si->numA, sizeof(sim_target)))
1460 fprintf(stderr, "%s: Out of Memory\n", progname);
1463 si->teamAID = get_string_resource(dpy, "teamAName", "TeamAName");
1464 for (i = 0; i < si->numA; i++) {
1465 if ((si->teamA[i].name = (char *) malloc(strlen(si->teamAID) + 4))
1468 fprintf(stderr, "%s: Out of Memory\n", progname);
1471 sprintf(si->teamA[i].name, "%s%03d", si->teamAID, i+1);
1472 si->teamA[i].nexttick = random() % 90;
1473 si->teamA[i].nextdist = BELLRAND(maxdist);
1474 si->teamA[i].movedonsweep = -1;
1479 si->numB = get_integer_resource(dpy, "teamBCount", "TeamBCount");
1480 if ((si->teamB = (sim_target *)calloc(si->numB, sizeof(sim_target)))
1483 fprintf(stderr, "%s: Out of Memory\n", progname);
1486 si->teamBID = get_string_resource(dpy, "teamBName", "TeamBName");
1487 for (i = 0; i < si->numB; i++) {
1488 if ((si->teamB[i].name = (char *) malloc(strlen(si->teamBID) + 4))
1491 fprintf(stderr, "%s: Out of Memory\n", progname);
1494 sprintf(si->teamB[i].name, "%s%03d", si->teamBID, i+1);
1495 si->teamB[i].nexttick = random() % 90;
1496 si->teamB[i].nextdist = BELLRAND(maxdist);
1497 si->teamB[i].movedonsweep = -1;
1506 * Creates and returns a drawing mask for the scope:
1507 * mask out anything outside of the disc.
1510 scope_mask (Display *dpy, Window win, sonar_info *si)
1513 Pixmap mask = XCreatePixmap(dpy, win, si->width, si->height, 1);
1516 gc = XCreateGC (dpy, mask, GCForeground, &gcv);
1517 XFillRectangle (dpy, mask, gc, 0, 0, si->width, si->height);
1518 XSetForeground (dpy, gc, 1);
1519 XFillArc(dpy, mask, gc, si->minx, si->miny,
1520 si->maxx - si->minx, si->maxy - si->miny,
1528 reshape (sonar_info *si)
1530 XWindowAttributes xgwa;
1532 XGetWindowAttributes(si->dpy, si->win, &xgwa);
1533 si->width = xgwa.width;
1534 si->height = xgwa.height;
1535 si->centrex = si->width / 2;
1536 si->centrey = si->height / 2;
1537 si->maxx = si->centrex + MY_MIN(si->centrex, si->centrey) - 10;
1538 si->minx = si->centrex - MY_MIN(si->centrex, si->centrey) + 10;
1539 si->maxy = si->centrey + MY_MIN(si->centrex, si->centrey) - 10;
1540 si->miny = si->centrey - MY_MIN(si->centrex, si->centrey) + 10;
1541 si->radius = si->maxx - si->centrex;
1543 /* Install the clip mask... */
1545 mask = scope_mask (si->dpy, si->win, si);
1546 XSetClipMask(si->dpy, si->text, mask);
1547 XSetClipMask(si->dpy, si->erase, mask);
1548 XFreePixmap (si->dpy, mask); /* it's been copied into the GCs */
1554 * Update the location of a simulated bogie.
1558 updateLocation(sim_target *t)
1563 xtick = (int) (random() % 3) - 1;
1564 xdist = (int) (random() % 11) - 5;
1565 if (((t->nexttick + xtick) < 90) && ((t->nexttick + xtick) >= 0))
1566 t->nexttick += xtick;
1568 t->nexttick -= xtick;
1569 if (((t->nextdist + xdist) < 100) && ((t->nextdist+xdist) >= 0))
1570 t->nextdist += xdist;
1572 t->nextdist -= xdist;
1576 * The simulator. This uses information in the sim_info to simulate a bunch
1577 * of bogies flying around on the screen.
1581 * TODO: It would be cool to have the two teams chase each other around and
1586 simulator(sonar_info *si, void *vinfo)
1589 /* Local Variables */
1595 sim_info *info = (sim_info *) vinfo;
1599 for (i = 0; i < info->numA; i++) {
1600 t = &info->teamA[i];
1601 if ((t->movedonsweep != si->sweepnum) &&
1602 (t->nexttick == (si->current * -1))) {
1603 new = newBogie(strdup(t->name), t->nextdist, si->current, si->TTL);
1608 t->movedonsweep = si->sweepnum;
1614 for (i = 0; i < info->numB; i++) {
1615 t = &info->teamB[i];
1616 if ((t->movedonsweep != si->sweepnum) &&
1617 (t->nexttick == (si->current * -1))) {
1618 new = newBogie(strdup(t->name), t->nextdist, si->current, si->TTL);
1623 t->movedonsweep = si->sweepnum;
1633 * Compute the X coordinate of the label.
1636 * si - The sonar info block.
1637 * label - The label that will be drawn.
1638 * x - The x coordinate of the bogie.
1641 * The x coordinate of the start of the label.
1645 computeStringX(sonar_info *si, const char *label, int x)
1648 int width = XTextWidth(si->font, label, strlen(label));
1649 return x - (width / 2);
1653 * Compute the Y coordinate of the label.
1656 * si - The sonar information.
1657 * y - The y coordinate of the bogie.
1660 * The y coordinate of the start of the label.
1664 computeStringY(sonar_info *si, int y)
1667 int fheight = si->font->ascent /* + si->font->descent */;
1672 * Draw a Bogie on the radar screen.
1675 * si - Sonar Information.
1676 * draw - A flag to indicate if the bogie should be drawn or erased.
1677 * name - The name of the bogie.
1678 * degrees - The number of degrees that it should apprear at.
1679 * distance - The distance the object is from the centre.
1680 * ttl - The time this bogie has to live.
1681 * age - The time this bogie has been around.
1685 DrawBogie(sonar_info *si, int draw, const char *name, const char *desc,
1686 int degrees, int distance, int ttl, int age)
1689 /* Local Variables */
1693 int ox = si->centrex;
1694 int oy = si->centrey;
1697 /* Compute the coordinates of the object */
1700 distance = (log((double) distance) / 10.0) * si->radius;
1701 x = ox + ((double) distance * cos(4.0 * ((double) degrees)/57.29578));
1702 y = oy - ((double) distance * sin(4.0 * ((double) degrees)/57.29578));
1704 /* Set up the graphics context */
1708 /* Here we attempt to compute the distance into the total life of
1709 * object that we currently are. This distance is used against
1710 * the total lifetime to compute a fraction which is the index of
1711 * the color to draw the bogie.
1714 if (si->current <= degrees)
1715 delta = (si->current - degrees) * -1;
1717 delta = 90 + (degrees - si->current);
1718 delta += (age * 90);
1719 index = (si->text_steps - 1) * ((float) delta / (90.0 * (float) ttl));
1721 XSetForeground(si->dpy, gc, si->text_colors[index].pixel);
1726 /* Draw (or erase) the Bogie */
1728 XFillArc(si->dpy, si->win, gc, x, y, 5, 5, 0, 360 * 64);
1730 x += 3; /* move away from the dot */
1732 y = computeStringY(si, y);
1733 XDrawString(si->dpy, si->win, gc,
1734 computeStringX(si, name, x), y,
1735 name, strlen(name));
1739 y = computeStringY(si, y);
1740 XDrawString(si->dpy, si->win, gc,
1741 computeStringX(si, desc, x), y,
1742 desc, strlen(desc));
1748 * Draw the sonar grid.
1751 * si - Sonar information block.
1755 drawGrid(sonar_info *si)
1758 /* Local Variables */
1761 int width = si->maxx - si->minx;
1762 int height = si->maxy - si->miny;
1764 /* Draw the circles */
1766 XDrawArc(si->dpy, si->win, si->grid, si->minx - 10, si->miny - 10,
1767 width + 20, height + 20, 0, (360 * 64));
1769 XDrawArc(si->dpy, si->win, si->grid, si->minx, si->miny,
1770 width, height, 0, (360 * 64));
1772 XDrawArc(si->dpy, si->win, si->grid,
1773 (int) (si->minx + (.166 * width)),
1774 (int) (si->miny + (.166 * height)),
1775 (unsigned int) (.666 * width), (unsigned int)(.666 * height),
1778 XDrawArc(si->dpy, si->win, si->grid,
1779 (int) (si->minx + (.333 * width)),
1780 (int) (si->miny + (.333 * height)),
1781 (unsigned int) (.333 * width), (unsigned int) (.333 * height),
1784 /* Draw the radial lines */
1786 for (i = 0; i < 360; i += 10)
1788 XDrawLine(si->dpy, si->win, si->grid, si->centrex, si->centrey,
1789 (int) (si->centrex +
1790 (si->radius + 10) * (cos((double) i / 57.29578))),
1791 (int) (si->centrey -
1792 (si->radius + 10)*(sin((double) i / 57.29578))));
1794 XDrawLine(si->dpy, si->win, si->grid,
1795 (int) (si->centrex + si->radius *
1796 (cos((double) i / 57.29578))),
1797 (int) (si->centrey - si->radius *
1798 (sin((double) i / 57.29578))),
1799 (int) (si->centrex +
1800 (si->radius + 10) * (cos((double) i / 57.29578))),
1801 (int) (si->centrey -
1802 (si->radius + 10) * (sin((double) i / 57.29578))));
1806 * Update the Sonar scope.
1809 * si - The Sonar information.
1810 * bl - A list of bogies to add to the scope.
1814 Sonar(sonar_info *si, Bogie *bl)
1817 /* Local Variables */
1822 /* Check for expired tagets and remove them from the visible list */
1825 for (bp = si->visible; bp != NULL; bp = (bp ? bp->next : 0)) {
1828 * Remove it from the visible list if it's expired or we have
1829 * a new target with the same name.
1834 if (((bp->tick == si->current) && (++bp->age >= bp->ttl)) ||
1835 (findNode(bl, bp->name) != NULL)) {
1837 #ifndef HAVE_COCOA /* we repaint every frame: no need to erase */
1838 DrawBogie(si, 0, bp->name, bp->desc, bp->tick,
1839 bp->distance, bp->ttl, bp->age);
1840 #endif /* HAVE_COCOA */
1843 si->visible = bp->next;
1845 prev->next = bp->next;
1852 /* Draw the sweep */
1855 int start_deg = si->current * 4 * 64;
1856 int end_deg = start_deg + (si->sweep_degrees * 64);
1857 int seg_deg = (end_deg - start_deg) / si->sweep_segs;
1858 if (seg_deg <= 0) seg_deg = 1;
1860 /* Remove the trailing wedge the sonar */
1861 XFillArc(si->dpy, si->win, si->erase, si->minx, si->miny,
1862 si->maxx - si->minx, si->maxy - si->miny,
1866 for (i = 0; i < si->sweep_segs; i++) {
1867 int ii = si->sweep_segs - i - 1;
1868 XSetForeground (si->dpy, si->hi, si->sweep_colors[ii].pixel);
1869 XFillArc (si->dpy, si->win, si->hi, si->minx, si->miny,
1870 si->maxx - si->minx, si->maxy - si->miny,
1872 seg_deg * (ii + 1));
1876 /* Move the new targets to the visible list */
1878 for (bp = bl; bp != (Bogie *) 0; bp = bl) {
1880 bp->next = si->visible;
1884 /* Draw the visible targets */
1886 for (bp = si->visible; bp != NULL; bp = bp->next) {
1887 if (bp->age < bp->ttl) /* grins */
1888 DrawBogie(si, 1, bp->name, bp->desc,
1889 bp->tick, bp->distance, bp->ttl,bp->age);
1892 /* Redraw the grid */
1898 static ping_target *
1899 parse_mode (sonar_info *si, Bool ping_works_p)
1901 char *source = get_string_resource (si->dpy, "ping", "Ping");
1907 ping_target *hostlist = 0;
1909 if (!source) source = strdup("");
1911 if (!*source || !strcmp (source, "default"))
1915 if (ping_works_p) /* if root or setuid, ping will work. */
1916 source = strdup("subnet/29,/etc/hosts");
1919 source = strdup("simulation");
1923 end = source + strlen(source);
1930 unsigned int n0=0, n1=0, n2=0, n3=0, m=0;
1932 # endif /* HAVE_PING */
1936 *next != ',' && *next != ' ' && *next != '\t' && *next != '\n';
1943 fprintf (stderr, "%s: parsing %s\n", progname, token);
1945 if (!strcmp (token, "simulation"))
1951 "%s: this program must be setuid to root for `ping mode' to work.\n"
1952 " Running in `simulation mode' instead.\n",
1958 if ((4 == sscanf (token, "%u.%u.%u/%u %c", &n0,&n1,&n2, &m,&d)) ||
1959 (5 == sscanf (token, "%u.%u.%u.%u/%u %c", &n0,&n1,&n2,&n3,&m,&d)))
1961 /* subnet: A.B.C.D/M
1964 unsigned long ip = pack_addr (n0, n1, n2, n3);
1965 new = subnetHostsList(ip, m);
1967 else if (4 == sscanf (token, "%u.%u.%u.%u %c", &n0, &n1, &n2, &n3, &d))
1971 new = newHost (token);
1973 else if (!strcmp (token, "subnet"))
1975 new = subnetHostsList(0, 24);
1977 else if (1 == sscanf (token, "subnet/%u %c", &m, &dummy))
1979 new = subnetHostsList(0, m);
1981 else if (*token == '.' || *token == '/' ||
1982 *token == '$' || *token == '~' ||
1987 new = readPingHostsFile (token);
1991 /* not an existant file - must be a host name
1993 new = newHost (token);
1998 ping_target *nn = new;
1999 while (nn && nn->next)
2001 nn->next = hostlist;
2006 #endif /* HAVE_PING */
2009 while (token < end &&
2010 (*token == ',' || *token == ' ' ||
2011 *token == '\t' || *token == '\n'))
2021 * Initialize the Sonar.
2024 * dpy - The X display.
2025 * win - The X window;
2028 * A sonar_info strcuture or null if memory allocation problems occur.
2032 sonar_init (Display *dpy, Window win)
2034 /* Local Variables */
2037 XWindowAttributes xwa;
2041 double s1, s2, v1, v2;
2043 debug_p = get_boolean_resource (dpy, "debug", "Debug");
2044 resolve_p = get_boolean_resource (dpy, "resolve", "Resolve");
2045 times_p = get_boolean_resource (dpy, "showTimes", "ShowTimes");
2047 /* Create the Sonar information structure */
2049 if ((si = (sonar_info *) calloc(1, sizeof(sonar_info))) == NULL) {
2050 fprintf(stderr, "%s: Out of memory\n", progname);
2054 /* Initialize the structure for the current environment */
2060 XGetWindowAttributes(dpy, win, &xwa);
2061 si->cmap = xwa.colormap;
2069 char *fn = get_string_resource (dpy, "font", "Font");
2070 if (((si->font = XLoadQueryFont(dpy, fn)) == NULL) &&
2071 ((si->font = XLoadQueryFont(dpy, "fixed")) == NULL)) {
2072 fprintf(stderr, "%s: can't load an appropriate font\n", progname);
2078 /* Get the delay between animation frames */
2080 si->delay = get_integer_resource (dpy, "delay", "Integer");
2082 if (si->delay < 0) si->delay = 0;
2083 si->TTL = get_integer_resource(dpy, "ttl", "TTL");
2085 /* Create the Graphics Contexts that will be used to draw things */
2088 get_pixel_resource (dpy, si->cmap, "sweepColor", "SweepColor");
2089 si->hi = XCreateGC(dpy, win, GCForeground, &gcv);
2090 gcv.font = si->font->fid;
2091 si->text = XCreateGC(dpy, win, GCForeground|GCFont, &gcv);
2092 gcv.foreground = get_pixel_resource(dpy, si->cmap,
2093 "scopeColor", "ScopeColor");
2094 si->erase = XCreateGC (dpy, win, GCForeground, &gcv);
2095 gcv.foreground = get_pixel_resource(dpy, si->cmap,
2096 "gridColor", "GridColor");
2097 si->grid = XCreateGC (dpy, win, GCForeground, &gcv);
2101 /* Compute pixel values for fading text on the display */
2103 char *s = get_string_resource(dpy, "textColor", "TextColor");
2104 XParseColor(dpy, si->cmap, s, &start);
2106 s = get_string_resource(dpy, "scopeColor", "ScopeColor");
2107 XParseColor(dpy, si->cmap, s, &end);
2111 rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
2112 rgb_to_hsv (end.red, end.green, end.blue, &h2, &s2, &v2);
2114 si->text_steps = get_integer_resource(dpy, "textSteps", "TextSteps");
2115 if (si->text_steps < 0 || si->text_steps > 255)
2116 si->text_steps = 10;
2118 si->text_colors = (XColor *) calloc(si->text_steps, sizeof(XColor));
2119 make_color_ramp (dpy, si->cmap,
2122 si->text_colors, &si->text_steps,
2123 False, True, False);
2125 /* Compute the pixel values for the fading sweep */
2128 char *s = get_string_resource(dpy, "sweepColor", "SweepColor");
2129 XParseColor(dpy, si->cmap, s, &start);
2133 rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
2135 si->sweep_degrees = get_integer_resource(dpy, "sweepDegrees", "Degrees");
2136 if (si->sweep_degrees <= 0) si->sweep_degrees = 20;
2137 if (si->sweep_degrees > 350) si->sweep_degrees = 350;
2139 si->sweep_segs = get_integer_resource(dpy, "sweepSegments", "SweepSegments");
2140 if (si->sweep_segs < 1 || si->sweep_segs > 255)
2141 si->sweep_segs = 255;
2143 si->sweep_colors = (XColor *) calloc(si->sweep_segs, sizeof(XColor));
2144 make_color_ramp (dpy, si->cmap,
2147 si->sweep_colors, &si->sweep_segs,
2148 False, True, False);
2150 if (si->sweep_segs <= 0)
2155 si->sensor_info = (void *) init_ping(si);
2156 # else /* !HAVE_PING */
2157 parse_mode (dpy, 0); /* just to check argument syntax */
2158 # endif /* !HAVE_PING */
2162 si->sensor = simulator;
2163 si->sensor_info = (void *) init_sim(dpy);
2164 if (! si->sensor_info)
2174 static unsigned long
2175 sonar_draw (Display *dpy, Window window, void *closure)
2177 sonar_info *si = (sonar_info *) closure;
2179 struct timeval start, finish;
2182 # ifdef HAVE_COCOA /* repaint the whole window so that antialiasing works */
2183 XClearWindow (dpy,window);
2184 XFillRectangle (dpy, window, si->erase, 0, 0, si->width, si->height);
2187 /* Call the sensor and display the results */
2189 # ifdef GETTIMEOFDAY_TWO_ARGS
2190 gettimeofday(&start, (struct timezone *) 0);
2192 gettimeofday(&start);
2194 bl = si->sensor(si, si->sensor_info);
2197 /* Set up and sleep for the next one */
2199 si->current = (si->current - 1) % 90;
2200 if (si->current == 0)
2203 # ifdef GETTIMEOFDAY_TWO_ARGS
2204 gettimeofday(&finish, (struct timezone *) 0);
2206 gettimeofday(&finish);
2209 delay = si->delay - delta(&start, &finish);
2210 if (delay < 0) delay = 0;
2216 sonar_reshape (Display *dpy, Window window, void *closure,
2217 unsigned int w, unsigned int h)
2219 sonar_info *si = (sonar_info *) closure;
2220 XClearWindow (si->dpy, si->win);
2225 sonar_event (Display *dpy, Window window, void *closure, XEvent *event)
2231 sonar_free (Display *dpy, Window window, void *closure)
2237 static const char *sonar_defaults [] = {
2238 ".background: #000000",
2239 ".sweepColor: #00FF00",
2241 "*scopeColor: #003300",
2242 "*gridColor: #00AA00",
2243 "*textColor: #FFFF00",
2247 "*sweepDegrees: 30",
2249 "*textSteps: 80", /* npixels */
2250 "*sweepSegments: 80", /* npixels */
2252 "*pingTimeout: 3000",
2266 static XrmOptionDescRec sonar_options [] = {
2267 {"-background", ".background", XrmoptionSepArg, 0 },
2268 {"-sweep-color", ".sweepColor", XrmoptionSepArg, 0 },
2269 {"-scope-color", ".scopeColor", XrmoptionSepArg, 0 },
2270 {"-grid-color", ".gridColor", XrmoptionSepArg, 0 },
2271 {"-text-color", ".textColor", XrmoptionSepArg, 0 },
2272 {"-ttl", ".ttl", XrmoptionSepArg, 0 },
2273 {"-font", ".font", XrmoptionSepArg, 0 },
2275 {"-ping-timeout", ".pingTimeout", XrmoptionSepArg, 0 },
2276 #endif /* HAVE_PING */
2277 {"-team-a-name", ".teamAName", XrmoptionSepArg, 0 },
2278 {"-team-b-name", ".teamBName", XrmoptionSepArg, 0 },
2279 {"-team-a-count", ".teamACount", XrmoptionSepArg, 0 },
2280 {"-team-b-count", ".teamBCount", XrmoptionSepArg, 0 },
2282 {"-ping", ".ping", XrmoptionSepArg, 0 },
2283 {"-no-dns", ".resolve", XrmoptionNoArg, "False" },
2284 {"-no-times", ".showTimes", XrmoptionNoArg, "False" },
2285 {"-debug", ".debug", XrmoptionNoArg, "True" },
2290 XSCREENSAVER_MODULE ("Sonar", sonar)