/* sonar.c --- Simulate a sonar screen.
+ * Copyright (C) 1998-2006 by Stephen Martin and Jamie Zawinski
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
*
* This is an implementation of a general purpose reporting tool in the
* format of a Sonar display. It is designed such that a sensor is read
* because, unfortunately, different systems have different ways of creating
* these sorts of packets.
*
- * Also: creating an ICMP socket is a privileged operation, so the program
- * needs to be installed SUID root if you want to use the ping mode. If you
- * check the code you will see that this privilige is given up immediately
- * after the socket is created.
+ * In order to use the ping sensor on most systems, this program must be
+ * installed as setuid root, so that it can create an ICMP RAW socket. Root
+ * privileges are disavowed shortly after startup (just after connecting to
+ * the X server and reading the resource database) so this is *believed* to
+ * be a safe thing to do, but it is usually recommended that you have as few
+ * setuid programs around as possible, on general principles.
+ *
+ * It is not necessary to make it setuid on MacOS systems, because on those
+ * systems, unprivileged programs can ping by using ICMP DGRAM sockets
+ * instead of ICMP RAW.
*
* It should be easy to extend this code to support other sorts of sensors.
* Some ideas:
* - plot the process table, by process size, cpu usage, or total time;
* - plot the logged on users by idle time or cpu usage.
*
- * Copyright (C) 1998, 2001
- * by Stephen Martin (smartin@vanderfleet-martin.net).
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that
- * copyright notice and this permission notice appear in supporting
- * documentation. No representations are made about the suitability of this
- * software for any purpose. It is provided "as is" without express or
- * implied warranty.
- *
- * $Revision: 1.29 $
+ * $Revision: 1.57 $
*
* Version 1.0 April 27, 1998.
- * - Initial version
+ * - Initial version, by Stephen Martin <smartin@vanderfleet-martin.net>
* - Submitted to RedHat Screensaver Contest
*
* Version 1.1 November 3, 1998.
* - Now need to define HAVE_PING to compile in the ping stuff.
*/
+
/* These are computed by configure now:
#define HAVE_ICMP
#define HAVE_ICMPHDR
#include "colors.h"
#include "hsv.h"
+#undef usleep /* conflicts with unistd.h on OSX */
+
#if defined(HAVE_ICMP) || defined(HAVE_ICMPHDR)
# include <unistd.h>
# include <limits.h>
typedef struct Bogie {
char *name; /* The name of the thing being displayed */
+ char *desc; /* Beneath the name (e.g., ping time) */
int distance; /* The distance to this thing (0 - 100) */
int tick; /* The tick that it was found on */
int ttl; /* The time to live */
* This contains all of the runtime information about the sonar scope.
*/
-typedef struct {
+typedef struct ping_target ping_target;
+
+typedef struct sonar_info sonar_info;
+struct sonar_info {
Display *dpy; /* The X display */
Window win; /* The window */
GC hi, /* The leading edge of the sweep */
int TTL; /* The number of ticks that bogies are visible
on the screen before they fade away. */
-} sonar_info;
-static Bool debug_p = False;
+ ping_target *last_ptr;
+ Bogie *(*sensor)(sonar_info *, void *); /* The current sensor */
+ void *sensor_info; /* Information about the sensor */
-/*
- * Variables to support the differnt Sonar modes.
- */
+};
+
+static Bool debug_p = False;
+static Bool resolve_p = True;
+static Bool times_p = True;
-Bogie *(*sensor)(sonar_info *, void *); /* The current sensor */
-void *sensor_info; /* Information about the sensor */
/*
* A list of targets to ping.
*/
-typedef struct ping_target {
+struct ping_target {
char *name; /* The name of the target */
#ifdef HAVE_PING
struct sockaddr address; /* The address of the target */
#endif /* HAVE_PING */
struct ping_target *next; /* The next one in the list */
-} ping_target;
+};
#ifdef HAVE_PING
char *teamBID; /* The identifier for bogies in team B */
} sim_info;
-/* Name of the Screensaver hack */
-
-char *progclass="sonar";
-
-/* Application Defaults */
-
-char *defaults [] = {
- ".background: #000000",
- ".sweepColor: #00FF00",
- "*delay: 100000",
- "*scopeColor: #003300",
- "*gridColor: #00AA00",
- "*textColor: #FFFF00",
- "*ttl: 90",
- "*mode: default",
- "*font: fixed",
- "*sweepDegrees: 30",
-
- "*textSteps: 80", /* npixels */
- "*sweepSegments: 80", /* npixels */
-
- "*pingTimeout: 3000",
-
- "*teamAName: F18",
- "*teamBName: MIG",
- "*teamACount: 4",
- "*teamBCount: 4",
-
- "*ping: default",
- ".debug: false",
- 0
-};
-
-/* Options passed to this program */
-XrmOptionDescRec options [] = {
- {"-background", ".background", XrmoptionSepArg, 0 },
- {"-sweep-color", ".sweepColor", XrmoptionSepArg, 0 },
- {"-scope-color", ".scopeColor", XrmoptionSepArg, 0 },
- {"-grid-color", ".gridColor", XrmoptionSepArg, 0 },
- {"-text-color", ".textColor", XrmoptionSepArg, 0 },
- {"-ttl", ".ttl", XrmoptionSepArg, 0 },
- {"-font", ".font", XrmoptionSepArg, 0 },
-#ifdef HAVE_PING
- {"-ping-timeout", ".pingTimeout", XrmoptionSepArg, 0 },
-#endif /* HAVE_PING */
- {"-team-a-name", ".teamAName", XrmoptionSepArg, 0 },
- {"-team-b-name", ".teamBName", XrmoptionSepArg, 0 },
- {"-team-a-count", ".teamACount", XrmoptionSepArg, 0 },
- {"-team-b-count", ".teamBCount", XrmoptionSepArg, 0 },
-
- {"-ping", ".ping", XrmoptionSepArg, 0 },
- {"-debug", ".debug", XrmoptionNoArg, "True" },
- { 0, 0, 0, 0 }
-};
/*
* Create a new Bogie and set some initial values.
#ifdef HAVE_PING
+/* Packs an IP address quad into bigendian network order. */
+static unsigned long
+pack_addr (unsigned int a, unsigned int b, unsigned int c, unsigned int d)
+{
+ unsigned long i = (((a & 255) << 24) |
+ ((b & 255) << 16) |
+ ((c & 255) << 8) |
+ ((d & 255) ));
+ return htonl (i);
+}
+
+/* Unpacks an IP address quad from bigendian network order. */
+static void
+unpack_addr (unsigned long addr,
+ unsigned int *a, unsigned int *b,
+ unsigned int *c, unsigned int *d)
+{
+ addr = ntohl (addr);
+ *a = (addr >> 24) & 255;
+ *b = (addr >> 16) & 255;
+ *c = (addr >> 8) & 255;
+ *d = (addr ) & 255;
+}
+
+
/*
* Lookup the address for a ping target;
*
struct hostent *hent;
struct sockaddr_in *iaddr;
- int iip[4];
+ unsigned int ip[4];
char c;
iaddr = (struct sockaddr_in *) &(target->address);
iaddr->sin_family = AF_INET;
- if (4 == sscanf(target->name, "%d.%d.%d.%d%c",
- &iip[0], &iip[1], &iip[2], &iip[3], &c))
+ if (4 == sscanf (target->name, " %u.%u.%u.%u %c",
+ &ip[0], &ip[1], &ip[2], &ip[3], &c))
{
/* It's an IP address.
*/
- unsigned char ip[4];
-
- ip[0] = iip[0];
- ip[1] = iip[1];
- ip[2] = iip[2];
- ip[3] = iip[3];
-
if (ip[3] == 0)
{
if (debug_p > 1)
return 0;
}
- iaddr->sin_addr.s_addr = ((ip[3] << 24) |
- (ip[2] << 16) |
- (ip[1] << 8) |
- (ip[0]));
- hent = gethostbyaddr ((const char *) ip, 4, AF_INET);
+ iaddr->sin_addr.s_addr = pack_addr (ip[0], ip[1], ip[2], ip[3]);
+ if (resolve_p)
+ hent = gethostbyaddr ((const char *) &iaddr->sin_addr.s_addr,
+ sizeof(iaddr->sin_addr.s_addr),
+ AF_INET);
+ else
+ hent = 0;
if (debug_p > 1)
fprintf (stderr, "%s: %s => %s\n",
{
/* It's a host name.
*/
+
+
+ /* don't waste time being confused by non-hostname tokens
+ in .ssh/known_hosts */
+ if (!strcmp (target->name, "ssh-rsa") ||
+ !strcmp (target->name, "ssh-dsa") ||
+ !strcmp (target->name, "ssh-dss") ||
+ strlen (target->name) >= 80)
+ return 0;
+
hent = gethostbyname (target->name);
if (!hent)
{
- fprintf (stderr, "%s: could not resolve host: %s\n",
- progname, target->name);
+ if (debug_p)
+ fprintf (stderr, "%s: could not resolve host: %s\n",
+ progname, target->name);
return 0;
}
sizeof(iaddr->sin_addr));
if (debug_p > 1)
- fprintf (stderr, "%s: %s => %d.%d.%d.%d\n",
- progname, target->name,
- iaddr->sin_addr.s_addr & 255,
- iaddr->sin_addr.s_addr >> 8 & 255,
- iaddr->sin_addr.s_addr >> 16 & 255,
- iaddr->sin_addr.s_addr >> 24 & 255);
+ {
+ unsigned int a, b, c, d;
+ unpack_addr (iaddr->sin_addr.s_addr, &a, &b, &c, &d);
+ fprintf (stderr, "%s: %s => %d.%d.%d.%d\n",
+ progname, target->name, a, b, c, d);
+ }
}
return 1;
}
print_host (FILE *out, unsigned long ip, const char *name)
{
char ips[50];
- sprintf (ips, "%lu.%lu.%lu.%lu",
- (ip) & 255,
- (ip >> 8) & 255,
- (ip >> 16) & 255,
- (ip >> 24) & 255);
+ unsigned int a, b, c, d;
+ unpack_addr (ip, &a, &b, &c, &d); /* ip is in network order */
+ sprintf (ips, "%u.%u.%u.%u", a, b, c, d);
if (!name || !*name) name = "<unknown>";
fprintf (out, "%-16s %s\n", ips, name);
}
if (! lookupHost(target))
goto target_init_error;
- /* Don't ever use loopback (127.0.0) hosts */
+ /* Don't ever use loopback (127.0.0.x) hosts */
{
struct sockaddr_in *iaddr = (struct sockaddr_in *) &(target->address);
unsigned long ip = iaddr->sin_addr.s_addr;
- if ((ip & 255) == 127 &&
- ((ip >> 8) & 255) == 0 &&
- ((ip >> 16) & 255) == 0)
+
+ if ((ntohl (ip) & 0xFFFFFF00L) == 0x7f000000L) /* 127.0.0.x */
{
if (debug_p)
fprintf (stderr, "%s: ignoring loopback host %s\n",
}
}
+ /* Don't ever use broadcast (255.x.x.x) hosts */
+ {
+ struct sockaddr_in *iaddr = (struct sockaddr_in *) &(target->address);
+ unsigned long ip = iaddr->sin_addr.s_addr;
+ if ((ntohl (ip) & 0xFF000000L) == 0xFF000000L) /* 255.x.x.x */
+ {
+ if (debug_p)
+ fprintf (stderr, "%s: ignoring broadcast host %s\n",
+ progname, target->name);
+ goto target_init_error;
+ }
+ }
+
/* Done */
if (debug_p)
return NULL;
}
+ /* Kludge: on OSX, variables have not been expanded in the command
+ line arguments, so as a special case, allow the string to begin
+ with literal "$HOME/" or "~/".
+
+ This is so that the "Known Hosts" menu item in sonar.xml works.
+ */
+ if (!strncmp(fname, "~/", 2) || !strncmp(fname, "$HOME/", 6)) {
+ char *s = strchr (fname, '/');
+ strcpy (buf, getenv("HOME"));
+ strcat (buf, s);
+ fname = buf;
+ }
+
/* Open the file */
if ((fp = fopen(fname, "r")) == NULL) {
char msg[1024];
- sprintf(msg, "%s: unable to open host file %s", progname, fname);
- perror(msg);
+ sprintf(msg, "%s: unable to open host file %s", progname, fname);
+#ifdef HAVE_COCOA
+ if (debug_p) /* on OSX don't syslog this */
+#endif
+ perror(msg);
return NULL;
}
struct sockaddr_in *i1 = (struct sockaddr_in *) &(rest->address);
unsigned long ip1 = i1->sin_addr.s_addr;
- static ping_target *rest2;
+ ping_target *rest2;
for (rest2 = rest; rest2; rest2 = rest2->next)
{
if (rest2 && rest2->next)
/*
* Generate a list ping targets consisting of all of the entries on
- * the same subnet.
+ * the same subnet. 'base' ip is in network order; 0 means localhost.
*
* Returns:
* A list of all of the hosts on this net.
*/
static ping_target *
-subnetHostsList(int base, int subnet_width)
+subnetHostsList(unsigned long n_base, int subnet_width)
{
- unsigned long mask;
+ unsigned long h_mask; /* host order */
+ unsigned long h_base; /* host order */
/* Local Variables */
/* Construct targets for all addresses in this subnet */
- mask = 0;
+ h_mask = 0;
for (i = 0; i < subnet_width; i++)
- mask |= (1L << (31-i));
+ h_mask |= (1L << (31-i));
/* If no base IP specified, assume localhost. */
- if (base == 0)
- base = ((((unsigned char) hent->h_addr_list[0][0]) << 24) |
- (((unsigned char) hent->h_addr_list[0][1]) << 16) |
- (((unsigned char) hent->h_addr_list[0][2]) << 8) |
- (((unsigned char) hent->h_addr_list[0][3])));
-
- if (base == ((127 << 24) | 1))
+ if (n_base == 0)
+ n_base = pack_addr (hent->h_addr_list[0][0],
+ hent->h_addr_list[0][1],
+ hent->h_addr_list[0][2],
+ hent->h_addr_list[0][3]);
+ h_base = ntohl (n_base);
+
+ if (h_base == 0x7F000001L) /* 127.0.0.1 in host order */
{
+ unsigned int a, b, c, d;
+ unpack_addr (n_base, &a, &b, &c, &d);
fprintf (stderr,
"%s: unable to determine local subnet address: \"%s\"\n"
- " resolves to loopback address %d.%d.%d.%d.\n",
- progname, hostname,
- (base >> 24) & 255, (base >> 16) & 255,
- (base >> 8) & 255, (base ) & 255);
+ " resolves to loopback address %u.%u.%u.%u.\n",
+ progname, hostname, a, b, c, d);
return NULL;
}
for (i = 255; i >= 0; i--) {
- int ip = (base & 0xFFFFFF00) | i;
+ unsigned int a, b, c, d;
+ int ip = (h_base & 0xFFFFFF00L) | i; /* host order */
- if ((ip & mask) != (base & mask)) /* not in the mask range at all */
+ if ((ip & h_mask) != (h_base & h_mask)) /* not in mask range at all */
continue;
- if ((ip & ~mask) == 0) /* broadcast address */
+ if ((ip & ~h_mask) == 0) /* broadcast address */
continue;
- if ((ip & ~mask) == ~mask) /* broadcast address */
+ if ((ip & ~h_mask) == ~h_mask) /* broadcast address */
continue;
- sprintf (address, "%d.%d.%d.%d",
- (ip>>24)&255, (ip>>16)&255, (ip>>8)&255, (ip)&255);
+ unpack_addr (htonl (ip), &a, &b, &c, &d);
+ sprintf (address, "%u.%u.%u.%u", a, b, c, d);
if (debug_p > 1)
- fprintf(stderr, "%s: subnet: %s (%d.%d.%d.%d & %d.%d.%d.%d / %d)\n",
- progname,
- address,
- (int) (base>>24)&255,
- (int) (base>>16)&255,
- (int) (base>> 8)&255,
- (int) (base&mask&255),
- (int) (mask>>24)&255,
- (int) (mask>>16)&255,
- (int) (mask>> 8)&255,
- (int) (mask&255),
- (int) subnet_width);
+ {
+ unsigned int aa, ab, ac, ad;
+ unsigned int ma, mb, mc, md;
+ unpack_addr (htonl (h_base & h_mask), &aa, &ab, &ac, &ad);
+ unpack_addr (htonl (h_mask), &ma, &mb, &mc, &md);
+ fprintf (stderr,
+ "%s: subnet: %s (%u.%u.%u.%u & %u.%u.%u.%u / %d)\n",
+ progname, address,
+ aa, ab, ac, ad,
+ ma, mb, mc, md,
+ subnet_width);
+ }
p = address + strlen(address) + 1;
sprintf(p, "%d", i);
* A newly allocated ping_info structure or null if an error occured.
*/
-static ping_target *parse_mode (Bool ping_works_p);
+static ping_target *parse_mode (sonar_info *, Bool ping_works_p);
+
+/* yes, there is only one, even when multiple savers are running in the
+ same address space - since we can only open this socket before dropping
+ privs.
+ */
+static int global_icmpsock = 0;
static ping_info *
-init_ping(void)
+init_ping(sonar_info *si)
{
Bool socket_initted_p = False;
+ Bool socket_raw_p = False;
/* Local Variables */
goto ping_init_error;
}
- /* Create the ICMP socket */
+ /* Create the ICMP socket. Do this before dropping privs.
+
+ Raw sockets can only be opened by root (or setuid root), so we
+ only try to do this when the effective uid is 0.
- if ((pi->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) >= 0) {
+ We used to just always try, and notice the failure. But apparently
+ that causes "SELinux" to log spurious warnings when running with the
+ "strict" policy. So to avoid that, we just don't try unless we
+ know it will work.
+
+ On MacOS X, we can avoid the whole problem by using a
+ non-privileged datagram instead of a raw socket.
+ */
+ if (global_icmpsock) {
+ pi->icmpsock = global_icmpsock;
socket_initted_p = True;
+ if (debug_p)
+ fprintf (stderr, "%s: re-using icmp socket\n", progname);
+
+ } else if ((pi->icmpsock =
+ socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) >= 0) {
+ socket_initted_p = True;
+
+ } else if (geteuid() == 0 &&
+ (pi->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) >= 0) {
+ socket_initted_p = True;
+ socket_raw_p = True;
}
+ if (socket_initted_p) {
+ global_icmpsock = pi->icmpsock;
+ socket_initted_p = True;
+ if (debug_p)
+ fprintf (stderr, "%s: opened %s icmp socket\n", progname,
+ (socket_raw_p ? "raw" : "dgram"));
+ } else if (debug_p)
+ fprintf (stderr, "%s: unable to open icmp socket\n", progname);
+
/* Disavow privs */
setuid(getuid());
pi->pid = getpid() & 0xFFFF;
pi->seq = 0;
- pi->timeout = get_integer_resource("pingTimeout", "PingTimeout");
+ pi->timeout = get_integer_resource(si->dpy, "pingTimeout", "PingTimeout");
/* Generate a list of targets */
- pi->targets = parse_mode (socket_initted_p);
+ pi->targets = parse_mode (si, socket_initted_p);
pi->targets = delete_duplicate_hosts (pi->targets);
ip = (struct ip *) packet;
iphdrlen = IP_HDRLEN(ip) << 2;
icmph = (struct ICMP *) &packet[iphdrlen];
+ then = (struct timeval *) &packet[iphdrlen + sizeof(struct ICMP)];
+
/* Was the packet a reply?? */
return bl;
}
+# if 0 /* Don't need to do this -- the host names are already as
+ resolved as they're going to get. (We stored the resolved
+ name in the outgoing ping packet, so that same string just
+ came back to us.)
+ */
+
/* If the name is an IP addr, try to resolve it. */
{
int iip[4];
if (4 == sscanf(name, " %d.%d.%d.%d %c",
&iip[0], &iip[1], &iip[2], &iip[3], &c))
{
- unsigned char ip[4];
+ struct sockaddr_in iaddr;
struct hostent *h;
- ip[0] = iip[0]; ip[1] = iip[1]; ip[2] = iip[2]; ip[3] = iip[3];
- h = gethostbyaddr ((char *) ip, 4, AF_INET);
+ iaddr.sin_addr.s_addr = pack_addr (iip[0],iip[1],iip[2],iip[3]);
+ if (resolve_p)
+ h = gethostbyaddr ((const char *) &iaddr.sin_addr.s_addr,
+ sizeof(iaddr.sin_addr.s_addr),
+ AF_INET);
+ else
+ h = 0;
+
if (h && h->h_name && *h->h_name)
{
free (name);
}
}
}
+# endif /* 0 */
/* Create the new Bogie and add it to the list we are building */
new->next = bl;
bl = new;
- /* Compute the round trip time */
+ {
+ float msec = delta(then, &now) / 1000.0;
- then = (struct timeval *) &packet[iphdrlen +
- sizeof(struct ICMP)];
- new->distance = delta(then, &now) / 100;
- if (new->distance == 0)
- new->distance = 2; /* HACK */
+ if (times_p)
+ {
+ if (new->desc) free (new->desc);
+ new->desc = (char *) malloc (30);
+ if (msec > 99) sprintf (new->desc, " %.0f ms ", msec);
+ else if (msec > 9) sprintf (new->desc, " %.1f ms ", msec);
+ else if (msec > 1) sprintf (new->desc, " %.2f ms ", msec);
+ else sprintf (new->desc, " %.3f ms ", msec);
+ }
+
+ if (debug_p && times_p) /* print ping-like stuff to stdout */
+ {
+ struct sockaddr_in *iaddr = (struct sockaddr_in *) &from;
+ unsigned int a, b, c, d;
+ char ipstr[20];
+ char *s = strdup (new->desc);
+ char *s2 = s, *s3 = s;
+ while (*s2 == ' ') s2++;
+ s3 = strchr (s2, ' ');
+ if (s3) *s3 = 0;
+
+ unpack_addr (iaddr->sin_addr.s_addr, &a, &b, &c, &d);
+ sprintf (ipstr, "%d.%d.%d.%d", a, b, c, d);
+
+ fprintf (stdout,
+ "%3d bytes from %28s: "
+ "icmp_seq=%-4d ttl=%d time=%s ms\n",
+ result,
+ name,
+ /*ipstr,*/
+ ICMP_SEQ(icmph), si->TTL, s2);
+ free (s);
+ }
+
+ /* Don't put anyone *too* close to the center of the screen. */
+ msec += 0.6;
+
+ new->distance = msec * 10;
+ }
}
}
*/
ping_info *pi = (ping_info *) vpi;
- static ping_target *ptr = NULL;
int tick = si->current * -1 + 1;
- if ((ptr == NULL) && (tick == 1))
- ptr = pi->targets;
+ if ((si->last_ptr == NULL) && (tick == 1))
+ si->last_ptr = pi->targets;
if (pi->numtargets <= 90) {
int xdrant = 90 / pi->numtargets;
if ((tick % xdrant) == 0) {
- if (ptr != (ping_target *) 0) {
- sendping(pi, ptr);
- ptr = ptr->next;
+ if (si->last_ptr != (ping_target *) 0) {
+ sendping(pi, si->last_ptr);
+ si->last_ptr = si->last_ptr->next;
}
}
} else if (pi->numtargets > 90) {
- if (ptr != (ping_target *) 0) {
- sendping(pi, ptr);
- ptr = ptr->next;
+ if (si->last_ptr != (ping_target *) 0) {
+ sendping(pi, si->last_ptr);
+ si->last_ptr = si->last_ptr->next;
}
}
* Initialize the simulation mode.
*/
+#define BELLRAND(x) (((random()%(x)) + (random()%(x)) + (random()%(x)))/3)
+
static sim_info *
-init_sim(void)
+init_sim(Display *dpy)
{
-
/* Local Variables */
sim_info *si;
int i;
+ int maxdist = 20; /* larger than this is off the (log) display */
+
/* Create the simulation info structure */
if ((si = (sim_info *) calloc(1, sizeof(sim_info))) == NULL) {
/* Team A */
- si->numA = get_integer_resource("teamACount", "TeamACount");
+ si->numA = get_integer_resource(dpy, "teamACount", "TeamACount");
if ((si->teamA = (sim_target *)calloc(si->numA, sizeof(sim_target)))
== NULL) {
free(si);
fprintf(stderr, "%s: Out of Memory\n", progname);
return NULL;
}
- si->teamAID = get_string_resource("teamAName", "TeamAName");
+ si->teamAID = get_string_resource(dpy, "teamAName", "TeamAName");
for (i = 0; i < si->numA; i++) {
if ((si->teamA[i].name = (char *) malloc(strlen(si->teamAID) + 4))
== NULL) {
}
sprintf(si->teamA[i].name, "%s%03d", si->teamAID, i+1);
si->teamA[i].nexttick = random() % 90;
- si->teamA[i].nextdist = random() % 100;
+ si->teamA[i].nextdist = BELLRAND(maxdist);
si->teamA[i].movedonsweep = -1;
}
/* Team B */
- si->numB = get_integer_resource("teamBCount", "TeamBCount");
+ si->numB = get_integer_resource(dpy, "teamBCount", "TeamBCount");
if ((si->teamB = (sim_target *)calloc(si->numB, sizeof(sim_target)))
== NULL) {
free(si);
fprintf(stderr, "%s: Out of Memory\n", progname);
return NULL;
}
- si->teamBID = get_string_resource("teamBName", "TeamBName");
+ si->teamBID = get_string_resource(dpy, "teamBName", "TeamBName");
for (i = 0; i < si->numB; i++) {
if ((si->teamB[i].name = (char *) malloc(strlen(si->teamBID) + 4))
== NULL) {
}
sprintf(si->teamB[i].name, "%s%03d", si->teamBID, i+1);
si->teamB[i].nexttick = random() % 90;
- si->teamB[i].nextdist = random() % 100;
+ si->teamB[i].nextdist = BELLRAND(maxdist);
si->teamB[i].movedonsweep = -1;
}
{
XGCValues gcv;
Pixmap mask = XCreatePixmap(dpy, win, si->width, si->height, 1);
- GC gc = XCreateGC (dpy, mask, 0, &gcv);
- XSetFunction (dpy, gc, GXclear);
+ GC gc;
+ gcv.foreground = 0;
+ gc = XCreateGC (dpy, mask, GCForeground, &gcv);
XFillRectangle (dpy, mask, gc, 0, 0, si->width, si->height);
- XSetFunction (dpy, gc, GXset);
+ XSetForeground (dpy, gc, 1);
XFillArc(dpy, mask, gc, si->minx, si->miny,
si->maxx - si->minx, si->maxy - si->miny,
0, 360 * 64);
+ XFreeGC (dpy, gc);
return mask;
}
-/*
- * Initialize the Sonar.
- *
- * Args:
- * dpy - The X display.
- * win - The X window;
- *
- * Returns:
- * A sonar_info strcuture or null if memory allocation problems occur.
- */
-
-static sonar_info *
-init_sonar(Display *dpy, Window win)
+static void
+reshape (sonar_info *si)
{
-
- /* Local Variables */
-
- XGCValues gcv;
- XWindowAttributes xwa;
- sonar_info *si;
- XColor start, end;
- int h1, h2;
- double s1, s2, v1, v2;
-
- /* Create the Sonar information structure */
-
- if ((si = (sonar_info *) calloc(1, sizeof(sonar_info))) == NULL) {
- fprintf(stderr, "%s: Out of memory\n", progname);
- return NULL;
- }
-
- /* Initialize the structure for the current environment */
-
- si->dpy = dpy;
- si->win = win;
- si->visible = NULL;
- XGetWindowAttributes(dpy, win, &xwa);
- si->cmap = xwa.colormap;
- si->width = xwa.width;
- si->height = xwa.height;
- si->centrex = si->width / 2;
- si->centrey = si->height / 2;
- si->maxx = si->centrex + MY_MIN(si->centrex, si->centrey) - 10;
- si->minx = si->centrex - MY_MIN(si->centrex, si->centrey) + 10;
- si->maxy = si->centrey + MY_MIN(si->centrex, si->centrey) - 10;
- si->miny = si->centrey - MY_MIN(si->centrex, si->centrey) + 10;
- si->radius = si->maxx - si->centrex;
- si->current = 0;
- si->sweepnum = 0;
-
- /* Get the font */
-
- if (((si->font = XLoadQueryFont(dpy, get_string_resource ("font", "Font")))
- == NULL) &&
- ((si->font = XLoadQueryFont(dpy, "fixed")) == NULL)) {
- fprintf(stderr, "%s: can't load an appropriate font\n", progname);
- return NULL;
- }
-
- /* Get the delay between animation frames */
-
- si->delay = get_integer_resource ("delay", "Integer");
-
- if (si->delay < 0) si->delay = 0;
- si->TTL = get_integer_resource("ttl", "TTL");
-
- /* Create the Graphics Contexts that will be used to draw things */
-
- gcv.foreground =
- get_pixel_resource ("sweepColor", "SweepColor", dpy, si->cmap);
- si->hi = XCreateGC(dpy, win, GCForeground, &gcv);
- gcv.font = si->font->fid;
- si->text = XCreateGC(dpy, win, GCForeground|GCFont, &gcv);
- gcv.foreground = get_pixel_resource("scopeColor", "ScopeColor",
- dpy, si->cmap);
- si->erase = XCreateGC (dpy, win, GCForeground, &gcv);
- gcv.foreground = get_pixel_resource("gridColor", "GridColor",
- dpy, si->cmap);
- si->grid = XCreateGC (dpy, win, GCForeground, &gcv);
-
- /* Install the clip mask... */
- {
- Pixmap mask = scope_mask (dpy, win, si);
- XSetClipMask(dpy, si->text, mask);
- XSetClipMask(dpy, si->erase, mask);
- XFreePixmap (dpy, mask); /* it's been copied into the GCs */
- }
-
- /* Compute pixel values for fading text on the display */
-
- XParseColor(dpy, si->cmap,
- get_string_resource("textColor", "TextColor"), &start);
- XParseColor(dpy, si->cmap,
- get_string_resource("scopeColor", "ScopeColor"), &end);
-
- rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
- rgb_to_hsv (end.red, end.green, end.blue, &h2, &s2, &v2);
-
- si->text_steps = get_integer_resource("textSteps", "TextSteps");
- if (si->text_steps < 0 || si->text_steps > 255)
- si->text_steps = 10;
-
- si->text_colors = (XColor *) calloc(si->text_steps, sizeof(XColor));
- make_color_ramp (dpy, si->cmap,
- h1, s1, v1,
- h2, s2, v2,
- si->text_colors, &si->text_steps,
- False, True, False);
-
- /* Compute the pixel values for the fading sweep */
-
- XParseColor(dpy, si->cmap,
- get_string_resource("sweepColor", "SweepColor"), &start);
-
- rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
-
- si->sweep_degrees = get_integer_resource("sweepDegrees", "Degrees");
- if (si->sweep_degrees <= 0) si->sweep_degrees = 20;
- if (si->sweep_degrees > 350) si->sweep_degrees = 350;
-
- si->sweep_segs = get_integer_resource("sweepSegments", "SweepSegments");
- if (si->sweep_segs < 1 || si->sweep_segs > 255)
- si->sweep_segs = 255;
-
- si->sweep_colors = (XColor *) calloc(si->sweep_segs, sizeof(XColor));
- make_color_ramp (dpy, si->cmap,
- h1, s1, v1,
- h2, s2, v2,
- si->sweep_colors, &si->sweep_segs,
- False, True, False);
-
- if (si->sweep_segs <= 0)
- si->sweep_segs = 1;
-
- /* Done */
-
- return si;
+ XWindowAttributes xgwa;
+ Pixmap mask;
+ XGetWindowAttributes(si->dpy, si->win, &xgwa);
+ si->width = xgwa.width;
+ si->height = xgwa.height;
+ si->centrex = si->width / 2;
+ si->centrey = si->height / 2;
+ si->maxx = si->centrex + MY_MIN(si->centrex, si->centrey) - 10;
+ si->minx = si->centrex - MY_MIN(si->centrex, si->centrey) + 10;
+ si->maxy = si->centrey + MY_MIN(si->centrex, si->centrey) - 10;
+ si->miny = si->centrey - MY_MIN(si->centrex, si->centrey) + 10;
+ si->radius = si->maxx - si->centrex;
+
+ /* Install the clip mask... */
+ if (! debug_p) {
+ mask = scope_mask (si->dpy, si->win, si);
+ XSetClipMask(si->dpy, si->text, mask);
+ XSetClipMask(si->dpy, si->erase, mask);
+ XFreePixmap (si->dpy, mask); /* it's been copied into the GCs */
+ }
}
+
/*
* Update the location of a simulated bogie.
*/
*/
static int
-computeStringX(sonar_info *si, char *label, int x)
+computeStringX(sonar_info *si, const char *label, int x)
{
int width = XTextWidth(si->font, label, strlen(label));
* The y coordinate of the start of the label.
*/
-/* TODO: Add smarts to keep label in sonar screen */
-
static int
computeStringY(sonar_info *si, int y)
{
- int fheight = si->font->ascent + si->font->descent;
- return y + 5 + fheight;
+ int fheight = si->font->ascent /* + si->font->descent */;
+ return y + fheight;
}
/*
*/
static void
-DrawBogie(sonar_info *si, int draw, char *name, int degrees,
- int distance, int ttl, int age)
+DrawBogie(sonar_info *si, int draw, const char *name, const char *desc,
+ int degrees, int distance, int ttl, int age)
{
/* Local Variables */
/* Draw (or erase) the Bogie */
XFillArc(si->dpy, si->win, gc, x, y, 5, 5, 0, 360 * 64);
+
+ x += 3; /* move away from the dot */
+ y += 7;
+ y = computeStringY(si, y);
XDrawString(si->dpy, si->win, gc,
- computeStringX(si, name, x),
- computeStringY(si, y), name, strlen(name));
+ computeStringX(si, name, x), y,
+ name, strlen(name));
+
+ if (desc && *desc)
+ {
+ y = computeStringY(si, y);
+ XDrawString(si->dpy, si->win, gc,
+ computeStringX(si, desc, x), y,
+ desc, strlen(desc));
+ }
}
if (((bp->tick == si->current) && (++bp->age >= bp->ttl)) ||
(findNode(bl, bp->name) != NULL)) {
- DrawBogie(si, 0, bp->name, bp->tick,
+
+#ifndef HAVE_COCOA /* we repaint every frame: no need to erase */
+ DrawBogie(si, 0, bp->name, bp->desc, bp->tick,
bp->distance, bp->ttl, bp->age);
+#endif /* HAVE_COCOA */
+
if (prev == NULL)
si->visible = bp->next;
else
/* Draw the sweep */
{
- int seg_deg = (si->sweep_degrees * 64) / si->sweep_segs;
int start_deg = si->current * 4 * 64;
+ int end_deg = start_deg + (si->sweep_degrees * 64);
+ int seg_deg = (end_deg - start_deg) / si->sweep_segs;
if (seg_deg <= 0) seg_deg = 1;
- for (i = 0; i < si->sweep_segs; i++) {
- XSetForeground(si->dpy, si->hi, si->sweep_colors[i].pixel);
- XFillArc(si->dpy, si->win, si->hi, si->minx, si->miny,
- si->maxx - si->minx, si->maxy - si->miny,
- start_deg + (i * seg_deg),
- seg_deg);
- }
/* Remove the trailing wedge the sonar */
XFillArc(si->dpy, si->win, si->erase, si->minx, si->miny,
si->maxx - si->minx, si->maxy - si->miny,
- start_deg + (i * seg_deg),
- (4 * 64));
+ end_deg,
+ 4 * 64);
+
+ for (i = 0; i < si->sweep_segs; i++) {
+ int ii = si->sweep_segs - i - 1;
+ XSetForeground (si->dpy, si->hi, si->sweep_colors[ii].pixel);
+ XFillArc (si->dpy, si->win, si->hi, si->minx, si->miny,
+ si->maxx - si->minx, si->maxy - si->miny,
+ start_deg,
+ seg_deg * (ii + 1));
+ }
}
/* Move the new targets to the visible list */
for (bp = si->visible; bp != NULL; bp = bp->next) {
if (bp->age < bp->ttl) /* grins */
- DrawBogie(si, 1, bp->name, bp->tick, bp->distance, bp->ttl,bp->age);
+ DrawBogie(si, 1, bp->name, bp->desc,
+ bp->tick, bp->distance, bp->ttl,bp->age);
}
/* Redraw the grid */
static ping_target *
-parse_mode (Bool ping_works_p)
+parse_mode (sonar_info *si, Bool ping_works_p)
{
- char *source = get_string_resource ("ping", "Ping");
+ char *source = get_string_resource (si->dpy, "ping", "Ping");
char *token, *end;
+#ifdef HAVE_PING
char dummy;
+#endif
ping_target *hostlist = 0;
if (!*source || !strcmp (source, "default"))
{
+ free (source);
# ifdef HAVE_PING
if (ping_works_p) /* if root or setuid, ping will work. */
source = strdup("subnet/29,/etc/hosts");
/* subnet: A.B.C.D/M
subnet: A.B.C/M
*/
- unsigned long ip = (n0 << 24) | (n1 << 16) | (n2 << 8) | n3;
+ unsigned long ip = pack_addr (n0, n1, n2, n3);
new = subnetHostsList(ip, m);
}
else if (4 == sscanf (token, "%u.%u.%u.%u %c", &n0, &n1, &n2, &n3, &d))
{
new = subnetHostsList(0, m);
}
- else if (*token == '.' || *token == '/' || !stat (token, &st))
+ else if (*token == '.' || *token == '/' ||
+ *token == '$' || *token == '~' ||
+ !stat (token, &st))
{
/* file name
*/
nn->next = hostlist;
hostlist = new;
- sensor = ping;
+ si->sensor = ping;
}
#endif /* HAVE_PING */
token++;
}
+ free (source);
return hostlist;
}
-
/*
- * Main screen saver hack.
+ * Initialize the Sonar.
*
* Args:
* dpy - The X display.
- * win - The X window.
+ * win - The X window;
+ *
+ * Returns:
+ * A sonar_info strcuture or null if memory allocation problems occur.
*/
-void
-screenhack(Display *dpy, Window win)
+static void *
+sonar_init (Display *dpy, Window win)
{
-
/* Local Variables */
+ XGCValues gcv;
+ XWindowAttributes xwa;
sonar_info *si;
- struct timeval start, finish;
- Bogie *bl;
- long sleeptime;
+ XColor start, end;
+ int h1, h2;
+ double s1, s2, v1, v2;
+
+ debug_p = get_boolean_resource (dpy, "debug", "Debug");
+ resolve_p = get_boolean_resource (dpy, "resolve", "Resolve");
+ times_p = get_boolean_resource (dpy, "showTimes", "ShowTimes");
+
+ /* Create the Sonar information structure */
+
+ if ((si = (sonar_info *) calloc(1, sizeof(sonar_info))) == NULL) {
+ fprintf(stderr, "%s: Out of memory\n", progname);
+ exit (1);
+ }
+
+ /* Initialize the structure for the current environment */
+
+ si->dpy = dpy;
+ si->win = win;
+ si->visible = NULL;
+
+ XGetWindowAttributes(dpy, win, &xwa);
+ si->cmap = xwa.colormap;
+
+ si->current = 0;
+ si->sweepnum = 0;
+
+ /* Get the font */
+
+ {
+ char *fn = get_string_resource (dpy, "font", "Font");
+ if (((si->font = XLoadQueryFont(dpy, fn)) == NULL) &&
+ ((si->font = XLoadQueryFont(dpy, "fixed")) == NULL)) {
+ fprintf(stderr, "%s: can't load an appropriate font\n", progname);
+ exit (1);
+ }
+ if (fn) free (fn);
+ }
+
+ /* Get the delay between animation frames */
+
+ si->delay = get_integer_resource (dpy, "delay", "Integer");
+
+ if (si->delay < 0) si->delay = 0;
+ si->TTL = get_integer_resource(dpy, "ttl", "TTL");
+
+ /* Create the Graphics Contexts that will be used to draw things */
+
+ gcv.foreground =
+ get_pixel_resource (dpy, si->cmap, "sweepColor", "SweepColor");
+ si->hi = XCreateGC(dpy, win, GCForeground, &gcv);
+ gcv.font = si->font->fid;
+ si->text = XCreateGC(dpy, win, GCForeground|GCFont, &gcv);
+ gcv.foreground = get_pixel_resource(dpy, si->cmap,
+ "scopeColor", "ScopeColor");
+ si->erase = XCreateGC (dpy, win, GCForeground, &gcv);
+ gcv.foreground = get_pixel_resource(dpy, si->cmap,
+ "gridColor", "GridColor");
+ si->grid = XCreateGC (dpy, win, GCForeground, &gcv);
+
+ reshape (si);
+
+ /* Compute pixel values for fading text on the display */
+ {
+ char *s = get_string_resource(dpy, "textColor", "TextColor");
+ XParseColor(dpy, si->cmap, s, &start);
+ if (s) free (s);
+ s = get_string_resource(dpy, "scopeColor", "ScopeColor");
+ XParseColor(dpy, si->cmap, s, &end);
+ if (s) free (s);
+ }
+
+ rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
+ rgb_to_hsv (end.red, end.green, end.blue, &h2, &s2, &v2);
+
+ si->text_steps = get_integer_resource(dpy, "textSteps", "TextSteps");
+ if (si->text_steps < 0 || si->text_steps > 255)
+ si->text_steps = 10;
+
+ si->text_colors = (XColor *) calloc(si->text_steps, sizeof(XColor));
+ make_color_ramp (dpy, si->cmap,
+ h1, s1, v1,
+ h2, s2, v2,
+ si->text_colors, &si->text_steps,
+ False, True, False);
+
+ /* Compute the pixel values for the fading sweep */
+
+ {
+ char *s = get_string_resource(dpy, "sweepColor", "SweepColor");
+ XParseColor(dpy, si->cmap, s, &start);
+ if (s) free (s);
+ }
+
+ rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
+
+ si->sweep_degrees = get_integer_resource(dpy, "sweepDegrees", "Degrees");
+ if (si->sweep_degrees <= 0) si->sweep_degrees = 20;
+ if (si->sweep_degrees > 350) si->sweep_degrees = 350;
+
+ si->sweep_segs = get_integer_resource(dpy, "sweepSegments", "SweepSegments");
+ if (si->sweep_segs < 1 || si->sweep_segs > 255)
+ si->sweep_segs = 255;
+
+ si->sweep_colors = (XColor *) calloc(si->sweep_segs, sizeof(XColor));
+ make_color_ramp (dpy, si->cmap,
+ h1, s1, v1,
+ h2, s2, v2,
+ si->sweep_colors, &si->sweep_segs,
+ False, True, False);
+
+ if (si->sweep_segs <= 0)
+ si->sweep_segs = 1;
- debug_p = get_boolean_resource ("debug", "Debug");
- sensor = 0;
# ifdef HAVE_PING
- sensor_info = (void *) init_ping();
+ si->sensor_info = (void *) init_ping(si);
# else /* !HAVE_PING */
- sensor_info = 0;
- parse_mode (0); /* just to check argument syntax */
+ parse_mode (dpy, 0); /* just to check argument syntax */
# endif /* !HAVE_PING */
- if (sensor == 0)
+ if (!si->sensor)
{
- sensor = simulator;
- if ((sensor_info = (void *) init_sim()) == NULL)
+ si->sensor = simulator;
+ si->sensor_info = (void *) init_sim(dpy);
+ if (! si->sensor_info)
exit(1);
}
- if ((si = init_sonar(dpy, win)) == (sonar_info *) 0)
- exit(1);
+ /* Done */
+ return si;
+}
- /* Sonar loop */
- while (1) {
+static unsigned long
+sonar_draw (Display *dpy, Window window, void *closure)
+{
+ sonar_info *si = (sonar_info *) closure;
+ Bogie *bl;
+ struct timeval start, finish;
+ long delay;
+
+# ifdef HAVE_COCOA /* repaint the whole window so that antialiasing works */
+ XClearWindow (dpy,window);
+ XFillRectangle (dpy, window, si->erase, 0, 0, si->width, si->height);
+# endif
- /* Call the sensor and display the results */
+ /* Call the sensor and display the results */
# ifdef GETTIMEOFDAY_TWO_ARGS
- gettimeofday(&start, (struct timezone *) 0);
+ gettimeofday(&start, (struct timezone *) 0);
# else
- gettimeofday(&start);
+ gettimeofday(&start);
# endif
- bl = sensor(si, sensor_info);
- Sonar(si, bl);
+ bl = si->sensor(si, si->sensor_info);
+ Sonar(si, bl);
- /* Set up and sleep for the next one */
+ /* Set up and sleep for the next one */
+
+ si->current = (si->current - 1) % 90;
+ if (si->current == 0)
+ si->sweepnum++;
- si->current = (si->current - 1) % 90;
- if (si->current == 0)
- si->sweepnum++;
- XSync (dpy, False);
# ifdef GETTIMEOFDAY_TWO_ARGS
- gettimeofday(&finish, (struct timezone *) 0);
+ gettimeofday(&finish, (struct timezone *) 0);
# else
- gettimeofday(&finish);
+ gettimeofday(&finish);
# endif
- sleeptime = si->delay - delta(&start, &finish);
- screenhack_handle_events (dpy);
- if (sleeptime > 0L)
- usleep(sleeptime);
- }
+ delay = si->delay - delta(&start, &finish);
+ if (delay < 0) delay = 0;
+ return delay;
+}
+
+
+static void
+sonar_reshape (Display *dpy, Window window, void *closure,
+ unsigned int w, unsigned int h)
+{
+ sonar_info *si = (sonar_info *) closure;
+ XClearWindow (si->dpy, si->win);
+ reshape (si);
+}
+
+static Bool
+sonar_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+ return False;
+}
+
+static void
+sonar_free (Display *dpy, Window window, void *closure)
+{
}
+
+
+
+static const char *sonar_defaults [] = {
+ ".background: #000000",
+ ".sweepColor: #00FF00",
+ "*delay: 100000",
+ "*scopeColor: #003300",
+ "*gridColor: #00AA00",
+ "*textColor: #FFFF00",
+ "*ttl: 90",
+ "*mode: default",
+ "*font: fixed",
+ "*sweepDegrees: 30",
+
+ "*textSteps: 80", /* npixels */
+ "*sweepSegments: 80", /* npixels */
+
+ "*pingTimeout: 3000",
+
+ "*teamAName: F18",
+ "*teamBName: MIG",
+ "*teamACount: 4",
+ "*teamBCount: 4",
+
+ "*ping: default",
+ "*resolve: true",
+ "*showTimes: true",
+ ".debug: false",
+ 0
+};
+
+static XrmOptionDescRec sonar_options [] = {
+ {"-background", ".background", XrmoptionSepArg, 0 },
+ {"-sweep-color", ".sweepColor", XrmoptionSepArg, 0 },
+ {"-scope-color", ".scopeColor", XrmoptionSepArg, 0 },
+ {"-grid-color", ".gridColor", XrmoptionSepArg, 0 },
+ {"-text-color", ".textColor", XrmoptionSepArg, 0 },
+ {"-ttl", ".ttl", XrmoptionSepArg, 0 },
+ {"-font", ".font", XrmoptionSepArg, 0 },
+#ifdef HAVE_PING
+ {"-ping-timeout", ".pingTimeout", XrmoptionSepArg, 0 },
+#endif /* HAVE_PING */
+ {"-team-a-name", ".teamAName", XrmoptionSepArg, 0 },
+ {"-team-b-name", ".teamBName", XrmoptionSepArg, 0 },
+ {"-team-a-count", ".teamACount", XrmoptionSepArg, 0 },
+ {"-team-b-count", ".teamBCount", XrmoptionSepArg, 0 },
+
+ {"-ping", ".ping", XrmoptionSepArg, 0 },
+ {"-no-dns", ".resolve", XrmoptionNoArg, "False" },
+ {"-no-times", ".showTimes", XrmoptionNoArg, "False" },
+ {"-debug", ".debug", XrmoptionNoArg, "True" },
+ { 0, 0, 0, 0 }
+};
+
+
+XSCREENSAVER_MODULE ("Sonar", sonar)