1 /* sonar.c --- Simulate a sonar screen.
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.
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.
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.
21 * It should be easy to extend this code to support other sorts of sensors.
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.
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
39 * Version 1.0 April 27, 1998.
41 * - Submitted to RedHat Screensaver Contest
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
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
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
67 * Version 1.4 November 18, 1998.
68 * - More ping portability fixes.
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.
80 #include <sys/types.h>
81 #include <sys/socket.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>
95 #endif /* HAVE_PING */
99 #include "screenhack.h"
102 #include <X11/extensions/XShm.h>
107 #define MIN(a,b) ((a)<(b)?(a - 50):(b - 10))
110 /* Forward References */
113 static u_short checksum(u_short *, int);
115 static long delta(struct timeval *, struct timeval *);
117 /* Data Structures */
122 * This represents an object that is visable on the scope.
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 */
137 * This contains all of the runtime information about the sonar scope.
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 */
161 int delay; /* how long between each frame of the anim */
166 * Variables to support the differnt Sonar modes.
169 Bogie *(*sensor)(sonar_info *, void *); /* The current sensor */
170 void *sensor_info; /* Information about the sensor */
173 * A list of targets to 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 */
186 * This contains the information for the ping sensor.
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 */
198 /* Flag to indicate that the timer has expired on us */
200 static int timer_expired;
203 #endif /* HAVE_PING */
206 * A list of targets for the simulator
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 */
217 * Simulator Information.
219 * This contains the information for the simulator mode.
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 */
231 /* Name of the Screensaver hack */
233 char *progclass="sonar";
235 /* Application Defaults */
237 char *defaults [] = {
238 ".background: #000000",
239 ".sweepColor: #00FF00",
241 "*scopeColor: #003300",
242 "*gridColor: #00AA00",
243 "*textColor: #FFFF00",
249 "*textSteps: 80", /* npixels */
250 "*sweepSegments: 80", /* npixels */
253 "*pingTimeout: 3000",
255 "*pingFile: /etc/hosts",
256 "*pingList: localhost",
257 #endif /* HAVE_PING */
265 /* Options passed to this program */
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 },
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 },
290 * The number of ticks that bogies are visable on the screen before they
297 * Create a new Bogie and set some initial values.
300 * name - The name of the bogie.
301 * distance - The distance value.
302 * tick - The tick value.
303 * ttl - The time to live value.
306 * The newly allocated bogie or null if a memory problem occured.
310 newBogie(char *name, int distance, int tick, int ttl)
313 /* Local Variables */
317 /* Allocate a bogie and initialize it */
319 if ((new = (Bogie *) calloc(1, sizeof(Bogie))) == NULL) {
320 fprintf(stderr, "Out of Memory\n");
324 new->distance = distance;
328 new->next = (Bogie *) 0;
336 * b - The bogie to free.
342 if (b->name != (char *) 0)
348 * Find a bogie by name in a list.
350 * This does a simple linear search of the list for a given name.
353 * bl - The Bogie list to search.
354 * name - The name to look for.
357 * The requested Bogie or null if it wasn't found.
361 findNode(Bogie *bl, char *name)
364 /* Local Variables */
368 /* Abort if the list is empty or no name is given */
370 if ((name == NULL) || (bl == NULL))
373 /* Search the list for the desired name */
377 if (strcmp(p->name, name) == 0)
390 * Lookup the address for a ping target;
393 * target - The ping_target fill in the address for.
396 * 1 if the host was successfully resolved, 0 otherwise.
400 lookupHost(ping_target *target)
403 /* Local Variables */
405 struct sockaddr_in *iaddr;
407 /* Set up the target address we first assume that the name is the
408 IP address as a string */
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) {
414 /* Conversion of IP address failed, try to look the host up by name */
416 struct hostent *hent = gethostbyname(target->name);
418 fprintf(stderr, "Could not resolve host %s\n", target->name);
421 memcpy(&iaddr->sin_addr, hent->h_addr_list[0],
422 sizeof(iaddr->sin_addr));
431 * Create a target for a host.
434 * name - The name of the host.
437 * A newly allocated target or null if the host could not be resolved.
444 /* Local Variables */
446 ping_target *target = NULL;
448 /* Create the target */
450 if ((target = calloc(1, sizeof(ping_target))) == NULL) {
451 fprintf(stderr, "Out of Memory\n");
452 goto target_init_error;
454 if ((target->name = strdup(name)) == NULL) {
455 fprintf(stderr, "Out of Memory\n");
456 goto target_init_error;
459 /* Lookup the host */
461 if (! lookupHost(target))
462 goto target_init_error;
468 /* Handle errors here */
477 * Generate a list of ping targets from the entries in a file.
480 * fname - The name of the file. This file is expected to be in the same
481 * format as /etc/hosts.
484 * A list of targets to ping or null if an error occured.
488 readPingHostsFile(char *fname)
491 /* Local Variables */
496 ping_target *list = NULL;
500 /* Make sure we in fact have a file to process */
502 if ((fname == NULL) || (fname[0] == '\0')) {
503 fprintf(stderr, "Invalid ping host file name\n");
509 if ((fp = fopen(fname, "r")) == NULL) {
511 sprintf(msg, "Unable to open host file %s", fname);
516 /* Read the file line by line */
518 while ((p = fgets(buf, LINE_MAX, fp)) != NULL) {
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
527 while ((*p == ' ') || (*p == '\t'))
532 /* Get the name and address */
535 if ((addr = strtok(buf, " \t\n")) != NULL)
536 name = strtok(NULL, " \t\n");
540 /* Create a new target using first the name then the address */
548 /* Add it to the list if we got one */
556 /* Close the file and return the list */
563 * Generate a list of ping targets from the entries in a string.
566 * list - A list of comma separated host names.
569 * A list of targets to ping or null if an error occured.
573 readPingHostsList(char *list)
576 /* Local Variables */
579 ping_target *hostlist = NULL;
582 /* Check that there is a list */
584 if ((list == NULL) || (list[0] == '\0'))
587 /* Loop through the hosts and add them to the list to return */
589 host = strtok(list, ",");
590 while (host != NULL) {
593 new->next = hostlist;
596 host = strtok(NULL, ",");
605 * Generate a list ping targets consisting of all of the entries on
609 * A list of all of the hosts on this net.
613 subnetHostsList(void)
616 /* Local Variables */
618 char hostname[BUFSIZ];
619 char address[BUFSIZ];
620 struct hostent *hent;
624 ping_target *list = NULL;
626 /* Get our hostname */
628 if (gethostname(hostname, BUFSIZ)) {
629 fprintf(stderr, "Unable to get local hostname\n");
633 /* Get our IP address and convert it to a string */
635 if ((hent = gethostbyname(hostname)) == NULL) {
636 fprintf(stderr, "Unable to lookup our IP address\n");
639 strcpy(address, inet_ntoa(*((struct in_addr *)hent->h_addr_list[0])));
641 /* Get a pointer to the last "." in the string */
643 if ((p = strrchr(address, '.')) == NULL) {
644 fprintf(stderr, "Can't parse IP address %s\n", address);
649 /* Construct targets for all addresses in this subnet */
651 for (i = 254; i > 0; i--) {
653 new = newHost(address);
666 * Initialize the ping sensor.
669 * A newly allocated ping_info structure or null if an error occured.
676 /* Local Variables */
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 */
682 /* Create the ping info structure */
684 if ((pi = (ping_info *) calloc(1, sizeof(ping_info))) == NULL) {
685 fprintf(stderr, "Out of memory\n");
686 goto ping_init_error;
689 /* Create the ICMP socket and turn off SUID */
691 if ((pi->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
692 perror("Can't create ICMP socket");
694 "%s: this program must be setuid to root for `ping mode' to work.\n",
696 goto ping_init_error;
699 pi->pid = getpid() & 0xFFFF;
701 pi->timeout = get_integer_resource("pingTimeout", "PingTimeout");
703 /* Generate a list of targets */
705 src = get_string_resource("pingSource", "PingSource");
706 if (strcmp(src, "file") == 0) {
709 * The list of ping targets is to come from a file in
713 pi->targets = readPingHostsFile(get_string_resource("pingFile",
716 } else if (strcmp(src, "list") == 0) {
718 /* The list of hosts is to come from the pinghostlist resource */
720 pi->targets = readPingHostsList(get_string_resource("pingList",
723 } else if (strcmp(src, "subnet") == 0) {
725 pi->targets = subnetHostsList();
731 fprintf(stderr, "Illegal pingSource: %s\n", src);
732 goto ping_init_error;
735 /* Make sure there is something to ping */
737 if (pi->targets == NULL) {
738 fprintf(stderr, "Nothing to ping");
739 goto ping_init_error;
742 /* Count the targets */
755 /* Handle initialization errors here */
767 * pi - The ping information strcuture.
768 * host - The name or IP address of the host to ping (in ascii).
772 sendping(ping_info *pi, ping_target *pt)
775 /* Local Variables */
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.
787 int pcktsiz = sizeof(struct icmp) + sizeof(struct timeval) +
788 strlen(pt->name) + 1;
790 /* Create the ICMP packet */
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)],
804 icmph->icmp_cksum = checksum((u_short *)packet, pcktsiz);
808 if ((result = sendto(pi->icmpsock, packet, pcktsiz, 0,
809 &pt->address, sizeof(pt->address))) != pcktsiz) {
812 sprintf(errbuf, "Error sending ping to %s", pt->name);
819 * Catch a signal and do nothing.
822 * sig - The signal that was caught.
832 * Compute the checksum on a ping packet.
835 * packet - A pointer to the packet to compute the checksum for.
836 * size - The size of the packet.
839 * The computed checksum
844 checksum(u_short *packet, int size)
847 /* Local Variables */
849 register int nleft = size;
850 register u_short *w = packet;
851 register int sum = 0;
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.
865 /* mop up an odd byte, if necessary */
868 *(u_char *)(&answer) = *(u_char *)w ;
872 /* add back carry outs from top 16 bits to low 16 bits */
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 */
884 * Look for ping replies.
886 * Retrieve all outstanding ping replies.
889 * si - Information about the sonar.
890 * pi - Ping information.
891 * ttl - The time each bogie is to live on the screen
894 * A Bogie list of all the machines that replied.
898 getping(sonar_info *si, ping_info *pi, int ttl)
901 /* Local Variables */
903 struct sockaddr from;
908 struct timeval *then;
918 /* Set up a signal to interupt our wait for a packet */
920 sigemptyset(&sa.sa_mask);
922 sa.sa_handler = sigcatcher;
923 if (sigaction(SIGALRM, &sa, 0) == -1) {
924 perror("Unable to trap sigalarm");
928 /* Set up a timer to interupt us if we don't get a packet */
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;
935 setitimer(ITIMER_REAL, &it, NULL);
937 /* Wait for a result packet */
939 fromlen = sizeof(from);
940 while (! timer_expired &&
941 (result = recvfrom(pi->icmpsock, packet, sizeof(packet),
942 0, &from, &fromlen)) > 0) {
944 /* Check the packet */
946 gettimeofday(&now, (struct timezone *) 0);
947 ip = (struct ip *) packet;
948 iphdrlen = ip->ip_hl << 2;
949 icmph = (struct icmp *) &packet[iphdrlen];
951 /* Was the packet a reply?? */
953 if (icmph->icmp_type != ICMP_ECHOREPLY) {
954 /* Ignore anything but ICMP Replies */
960 if (icmph->icmp_id != pi->pid) {
961 /* Ignore packets not set from us */
965 /* Copy the name of the bogie */
968 strdup((char *) &packet[iphdrlen +
969 + sizeof(struct icmp)
970 + sizeof(struct timeval)])) == NULL) {
971 fprintf(stderr, "Out of memory\n");
975 /* If the name is an IP addr, try to resolve it. */
979 if (4 == sscanf(name, " %d.%d.%d.%d %c",
980 &iip[0], &iip[1], &iip[2], &iip[3], &c))
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)
989 name = strdup (h->h_name);
994 /* Create the new Bogie and add it to the list we are building */
996 if ((new = newBogie(name, 0, si->current, ttl)) == NULL)
1001 /* Compute the round trip time */
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 */
1019 * si - Sonar Information.
1020 * pi - Ping Information.
1023 * A list of hosts that replied to pings or null if there were none.
1027 ping(sonar_info *si, void *vpi)
1030 ping_info *pi = (ping_info *) vpi;
1031 static ping_target *ptr = NULL;
1033 int tick = si->current * -1 + 1;
1034 if ((ptr == NULL) && (tick == 1))
1037 if (pi->numtargets <= 90) {
1038 int xdrant = 90 / pi->numtargets;
1039 if ((tick % xdrant) == 0) {
1040 if (ptr != (ping_target *) 0) {
1046 } else if (pi->numtargets > 90) {
1047 if (ptr != (ping_target *) 0) {
1053 /* Get the results */
1055 return getping(si, pi, TTL);
1058 #endif /* HAVE_PING */
1061 * Calculate the difference between two timevals in microseconds.
1064 * then - The older timeval.
1065 * now - The newer timeval.
1068 * The difference between the two in microseconds.
1072 delta(struct timeval *then, struct timeval *now)
1074 return (((now->tv_sec - then->tv_sec) * 1000000) +
1075 (now->tv_usec - then->tv_usec));
1079 * Initialize the simulation mode.
1086 /* Local Variables */
1091 /* Seed the random number generator */
1093 srand((int) time(NULL));
1095 /* Create the simulation info structure */
1097 if ((si = (sim_info *) calloc(1, sizeof(sim_info))) == NULL) {
1098 fprintf(stderr, "Out of memory\n");
1104 si->numA = get_integer_resource("teamACount", "TeamACount");
1105 if ((si->teamA = (sim_target *)calloc(si->numA, sizeof(sim_target)))
1108 fprintf(stderr, "Out of Memory\n");
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))
1116 fprintf(stderr, "Out of Memory\n");
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);
1126 si->numB = get_integer_resource("teamBCount", "TeamBCount");
1127 if ((si->teamB = (sim_target *)calloc(si->numB, sizeof(sim_target)))
1130 fprintf(stderr, "Out of Memory\n");
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))
1138 fprintf(stderr, "Out of Memory\n");
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);
1152 * Initialize the Sonar.
1155 * dpy - The X display.
1156 * win - The X window;
1159 * A sonar_info strcuture or null if memory allocation problems occur.
1163 init_sonar(Display *dpy, Window win)
1166 /* Local Variables */
1169 XWindowAttributes xwa;
1173 double s1, s2, v1, v2;
1175 /* Create the Sonar information structure */
1177 if ((si = (sonar_info *) calloc(1, sizeof(sonar_info))) == NULL) {
1178 fprintf(stderr, "Out of memory\n");
1182 /* Initialize the structure for the current environment */
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;
1202 if (((si->font = XLoadQueryFont(dpy, get_string_resource ("font", "Font")))
1204 ((si->font = XLoadQueryFont(dpy, "fixed")) == NULL)) {
1205 fprintf(stderr, "Can't load an appropriate font\n");
1209 /* Get the delay between animation frames */
1211 si->delay = get_integer_resource ("delay", "Integer");
1212 if (si->delay < 0) si->delay = 0;
1214 /* Create the Graphics Contexts that will be used to draw things */
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",
1223 si->erase = XCreateGC (dpy, win, GCForeground, &gcv);
1224 gcv.foreground = get_pixel_resource("gridColor", "GridColor",
1226 si->grid = XCreateGC (dpy, win, GCForeground, &gcv);
1228 /* Compute pixel values for fading text on the display */
1230 XParseColor(dpy, si->cmap,
1231 get_string_resource("textColor", "TextColor"), &start);
1232 XParseColor(dpy, si->cmap,
1233 get_string_resource("scopeColor", "ScopeColor"), &end);
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);
1238 si->text_steps = get_integer_resource("textSteps", "TextSteps");
1239 if (si->text_steps < 0 || si->text_steps > 255)
1240 si->text_steps = 10;
1242 si->text_colors = (XColor *) calloc(si->text_steps, sizeof(XColor));
1243 make_color_ramp (dpy, si->cmap,
1246 si->text_colors, &si->text_steps,
1247 False, True, False);
1249 /* Compute the pixel values for the fading sweep */
1251 XParseColor(dpy, si->cmap,
1252 get_string_resource("sweepColor", "SweepColor"), &start);
1254 rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
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;
1260 si->sweep_segs = get_integer_resource("sweepSegments", "SweepSegments");
1261 if (si->sweep_segs < 1 || si->sweep_segs > 255)
1262 si->sweep_segs = 255;
1264 si->sweep_colors = (XColor *) calloc(si->sweep_segs, sizeof(XColor));
1265 make_color_ramp (dpy, si->cmap,
1268 si->sweep_colors, &si->sweep_segs,
1269 False, True, False);
1277 * Update the location of a simulated bogie.
1281 updateLocation(sim_target *t)
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;
1292 t->nexttick -= xtick;
1293 if (((t->nextdist + xdist) < 100) && ((t->nextdist+xdist) >= 0))
1294 t->nextdist += xdist;
1296 t->nextdist -= xdist;
1300 * The simulator. This uses information in the sim_info to simulate a bunch
1301 * of bogies flying around on the screen.
1305 * TODO: It would be cool to have the two teams chase each other around and
1310 simulator(sonar_info *si, void *vinfo)
1313 /* Local Variables */
1319 sim_info *info = (sim_info *) vinfo;
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);
1332 t->movedlasttick = 0;
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);
1344 t->movedlasttick = 1;
1347 t->movedlasttick = 0;
1356 * Compute the X coordinate of the label.
1359 * si - The sonar info block.
1360 * label - The label that will be drawn.
1361 * x - The x coordinate of the bogie.
1364 * The x coordinate of the start of the label.
1368 computeStringX(sonar_info *si, char *label, int x)
1371 int width = XTextWidth(si->font, label, strlen(label));
1372 return x - (width / 2);
1376 * Compute the Y coordinate of the label.
1379 * si - The sonar information.
1380 * y - The y coordinate of the bogie.
1383 * The y coordinate of the start of the label.
1386 /* TODO: Add smarts to keep label in sonar screen */
1389 computeStringY(sonar_info *si, int y)
1392 int fheight = si->font->ascent + si->font->descent;
1393 return y + 5 + fheight;
1397 * Draw a Bogie on the radar screen.
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.
1410 DrawBogie(sonar_info *si, int draw, char *name, int degrees,
1411 int distance, int ttl, int age)
1414 /* Local Variables */
1418 int ox = si->centrex;
1419 int oy = si->centrey;
1422 /* Compute the coordinates of the object */
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));
1428 /* Set up the graphics context */
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.
1438 if (si->current <= degrees)
1439 delta = (si->current - degrees) * -1;
1441 delta = 90 + (degrees - si->current);
1442 delta += (age * 90);
1443 index = (si->text_steps - 1) * ((float) delta / (90.0 * (float) ttl));
1445 XSetForeground(si->dpy, gc, si->text_colors[index].pixel);
1450 /* Draw (or erase) the Bogie */
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));
1460 * Draw the sonar grid.
1463 * si - Sonar information block.
1467 drawGrid(sonar_info *si)
1470 /* Local Variables */
1473 int width = si->maxx - si->minx;
1474 int height = si->maxy - si->miny;
1476 /* Draw the circles */
1478 XDrawArc(si->dpy, si->win, si->grid, si->minx - 10, si->miny - 10,
1479 width + 20, height + 20, 0, (360 * 64));
1481 XDrawArc(si->dpy, si->win, si->grid, si->minx, si->miny,
1482 width, height, 0, (360 * 64));
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),
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),
1496 /* Draw the radial lines */
1498 for (i = 0; i < 360; i += 10)
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))));
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))));
1518 * Update the Sonar scope.
1521 * si - The Sonar information.
1522 * bl - A list of bogies to add to the scope.
1526 Sonar(sonar_info *si, Bogie *bl)
1529 /* Local Variables */
1534 /* Check for expired tagets and remove them from the visable list */
1537 for (bp = si->visable; bp != NULL; bp = bp->next) {
1540 * Remove it from the visable list if it's expired or we have
1541 * a new target with the same name.
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);
1551 si->visable = bp->next;
1553 prev->next = bp->next;
1559 /* Draw the sweep */
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),
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),
1580 /* Move the new targets to the visable list */
1582 for (bp = bl; bp != (Bogie *) 0; bp = bl) {
1584 bp->next = si->visable;
1588 /* Draw the visable targets */
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);
1595 /* Redraw the grid */
1601 * Main screen saver hack.
1604 * dpy - The X display.
1605 * win - The X window.
1609 screenhack(Display *dpy, Window win)
1612 /* Local Variables */
1615 struct timeval start, finish;
1622 * Adding new sensors would involve supporting more modes other than
1623 * ping and initiailizing the sensor in the same way.
1626 mode = get_string_resource("mode", "Mode");
1628 if (!mode || !*mode || !strcmp(mode, "default")) /* Pick a good default. */
1631 if (geteuid() == 0) /* we're root or setuid -- ping will work. */
1635 mode = "simulation";
1639 if (strcmp(mode, "ping") == 0) {
1641 if ((sensor_info = (void *) init_ping()) == (void *) 0)
1643 fprintf (stderr, "%s: running in `simulation mode' instead.\n",
1648 #endif /* HAVE_PING */
1649 if (strcmp(mode, "simulation") == 0) {
1654 if ((sensor_info = (void *) init_sim()) == NULL)
1657 fprintf(stderr, "Unsupported Sonar mode: %s\n", mode);
1659 "\tCurrently supported modes are `ping' and `simulation'\n");
1662 if ((si = init_sonar(dpy, win)) == (sonar_info *) 0)
1667 TTL = get_integer_resource("ttl", "TTL");
1671 /* Call the sensor and display the results */
1673 gettimeofday(&start, (struct timezone *) 0);
1674 bl = sensor(si, sensor_info);
1677 /* Set up and sleep for the next one */
1679 si->current = (si->current - 1) % 90;
1681 gettimeofday(&finish, (struct timezone *) 0);
1682 sleeptime = si->delay - delta(&start, &finish);
1683 screenhack_handle_events (dpy);