diff options
Diffstat (limited to 'hacks/glx/sonar.c')
-rw-r--r-- | hacks/glx/sonar.c | 1258 |
1 files changed, 1258 insertions, 0 deletions
diff --git a/hacks/glx/sonar.c b/hacks/glx/sonar.c new file mode 100644 index 0000000..c4ea949 --- /dev/null +++ b/hacks/glx/sonar.c @@ -0,0 +1,1258 @@ +/* sonar, Copyright (c) 1998-2018 Jamie Zawinski and Stephen Martin + * + * 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. + */ + +/* Created in Apr 1998 by Stephen Martin <smartin@vanderfleet-martin.net> + * for the RedHat Screensaver Contest + * Heavily hacked by jwz ever since. + * Rewritten in OpenGL by jwz, Aug 2008. + * + * 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 + * on every movement of a sweep arm and the results of that sensor are + * displayed on the screen. The location of the display points (targets) on the + * screen are determined by the current localtion of the sweep and a distance + * value associated with the target. + * + * Currently the only two sensors that are implemented are the simulator + * (the default) and the ping sensor. The simulator randomly creates a set + * of bogies that move around on the scope while the ping sensor can be + * used to display hosts on your network. + * + * The ping code is only compiled in if you define HAVE_ICMP or HAVE_ICMPHDR, + * because, unfortunately, different systems have different ways of creating + * these sorts of packets. + * + * 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: + * + * - search the output of "netstat" for the list of hosts to ping; + * - plot the contents of /proc/interrupts; + * - plot the process table, by process size, cpu usage, or total time; + * - plot the logged on users by idle time or cpu usage. + * - plot IM contacts or Facebook friends and their last-activity times. + */ + +#define DEF_FONT "-*-courier-bold-r-normal-*-*-480-*-*-*-*-iso8859-1" +#define DEF_SPEED "1.0" +#define DEF_SWEEP_SIZE "0.3" +#define DEF_FONT_SIZE "12" +#define DEF_TEAM_A_NAME "F18" +#define DEF_TEAM_B_NAME "MIG" +#define DEF_TEAM_A_COUNT "4" +#define DEF_TEAM_B_COUNT "4" +#define DEF_PING "default" +#define DEF_PING_TIMEOUT "3000" +#define DEF_RESOLVE "True" +#define DEF_TIMES "True" +#define DEF_WOBBLE "True" +#define DEF_DEBUG "False" + +#include "thread_util.h" + +#define DEFAULTS "*delay: 30000 \n" \ + "*font: " DEF_FONT "\n" \ + "*showFPS: False \n" \ + "*wireframe: False \n" \ + "*texFontCacheSize: 300 \n" \ + THREAD_DEFAULTS_XLOCK + + +# define release_sonar 0 +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) + +#ifdef HAVE_UNISTD_H +# include <unistd.h> /* for setuid() */ +#endif + +#include "xlockmore.h" +#include "sonar.h" +#include "gltrackball.h" +#include "rotator.h" +#include "texfont.h" +#include <ctype.h> + +#ifdef USE_GL /* whole file */ + +/* #define TEST_ASYNC_NETDB 1 */ + +# if TEST_ASYNC_NETDB +# include "async_netdb.h" + +# include <assert.h> +# include <netinet/in.h> +# include <stdio.h> +# endif /* TEST_ASYNC_NETDB */ + +typedef struct { + double x,y,z; +} XYZ; + +typedef struct { + GLXContext *glx_context; + trackball_state *trackball; + rotator *rot; + Bool button_down_p; + + double start_time; + GLfloat sweep_offset; + + GLuint screen_list, grid_list, sweep_list, table_list; + int screen_polys, grid_polys, sweep_polys, table_polys; + GLfloat sweep_th; + GLfloat line_thickness; + + texture_font_data *texfont; + + enum { MSG, RESOLVE, RUN } state; + sonar_sensor_data *ssd; + char *error; + char *desc; + + sonar_bogie *displayed; /* on screen and fading */ + sonar_bogie *pending; /* returned by sensor, not yet on screen */ + +# if TEST_ASYNC_NETDB + async_name_from_addr_t query0; + async_addr_from_name_t query1; +# endif +} sonar_configuration; + +static sonar_configuration *sps = NULL; + +static GLfloat speed; +static GLfloat sweep_size; +static GLfloat font_size; +static Bool resolve_p; +static Bool times_p; +static Bool wobble_p; +static Bool debug_p; + +static char *team_a_name; +static char *team_b_name; +static int team_a_count; +static int team_b_count; +static int ping_timeout; +static char *ping_arg; + +static XrmOptionDescRec opts[] = { + { "-speed", ".speed", XrmoptionSepArg, 0 }, + { "-sweep-size", ".sweepSize", XrmoptionSepArg, 0 }, + { "-font-size", ".fontSize", XrmoptionSepArg, 0 }, + { "-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 }, + { "-ping-timeout", ".pingTimeout", XrmoptionSepArg, 0 }, + { "-dns", ".resolve", XrmoptionNoArg, "True" }, + { "+dns", ".resolve", XrmoptionNoArg, "False" }, + { "-times", ".times", XrmoptionNoArg, "True" }, + { "+times", ".times", XrmoptionNoArg, "False" }, + { "-wobble", ".wobble", XrmoptionNoArg, "True" }, + { "+wobble", ".wobble", XrmoptionNoArg, "False" }, + THREAD_OPTIONS + { "-debug", ".debug", XrmoptionNoArg, "True" }, +}; + +static argtype vars[] = { + {&speed, "speed", "Speed", DEF_SPEED, t_Float}, + {&sweep_size, "sweepSize", "SweepSize", DEF_SWEEP_SIZE, t_Float}, + {&font_size, "fontSize", "FontSize", DEF_FONT_SIZE, t_Float}, + {&team_a_name, "teamAName", "TeamName", DEF_TEAM_A_NAME, t_String}, + {&team_b_name, "teamBName", "TeamName", DEF_TEAM_B_NAME, t_String}, + {&team_a_count, "teamACount", "TeamCount", DEF_TEAM_A_COUNT, t_Int}, + {&team_b_count, "teamBCount", "TeamCount", DEF_TEAM_A_COUNT, t_Int}, + {&ping_arg, "ping", "Ping", DEF_PING, t_String}, + {&ping_timeout, "pingTimeout", "PingTimeout", DEF_PING_TIMEOUT, t_Int}, + {&resolve_p, "resolve", "Resolve", DEF_RESOLVE, t_Bool}, + {×_p, "times", "Times", DEF_TIMES, t_Bool}, + {&wobble_p, "wobble", "Wobble", DEF_WOBBLE, t_Bool}, + {&debug_p, "debug", "Debug", DEF_DEBUG, t_Bool}, +}; + +ENTRYPOINT ModeSpecOpt sonar_opts = {countof(opts), opts, countof(vars), vars, NULL}; + + +static int +draw_screen (ModeInfo *mi, Bool mesh_p, Bool sweep_p) +{ + sonar_configuration *sp = &sps[MI_SCREEN(mi)]; + int wire = MI_IS_WIREFRAME(mi); + int polys = 0; + int i; + int th_steps, r_steps, r_skip, th_skip, th_skip2, outer_r; + GLfloat curvature = M_PI * 0.4; + GLfloat r0, r1, z0, z1, zoff; + XYZ *ring; + + static const GLfloat glass[4] = {0.0, 0.4, 0.0, 0.5}; + static const GLfloat lines[4] = {0.0, 0.7, 0.0, 0.5}; + static const GLfloat sweepc[4] = {0.2, 1.0, 0.2, 0.5}; + static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0}; + static const GLfloat shiny = 20.0; + + if (wire && !(mesh_p || sweep_p)) return 0; + + glDisable (GL_TEXTURE_2D); + + glFrontFace (GL_CCW); + th_steps = 36 * 4; /* must be a multiple of th_skip2 divisor */ + r_steps = 40; + r_skip = 1; + th_skip = 1; + th_skip2 = 1; + outer_r = 0; + + glMaterialfv (GL_FRONT, GL_SPECULAR, spec); + glMateriali (GL_FRONT, GL_SHININESS, shiny); + glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mesh_p ? lines : glass); + if (wire) glColor3fv (lines); + + if (mesh_p) + { + th_skip = th_steps / 12; + th_skip2 = th_steps / 36; + r_skip = r_steps / 3; + outer_r = r_steps * 0.93; + + if (! wire) + glLineWidth (sp->line_thickness); + } + + ring = (XYZ *) calloc (th_steps, sizeof(*ring)); + + for (i = 0; i < th_steps; i++) + { + double a = M_PI * 2 * i / th_steps; + ring[i].x = cos(a); + ring[i].y = sin(a); + } + + /* place the bottom of the disc on the xy plane. */ + zoff = cos (curvature/2 * (M_PI/2)) / 2; + + for (i = r_steps; i > 0; i--) + { + int j0, j1; + + r0 = i / (GLfloat) r_steps; + r1 = (i+1) / (GLfloat) r_steps; + + if (r1 > 1) r1 = 1; /* avoid asin lossage */ + + z0 = cos (curvature/2 * asin (r0)) / 2 - zoff; + z1 = cos (curvature/2 * asin (r1)) / 2 - zoff; + + glBegin(wire || mesh_p ? GL_LINES : GL_QUAD_STRIP); + for (j0 = 0; j0 <= th_steps; j0++) + { + if (mesh_p && + (i < outer_r + ? (j0 % th_skip != 0) + : (j0 % th_skip2 != 0))) + continue; + + if (sweep_p) + { + GLfloat color[4]; + GLfloat r = 1 - (j0 / (GLfloat) (th_steps * sweep_size)); +#if 0 + color[0] = glass[0] + (sweepc[0] - glass[0]) * r; + color[1] = glass[1] + (sweepc[1] - glass[1]) * r; + color[2] = glass[2] + (sweepc[2] - glass[2]) * r; + color[3] = glass[3]; +#else + color[0] = sweepc[0]; + color[1] = sweepc[1]; + color[2] = sweepc[2]; + color[3] = r; +#endif + glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); + } + + j1 = j0 % th_steps; + glNormal3f (r0 * ring[j1].x, r0 * ring[j1].y, z0); + glVertex3f (r0 * ring[j1].x, r0 * ring[j1].y, z0); + glNormal3f (r1 * ring[j1].x, r1 * ring[j1].y, z1); + glVertex3f (r1 * ring[j1].x, r1 * ring[j1].y, z1); + polys++; + + if (sweep_p && j0 >= th_steps * sweep_size) + break; + if (sweep_p && wire) + break; + } + glEnd(); + + if (mesh_p && + (i == outer_r || + i == r_steps || + (i % r_skip == 0 && + i < r_steps - r_skip))) + { + glBegin(GL_LINE_LOOP); + for (j0 = 0; j0 < th_steps; j0++) + { + glNormal3f (r0 * ring[j0].x, r0 * ring[j0].y, z0); + glVertex3f (r0 * ring[j0].x, r0 * ring[j0].y, z0); + polys++; + } + glEnd(); + } + } + + /* one more polygon for the middle */ + if (!wire && !sweep_p) + { + glBegin(wire || mesh_p ? GL_LINE_LOOP : GL_POLYGON); + glNormal3f (0, 0, 1); + for (i = 0; i < th_steps; i++) + { + glNormal3f (r0 * ring[i].x, r0 * ring[i].y, z0); + glVertex3f (r0 * ring[i].x, r0 * ring[i].y, z0); + } + polys++; + glEnd(); + } + + free (ring); + + return polys; +} + + +static int +draw_text (ModeInfo *mi, const char *string, GLfloat r, GLfloat th, + GLfloat ttl, GLfloat size) +{ + sonar_configuration *sp = &sps[MI_SCREEN(mi)]; + int wire = MI_IS_WIREFRAME(mi); + int polys = 0; + GLfloat font_scale = 0.001 * (size > 0 ? size : font_size) / 14.0; + int lines = 0, max_w = 0, lh = 0; + char *string2 = strdup (string); + char *token = string2; + char *line; + + if (MI_WIDTH(mi) > 2560) font_scale /= 2; /* Retina displays */ + + if (size <= 0) /* if size not specified, draw in yellow with alpha */ + { + GLfloat color[4]; + color[0] = 1; + color[1] = 1; + color[2] = 0; + color[3] = (ttl / (M_PI * 2)) * 1.2; + if (color[3] > 1) color[3] = 1; + + glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); + if (wire) + glColor3f (color[0]*color[3], color[1]*color[3], color[2]*color[3]); + } + + while ((line = strtok (token, "\r\n"))) + { + XCharStruct e; + int w, ascent, descent; + texture_string_metrics (sp->texfont, line, &e, &ascent, &descent); + w = e.width; + lh = ascent + descent; + if (w > max_w) max_w = w; + lines++; + token = 0; + } + + glPushMatrix(); + glTranslatef (r * cos (th), r * sin(th), 0); + glScalef (font_scale, font_scale, font_scale); + + if (size <= 0) /* Draw the dot */ + { + GLfloat s = font_size * 1.7; + glDisable (GL_TEXTURE_2D); + glFrontFace (GL_CW); + glBegin (wire ? GL_LINE_LOOP : GL_QUADS); + glVertex3f (0, s, 0); + glVertex3f (s, s, 0); + glVertex3f (s, 0, 0); + glVertex3f (0, 0, 0); + glEnd(); + glTranslatef (-max_w/2, -lh, 0); + } + else + glTranslatef (-max_w/2, -lh/2, 0); + + /* draw each line, centered */ + if (! wire) glEnable (GL_TEXTURE_2D); + free (string2); + string2 = strdup (string); + token = string2; + while ((line = strtok (token, "\r\n"))) + { + XCharStruct e; + int w; + texture_string_metrics (sp->texfont, line, &e, 0, 0); + w = e.width; + glPushMatrix(); + glTranslatef ((max_w-w)/2, 0, polys * 4); /* 'polys' stops Z-fighting. */ + + if (wire) + { + glBegin (GL_LINE_LOOP); + glVertex3f (0, 0, 0); + glVertex3f (w, 0, 0); + glVertex3f (w, lh, 0); + glVertex3f (0, lh, 0); + glEnd(); + } + else + { + glFrontFace (GL_CW); + print_texture_string (sp->texfont, line); + } + glPopMatrix(); + glTranslatef (0, -lh, 0); + polys++; + token = 0; + } + glPopMatrix(); + + free (string2); + + if (! wire) glEnable (GL_DEPTH_TEST); + + return polys; +} + + +/* There's a disc with a hole in it around the screen, to act as a mask + preventing slightly off-screen bogies from showing up. This clips 'em. + */ +static int +draw_table (ModeInfo *mi) +{ + /*sonar_configuration *sp = &sps[MI_SCREEN(mi)];*/ + int wire = MI_IS_WIREFRAME(mi); + int polys = 0; + int i; + int th_steps = 36 * 4; /* same as in draw_screen */ + + static const GLfloat color[4] = {0.0, 0.0, 0.0, 1.0}; + static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0}; + static const GLfloat shiny = 0.0; + + if (wire) return 0; + + glDisable (GL_TEXTURE_2D); + + glMaterialfv (GL_FRONT, GL_SPECULAR, spec); + glMateriali (GL_FRONT, GL_SHININESS, shiny); + glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); + + glFrontFace (GL_CCW); + glBegin(wire ? GL_LINES : GL_QUAD_STRIP); + glNormal3f (0, 0, 1); + for (i = 0; i <= th_steps; i++) + { + double a = M_PI * 2 * i / th_steps; + double x = cos(a); + double y = sin(a); + glVertex3f (x, y, 0); + glVertex3f (x*10, y*10, 0); + polys++; + } + glEnd(); + + return polys; +} + + +static int +draw_angles (ModeInfo *mi) +{ + int i; + int polys = 0; + + static const GLfloat text[4] = {0.15, 0.15, 0.15, 1.0}; + static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0}; + + glMaterialfv (GL_FRONT, GL_SPECULAR, spec); + glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, text); + glTranslatef (0, 0, 0.01); + for (i = 0; i < 360; i += 10) + { + char buf[10]; + GLfloat a = M_PI/2 - (i / 180.0 * M_PI); + sprintf (buf, "%d", i); + polys += draw_text (mi, buf, 1.07, a, 0, 10.0); + } + + return polys; +} + + +static int +draw_bogies (ModeInfo *mi) +{ + sonar_configuration *sp = &sps[MI_SCREEN(mi)]; + int polys = 0; + sonar_bogie *b; + + for (b = sp->displayed; b; b = b->next) + { + char *s = (char *) + malloc (strlen (b->name) + (b->desc ? strlen(b->desc) : 0) + 3); + strcpy (s, b->name); + if (b->desc) + { + strcat (s, "\n"); + strcat (s, b->desc); + } + polys += draw_text (mi, s, b->r, b->th, b->ttl, -1); + free (s); + + /* Move *very slightly* forward so that the text is not all in the + same plane: this prevents flickering with overlapping text as + the textures fight for priority. */ + glTranslatef(0, 0, 0.00002); + } + + return polys; +} + + +/* called from sonar-sim.c and sonar-icmp.c */ +sonar_bogie * +sonar_copy_bogie (sonar_sensor_data *ssd, const sonar_bogie *b) +{ + sonar_bogie *b2 = (sonar_bogie *) calloc (1, sizeof(*b2)); + b2->name = strdup (b->name); + b2->desc = b->desc ? strdup (b->desc) : 0; + b2->r = b->r; + b2->th = b->th; + b2->ttl = b->ttl; + /* does not copy b->closure */ + + /* Take this opportunity to normalize 'th' to the range [0-2pi). */ + while (b2->th < 0) b2->th += M_PI*2; + while (b2->th >= M_PI*2) b2->th -= M_PI*2; + + return b2; +} + + +/* called from sonar-icmp.c */ +void +sonar_free_bogie (sonar_sensor_data *ssd, sonar_bogie *b) +{ + if (b->closure) + ssd->free_bogie_cb (ssd, b->closure); + free (b->name); + if (b->desc) free (b->desc); + free (b); +} + +/* removes it from the list and frees it + */ +static void +delete_bogie (sonar_sensor_data *ssd, sonar_bogie *b, + sonar_bogie **from_list) +{ + sonar_bogie *ob, *prev; + for (prev = 0, ob = *from_list; ob; prev = ob, ob = ob->next) + if (ob == b) + { + if (prev) + prev->next = b->next; + else + (*from_list) = b->next; + sonar_free_bogie (ssd, b); + break; + } +} + + +/* copies the bogie and adds it to the list. + if there's another bogie there with the same name, frees that one. + */ +static void +copy_and_insert_bogie (sonar_sensor_data *ssd, sonar_bogie *b, + sonar_bogie **to_list) +{ + sonar_bogie *ob, *next; + if (!b) abort(); + for (ob = *to_list, next = ob ? ob->next : 0; + ob; + ob = next, next = ob ? ob->next : 0) + { + if (ob == b) abort(); /* this will end badly */ + if (!strcmp (ob->name, b->name)) /* match! */ + { + delete_bogie (ssd, ob, to_list); + break; + } + } + + b = sonar_copy_bogie (ssd, b); + b->next = *to_list; + *to_list = b; +} + + +static void +update_sensor_data (sonar_configuration *sp) +{ + sonar_bogie *new_list = sp->ssd->scan_cb (sp->ssd); + sonar_bogie *b2; + + /* If a bogie exists in 'new_list' but not 'pending', add it. + If a bogie exists in both, update it in 'pending'. + */ + for (b2 = new_list; b2; b2 = b2->next) + { + if (debug_p > 2) + fprintf (stderr, "%s: updated: %s (%5.2f %5.2f %5.2f)\n", + progname, b2->name, b2->r, b2->th, b2->ttl); + copy_and_insert_bogie (sp->ssd, b2, &sp->pending); + } + if (debug_p > 2) fprintf (stderr, "\n"); +} + + +/* Returns whether the given angle lies between two other angles. + When those angles cross 0, it assumes the wedge is the smaller one. + That is: 5 lies between 10 and 350 degrees (a 20 degree wedge). + */ +static Bool +point_in_wedge (GLfloat th, GLfloat low, GLfloat high) +{ + if (low < high) + return (th > low && th <= high); + else + return (th <= high || th > low); +} + + +/* Returns the current time in seconds as a double. + */ +static double +double_time (void) +{ + struct timeval now; +# ifdef GETTIMEOFDAY_TWO_ARGS + struct timezone tzp; + gettimeofday(&now, &tzp); +# else + gettimeofday(&now); +# endif + + return (now.tv_sec + ((double) now.tv_usec * 0.000001)); +} + + +static void +sweep (sonar_configuration *sp) +{ + sonar_bogie *b; + + /* Move the sweep forward (clockwise). + */ + GLfloat prev_sweep, this_sweep, tick; + GLfloat cycle_secs = 30 / speed; /* default to one cycle every N seconds */ + this_sweep = ((cycle_secs - fmod (double_time() - sp->start_time + + sp->sweep_offset, + cycle_secs)) + / cycle_secs + * M_PI * 2); + prev_sweep = sp->sweep_th; + tick = prev_sweep - this_sweep; + while (tick < 0) tick += M_PI*2; + + sp->sweep_th = this_sweep; + + if (this_sweep < 0 || this_sweep >= M_PI*2) abort(); + if (prev_sweep < 0) /* skip first time */ + return; + + if (tick < 0 || tick >= M_PI*2) abort(); + + + /* Go through the 'pending' sensor data, find those bogies who are + just now being swept, and move them from 'pending' to 'displayed'. + (Leave bogies that have not yet been swept alone: we'll get to + them when the sweep moves forward.) + */ + b = sp->pending; + while (b) + { + sonar_bogie *next = b->next; + if (point_in_wedge (b->th, this_sweep, prev_sweep)) + { + if (debug_p > 1) { + time_t t = time((time_t *) 0); + fprintf (stderr, + "%s: sweep hit: %02d:%02d: %s: (%5.2f %5.2f %5.2f;" + " th=[%.2f < %.2f <= %.2f])\n", + progname, + (int) (t / 60) % 60, (int) t % 60, + b->name, b->r, b->th, b->ttl, + this_sweep, b->th, prev_sweep); + } + b->ttl = M_PI * 2.1; + copy_and_insert_bogie (sp->ssd, b, &sp->displayed); + delete_bogie (sp->ssd, b, &sp->pending); + } + b = next; + } + + + /* Update TTL on all currently-displayed bogies; delete the dead. + + Request sensor updates on the ones just now being swept. + + Any updates go into 'pending' and might not show up until + the next time the sweep comes around. This is to prevent + already-drawn bogies from jumping to a new position without + having faded out first. + */ + b = sp->displayed; + while (b) + { + sonar_bogie *next = b->next; + b->ttl -= tick; + + if (b->ttl <= 0) + { + if (debug_p > 1) + fprintf (stderr, "%s: TTL expired: %s (%5.2f %5.2f %5.2f)\n", + progname, b->name, b->r, b->th, b->ttl); + delete_bogie (sp->ssd, b, &sp->displayed); + } + b = next; + } + + update_sensor_data (sp); +} + + +static void +draw_startup_blurb (ModeInfo *mi) +{ + sonar_configuration *sp = &sps[MI_SCREEN(mi)]; + + if (sp->error) + { + const char *msg = sp->error; + static const GLfloat color[4] = {0, 1, 0, 1}; + + glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); + glTranslatef (0, 0, 0.3); + draw_text (mi, msg, 0, 0, 0, 30.0); + + /* only leave error message up for N seconds */ + if (sp->start_time + 6 < double_time()) + { + free (sp->error); + sp->error = 0; + } + } +} + + +/* Window management, etc + */ +ENTRYPOINT void +reshape_sonar (ModeInfo *mi, int width, int height) +{ + sonar_configuration *sp = &sps[MI_SCREEN(mi)]; + GLfloat h = (GLfloat) height / (GLfloat) width; + int y = 0; + + if (width > height * 5) { /* tiny window: show middle */ + height = width * 9/16; + y = -height/2; + h = height / (GLfloat) width; + } + + glViewport (0, y, (GLint) width, (GLint) height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective (30.0, 1/h, 1.0, 100.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt( 0.0, 0.0, 30.0, + 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0); + + glClear(GL_COLOR_BUFFER_BIT); + + sp->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0)); +} + + +ENTRYPOINT Bool +sonar_handle_event (ModeInfo *mi, XEvent *event) +{ + sonar_configuration *sp = &sps[MI_SCREEN(mi)]; + + if (gltrackball_event_handler (event, sp->trackball, + MI_WIDTH (mi), MI_HEIGHT (mi), + &sp->button_down_p)) + return True; + + return False; +} + +ENTRYPOINT void +init_sonar (ModeInfo *mi) +{ + sonar_configuration *sp; + + MI_INIT (mi, sps); + sp = &sps[MI_SCREEN(mi)]; + sp->glx_context = init_GL(mi); + + reshape_sonar (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */ + + sp->trackball = gltrackball_init (False); + sp->rot = make_rotator (0, 0, 0, 0, speed * 0.003, True); + + sp->texfont = load_texture_font (MI_DISPLAY(mi), "font"); + check_gl_error ("loading font"); + + sp->table_list = glGenLists (1); + glNewList (sp->table_list, GL_COMPILE); + sp->table_polys = draw_table (mi); + glEndList (); + + sp->screen_list = glGenLists (1); + glNewList (sp->screen_list, GL_COMPILE); + sp->screen_polys = draw_screen (mi, False, False); + glEndList (); + + sp->grid_list = glGenLists (1); + glNewList (sp->grid_list, GL_COMPILE); + sp->grid_polys = draw_screen (mi, True, False); + glEndList (); + + sp->sweep_list = glGenLists (1); + glNewList (sp->sweep_list, GL_COMPILE); + sp->sweep_polys = draw_screen (mi, False, True); + glEndList (); + + sp->start_time = double_time (); + sp->sweep_offset = random() % 60; + sp->sweep_th = -1; + sp->state = MSG; +} + + +# ifdef TEST_ASYNC_NETDB + +# include <arpa/inet.h> + +static void _print_sockaddr (void *addr, socklen_t addrlen, FILE *stream) +{ + sa_family_t family = ((struct sockaddr *)addr)->sa_family; + char buf[256]; + switch (family) + { + case AF_INET: + fputs (inet_ntoa(((struct sockaddr_in *)addr)->sin_addr), stream); + break; + case AF_INET6: + inet_ntop(family, &((struct sockaddr_in6 *)addr)->sin6_addr, + buf, sizeof (buf)); + fputs (buf, stream); + break; + default: + abort(); + break; + } +} + +static void _print_error (int gai_error, int errno_error, FILE *stream) +{ + fputs (gai_error == EAI_SYSTEM ? strerror(errno_error) : gai_strerror(gai_error), stream); +} + +# if ASYNC_NETDB_USE_GAI + +static void _print_thread (pthread_t thread, FILE *stream) +{ +# ifdef __linux__ + fprintf (stream, "%#lx", thread); +# elif defined __APPLE__ && defined __MACH__ + fprintf (stream, "%p", thread); +# else + putc ('?', stream); +# endif +} + +# endif /* ASYNC_NETDB_USE_GAI */ + +# endif /* TEST_ASYNC_NETDB */ + + +static void +init_sensor (ModeInfo *mi) +{ + sonar_configuration *sp = &sps[MI_SCREEN(mi)]; + + if (sp->ssd) abort(); + + if (!ping_arg || !*ping_arg || + !strcmp(ping_arg, "default") || + !!strcmp (ping_arg, "simulation")) + /* sonar_init_ping() always disavows privs, even on failure. */ + sp->ssd = sonar_init_ping (MI_DISPLAY (mi), &sp->error, &sp->desc, + ping_arg, ping_timeout, resolve_p, times_p, + debug_p); + else + setuid(getuid()); /* Disavow privs if not pinging. */ + + sp->start_time = double_time (); /* for error message timing */ + + if (!sp->ssd) + sp->ssd = sonar_init_simulation (MI_DISPLAY (mi), &sp->error, &sp->desc, + team_a_name, team_b_name, + team_a_count, team_b_count, + debug_p); + if (!sp->ssd) + abort(); + +# if TEST_ASYNC_NETDB + /* + For extremely mysterious reasons, setuid apparently causes + pthread_join(3) to deadlock. + A rough guess at the sequence of events: + 1. Worker thread is created. + 2. Worker thread exits. + 3. setuid(getuid()) is called. + 4. pthread_join is called slightly later. + + This may have something to do with glibc's use of SIGSETXID. + */ + + putc ('\n', stderr); + +# if !ASYNC_NETDB_USE_GAI + fputs ("Warning: getaddrinfo() was not available at compile time.\n", stderr); +# endif + + { + static const unsigned long addresses[] = + { + INADDR_LOOPBACK, + 0x00010203, + 0x08080808 + }; + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = htonl (addresses[random () % 3]); + + sp->query0 = async_name_from_addr_start (MI_DISPLAY (mi), (void *)&addr, + sizeof(addr)); + assert (sp->query0); + if (sp->query0) + { + fputs ("Looking up hostname from address: ", stderr); + _print_sockaddr (&addr, sizeof(addr), stderr); +# if ASYNC_NETDB_USE_GAI + fputs (" @ ", stderr); + _print_thread (sp->query0->io.thread, stderr); +# endif + putc ('\n', stderr); + } + + if (!(random () & 3)) + { + fputs ("Aborted hostname lookup (early)\n", stderr); + async_name_from_addr_cancel (sp->query0); + sp->query0 = NULL; + } + } + + { + static const char *const hosts[] = + { + "example.com", + "invalid", + "ip6-localhost" + }; + const char *host = hosts[random () % 3]; + + sp->query1 = async_addr_from_name_start (MI_DISPLAY(mi), host); + + assert (sp->query1); + + fprintf (stderr, "Looking up address from hostname: %s", host); +# if ASYNC_NETDB_USE_GAI + fputs (" @ ", stderr); + _print_thread (sp->query1->io.thread, stderr); +# endif + putc ('\n', stderr); + + if (!(random () & 3)) + { + fputs ("Aborted address lookup (early)\n", stderr); + async_addr_from_name_cancel (sp->query1); + sp->query1 = NULL; + } + } + + fflush (stderr); +# endif +} + + +ENTRYPOINT void +draw_sonar (ModeInfo *mi) +{ + sonar_configuration *sp = &sps[MI_SCREEN(mi)]; + Display *dpy = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + int wire = MI_IS_WIREFRAME(mi); + + if (!sp->glx_context) + return; + + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context)); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (!wire) + { + GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0}; + GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0}; + GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0}; + GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0}; + + glEnable(GL_TEXTURE_2D); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + glEnable(GL_NORMALIZE); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + glShadeModel(GL_SMOOTH); + + glLightfv(GL_LIGHT0, GL_POSITION, pos); + glLightfv(GL_LIGHT0, GL_AMBIENT, amb); + glLightfv(GL_LIGHT0, GL_DIFFUSE, dif); + glLightfv(GL_LIGHT0, GL_SPECULAR, spc); + } + + glPushMatrix (); + glRotatef(current_device_rotation(), 0, 0, 1); + + { + GLfloat s = 7; + if (MI_WIDTH(mi) < MI_HEIGHT(mi)) + s *= (MI_WIDTH(mi) / (float) MI_HEIGHT(mi)); + glScalef (s,s,s); + } + + gltrackball_rotate (sp->trackball); + + if (wobble_p) + { + double x, y, z; + double max = 40; + get_position (sp->rot, &x, &y, &z, !sp->button_down_p); + glRotatef (max/2 - x*max, 1, 0, 0); + glRotatef (max/2 - z*max, 0, 1, 0); + } + + mi->polygon_count = 0; + + glPushMatrix(); /* table */ + glCallList (sp->table_list); + mi->polygon_count += sp->table_polys; + glPopMatrix(); + + glPushMatrix(); /* text */ + glTranslatef (0, 0, -0.01); + mi->polygon_count += draw_bogies (mi); + glPopMatrix(); + + glCallList (sp->screen_list); /* glass */ + mi->polygon_count += sp->screen_polys; + + glTranslatef (0, 0, 0.004); /* sweep */ + glPushMatrix(); + glRotatef ((sp->sweep_th * 180 / M_PI), 0, 0, 1); + if (sp->sweep_th >= 0) + glCallList (sp->sweep_list); + mi->polygon_count += sp->sweep_polys; + glPopMatrix(); + + glTranslatef (0, 0, 0.004); /* grid */ + glCallList (sp->grid_list); + mi->polygon_count += sp->screen_polys; + + glPushMatrix(); + mi->polygon_count += draw_angles (mi); /* angles */ + glPopMatrix(); + + if (sp->desc) /* local subnet */ + { + glPushMatrix(); + glTranslatef (0, 0, 0.00002); + mi->polygon_count += draw_text (mi, sp->desc, 1.35, M_PI * 0.75, 0, 10); + /* glRotatef (45, 0, 0, 1); */ + /* mi->polygon_count += draw_text (mi, sp->desc, 1.2, M_PI/2, 0, 10); */ + glPopMatrix(); + } + + if (sp->error) + sp->state = MSG; + + switch (sp->state) { + case MSG: /* Frame 1: get "Resolving Hosts" on screen. */ + draw_startup_blurb(mi); + sp->state++; + break; + case RESOLVE: /* Frame 2: gethostbyaddr may take a while. */ + if (! sp->ssd) + init_sensor (mi); + sp->state++; + break; + case RUN: /* Frame N: ping away */ + sweep (sp); + break; + } + + glPopMatrix (); + + if (mi->fps_p) do_fps (mi); + glFinish(); + + glXSwapBuffers(dpy, window); + +# if TEST_ASYNC_NETDB + if(sp->query0 && async_name_from_addr_is_done (sp->query0)) + { + if (!(random () & 3)) + { + fputs ("Aborted hostname lookup (late)\n", stderr); + async_name_from_addr_cancel (sp->query0); + } + else + { + char *hostname = NULL; + int errno_error; + int gai_error = async_name_from_addr_finish (sp->query0, &hostname, + &errno_error); + + if(gai_error) + { + fputs ("Couldn't get hostname: ", stderr); + _print_error (gai_error, errno_error, stderr); + putc ('\n', stderr); + } + else + { + fprintf (stderr, "Got a hostname: %s\n", hostname); + free (hostname); + } + } + + sp->query0 = NULL; + } + + if(sp->query1 && async_addr_from_name_is_done (sp->query1)) + { + if (!(random () & 3)) + { + fputs ("Aborted address lookup (late)\n", stderr); + async_addr_from_name_cancel (sp->query1); + } + else + { + async_netdb_sockaddr_storage_t addr; + socklen_t addrlen; + int errno_error; + int gai_error = async_addr_from_name_finish (sp->query1, &addr, + &addrlen, &errno_error); + + if (gai_error) + { + fputs ("Couldn't get address: ", stderr); + _print_error (gai_error, errno_error, stderr); + putc ('\n', stderr); + } + else + { + fputs ("Got an address: ", stderr); + _print_sockaddr (&addr, addrlen, stderr); + putc ('\n', stderr); + } + } + + sp->query1 = NULL; + } + + fflush (stderr); +# endif /* TEST_ASYNC_NETDB */ +} + +ENTRYPOINT void +free_sonar (ModeInfo *mi) +{ +#if 0 + sonar_configuration *sp = &sps[MI_SCREEN(mi)]; + sonar_bogie *b = sp->displayed; + while (b) + { + sonar_bogie *next = b->next; + free_bogie (sp->ssd, b); + b = next; + } + sp->displayed = 0; + + b = sp->pending; + while (b) + { + sonar_bogie *next = b->next; + free_bogie (sp->ssd, b); + b = next; + } + sp->pending = 0; + + sp->ssd->free_data_cb (sp->ssd, sp->ssd->closure); + free (sp->ssd); + sp->ssd = 0; +#endif +} + +XSCREENSAVER_MODULE ("Sonar", sonar) + +#endif /* USE_GL */ |