1 /* sonar, Copyright (c) 1998-2015 Jamie Zawinski and Stephen Martin
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
12 /* Created in Apr 1998 by Stephen Martin <smartin@vanderfleet-martin.net>
13 * for the RedHat Screensaver Contest
14 * Heavily hacked by jwz ever since.
15 * Rewritten in OpenGL by jwz, Aug 2008.
17 * This is an implementation of a general purpose reporting tool in the
18 * format of a Sonar display. It is designed such that a sensor is read
19 * on every movement of a sweep arm and the results of that sensor are
20 * displayed on the screen. The location of the display points (targets) on the
21 * screen are determined by the current localtion of the sweep and a distance
22 * value associated with the target.
24 * Currently the only two sensors that are implemented are the simulator
25 * (the default) and the ping sensor. The simulator randomly creates a set
26 * of bogies that move around on the scope while the ping sensor can be
27 * used to display hosts on your network.
29 * The ping code is only compiled in if you define HAVE_ICMP or HAVE_ICMPHDR,
30 * because, unfortunately, different systems have different ways of creating
31 * these sorts of packets.
33 * In order to use the ping sensor on most systems, this program must be
34 * installed as setuid root, so that it can create an ICMP RAW socket. Root
35 * privileges are disavowed shortly after startup (just after connecting to
36 * the X server and reading the resource database) so this is *believed* to
37 * be a safe thing to do, but it is usually recommended that you have as few
38 * setuid programs around as possible, on general principles.
40 * It is not necessary to make it setuid on MacOS systems, because on those
41 * systems, unprivileged programs can ping by using ICMP DGRAM sockets
42 * instead of ICMP RAW.
44 * It should be easy to extend this code to support other sorts of sensors.
47 * - search the output of "netstat" for the list of hosts to ping;
48 * - plot the contents of /proc/interrupts;
49 * - plot the process table, by process size, cpu usage, or total time;
50 * - plot the logged on users by idle time or cpu usage.
51 * - plot IM contacts or Facebook friends and their last-activity times.
54 #define DEF_FONT "-*-courier-bold-r-normal-*-*-480-*-*-*-*-iso8859-1"
55 #define DEF_SPEED "1.0"
56 #define DEF_SWEEP_SIZE "0.3"
57 #define DEF_FONT_SIZE "12"
58 #define DEF_TEAM_A_NAME "F18"
59 #define DEF_TEAM_B_NAME "MIG"
60 #define DEF_TEAM_A_COUNT "4"
61 #define DEF_TEAM_B_COUNT "4"
62 #define DEF_PING "default"
63 #define DEF_PING_TIMEOUT "3000"
64 #define DEF_RESOLVE "True"
65 #define DEF_TIMES "True"
66 #define DEF_WOBBLE "True"
67 #define DEF_DEBUG "False"
69 #include "thread_util.h"
71 #define DEFAULTS "*delay: 30000 \n" \
72 "*font: " DEF_FONT "\n" \
73 "*showFPS: False \n" \
74 "*wireframe: False \n" \
75 "*texFontCacheSize: 300 \n" \
79 # define refresh_sonar 0
80 # define release_sonar 0
82 #define countof(x) (sizeof((x))/sizeof((*x)))
85 # include <unistd.h> /* for setuid() */
88 #include "xlockmore.h"
90 #include "gltrackball.h"
95 #ifdef USE_GL /* whole file */
97 /* #define TEST_ASYNC_NETDB 1 */
100 # include "async_netdb.h"
103 # include <netinet/in.h>
105 # endif /* TEST_ASYNC_NETDB */
112 GLXContext *glx_context;
113 trackball_state *trackball;
118 GLfloat sweep_offset;
120 GLuint screen_list, grid_list, sweep_list, table_list;
121 int screen_polys, grid_polys, sweep_polys, table_polys;
123 GLfloat line_thickness;
125 texture_font_data *texfont;
127 enum { MSG, RESOLVE, RUN } state;
128 sonar_sensor_data *ssd;
132 sonar_bogie *displayed; /* on screen and fading */
133 sonar_bogie *pending; /* returned by sensor, not yet on screen */
135 # if TEST_ASYNC_NETDB
136 async_name_from_addr_t query0;
137 async_addr_from_name_t query1;
139 } sonar_configuration;
141 static sonar_configuration *sps = NULL;
143 static GLfloat speed;
144 static GLfloat sweep_size;
145 static GLfloat font_size;
146 static Bool resolve_p;
148 static Bool wobble_p;
151 static char *team_a_name;
152 static char *team_b_name;
153 static int team_a_count;
154 static int team_b_count;
155 static int ping_timeout;
156 static char *ping_arg;
158 static XrmOptionDescRec opts[] = {
159 { "-speed", ".speed", XrmoptionSepArg, 0 },
160 { "-sweep-size", ".sweepSize", XrmoptionSepArg, 0 },
161 { "-font-size", ".fontSize", XrmoptionSepArg, 0 },
162 { "-team-a-name", ".teamAName", XrmoptionSepArg, 0 },
163 { "-team-b-name", ".teamBName", XrmoptionSepArg, 0 },
164 { "-team-a-count", ".teamACount", XrmoptionSepArg, 0 },
165 { "-team-b-count", ".teamBCount", XrmoptionSepArg, 0 },
166 { "-ping", ".ping", XrmoptionSepArg, 0 },
167 { "-ping-timeout", ".pingTimeout", XrmoptionSepArg, 0 },
168 { "-dns", ".resolve", XrmoptionNoArg, "True" },
169 { "+dns", ".resolve", XrmoptionNoArg, "False" },
170 { "-times", ".times", XrmoptionNoArg, "True" },
171 { "+times", ".times", XrmoptionNoArg, "False" },
172 { "-wobble", ".wobble", XrmoptionNoArg, "True" },
173 { "+wobble", ".wobble", XrmoptionNoArg, "False" },
175 { "-debug", ".debug", XrmoptionNoArg, "True" },
178 static argtype vars[] = {
179 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
180 {&sweep_size, "sweepSize", "SweepSize", DEF_SWEEP_SIZE, t_Float},
181 {&font_size, "fontSize", "FontSize", DEF_FONT_SIZE, t_Float},
182 {&team_a_name, "teamAName", "TeamName", DEF_TEAM_A_NAME, t_String},
183 {&team_b_name, "teamBName", "TeamName", DEF_TEAM_B_NAME, t_String},
184 {&team_a_count, "teamACount", "TeamCount", DEF_TEAM_A_COUNT, t_Int},
185 {&team_b_count, "teamBCount", "TeamCount", DEF_TEAM_A_COUNT, t_Int},
186 {&ping_arg, "ping", "Ping", DEF_PING, t_String},
187 {&ping_timeout, "pingTimeout", "PingTimeout", DEF_PING_TIMEOUT, t_Int},
188 {&resolve_p, "resolve", "Resolve", DEF_RESOLVE, t_Bool},
189 {×_p, "times", "Times", DEF_TIMES, t_Bool},
190 {&wobble_p, "wobble", "Wobble", DEF_WOBBLE, t_Bool},
191 {&debug_p, "debug", "Debug", DEF_DEBUG, t_Bool},
194 ENTRYPOINT ModeSpecOpt sonar_opts = {countof(opts), opts, countof(vars), vars, NULL};
198 draw_screen (ModeInfo *mi, Bool mesh_p, Bool sweep_p)
200 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
201 int wire = MI_IS_WIREFRAME(mi);
204 int th_steps, r_steps, r_skip, th_skip, th_skip2, outer_r;
205 GLfloat curvature = M_PI * 0.4;
206 GLfloat r0, r1, z0, z1, zoff;
209 static const GLfloat glass[4] = {0.0, 0.4, 0.0, 0.5};
210 static const GLfloat lines[4] = {0.0, 0.7, 0.0, 0.5};
211 static const GLfloat sweepc[4] = {0.2, 1.0, 0.2, 0.5};
212 static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
213 static const GLfloat shiny = 20.0;
215 if (wire && !(mesh_p || sweep_p)) return 0;
217 glDisable (GL_TEXTURE_2D);
219 glFrontFace (GL_CCW);
220 th_steps = 36 * 4; /* must be a multiple of th_skip2 divisor */
227 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
228 glMateriali (GL_FRONT, GL_SHININESS, shiny);
229 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mesh_p ? lines : glass);
230 if (wire) glColor3fv (lines);
234 th_skip = th_steps / 12;
235 th_skip2 = th_steps / 36;
236 r_skip = r_steps / 3;
237 outer_r = r_steps * 0.93;
240 glLineWidth (sp->line_thickness);
243 ring = (XYZ *) calloc (th_steps, sizeof(*ring));
245 for (i = 0; i < th_steps; i++)
247 double a = M_PI * 2 * i / th_steps;
252 /* place the bottom of the disc on the xy plane. */
253 zoff = cos (curvature/2 * (M_PI/2)) / 2;
255 for (i = r_steps; i > 0; i--)
259 r0 = i / (GLfloat) r_steps;
260 r1 = (i+1) / (GLfloat) r_steps;
262 if (r1 > 1) r1 = 1; /* avoid asin lossage */
264 z0 = cos (curvature/2 * asin (r0)) / 2 - zoff;
265 z1 = cos (curvature/2 * asin (r1)) / 2 - zoff;
267 glBegin(wire || mesh_p ? GL_LINES : GL_QUAD_STRIP);
268 for (j0 = 0; j0 <= th_steps; j0++)
272 ? (j0 % th_skip != 0)
273 : (j0 % th_skip2 != 0)))
279 GLfloat r = 1 - (j0 / (GLfloat) (th_steps * sweep_size));
281 color[0] = glass[0] + (sweepc[0] - glass[0]) * r;
282 color[1] = glass[1] + (sweepc[1] - glass[1]) * r;
283 color[2] = glass[2] + (sweepc[2] - glass[2]) * r;
286 color[0] = sweepc[0];
287 color[1] = sweepc[1];
288 color[2] = sweepc[2];
291 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
295 glNormal3f (r0 * ring[j1].x, r0 * ring[j1].y, z0);
296 glVertex3f (r0 * ring[j1].x, r0 * ring[j1].y, z0);
297 glNormal3f (r1 * ring[j1].x, r1 * ring[j1].y, z1);
298 glVertex3f (r1 * ring[j1].x, r1 * ring[j1].y, z1);
301 if (sweep_p && j0 >= th_steps * sweep_size)
312 i < r_steps - r_skip)))
314 glBegin(GL_LINE_LOOP);
315 for (j0 = 0; j0 < th_steps; j0++)
317 glNormal3f (r0 * ring[j0].x, r0 * ring[j0].y, z0);
318 glVertex3f (r0 * ring[j0].x, r0 * ring[j0].y, z0);
325 /* one more polygon for the middle */
326 if (!wire && !sweep_p)
328 glBegin(wire || mesh_p ? GL_LINE_LOOP : GL_POLYGON);
329 glNormal3f (0, 0, 1);
330 for (i = 0; i < th_steps; i++)
332 glNormal3f (r0 * ring[i].x, r0 * ring[i].y, z0);
333 glVertex3f (r0 * ring[i].x, r0 * ring[i].y, z0);
346 draw_text (ModeInfo *mi, const char *string, GLfloat r, GLfloat th,
347 GLfloat ttl, GLfloat size)
349 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
350 int wire = MI_IS_WIREFRAME(mi);
352 GLfloat font_scale = 0.001 * (size > 0 ? size : font_size) / 14.0;
353 int lines = 0, max_w = 0, lh = 0;
354 char *string2 = strdup (string);
355 char *token = string2;
358 if (size <= 0) /* if size not specified, draw in yellow with alpha */
364 color[3] = (ttl / (M_PI * 2)) * 1.2;
365 if (color[3] > 1) color[3] = 1;
367 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
369 glColor3f (color[0]*color[3], color[1]*color[3], color[2]*color[3]);
372 while ((line = strtok (token, "\r\n")))
375 int w, ascent, descent;
376 texture_string_metrics (sp->texfont, line, &e, &ascent, &descent);
378 lh = ascent + descent;
379 if (w > max_w) max_w = w;
385 glTranslatef (r * cos (th), r * sin(th), 0);
386 glScalef (font_scale, font_scale, font_scale);
388 if (size <= 0) /* Draw the dot */
390 GLfloat s = font_size * 1.7;
391 glDisable (GL_TEXTURE_2D);
393 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
394 glVertex3f (0, s, 0);
395 glVertex3f (s, s, 0);
396 glVertex3f (s, 0, 0);
397 glVertex3f (0, 0, 0);
399 glTranslatef (-max_w/2, -lh, 0);
402 glTranslatef (-max_w/2, -lh/2, 0);
404 /* draw each line, centered */
405 if (! wire) glEnable (GL_TEXTURE_2D);
407 string2 = strdup (string);
409 while ((line = strtok (token, "\r\n")))
413 texture_string_metrics (sp->texfont, line, &e, 0, 0);
416 glTranslatef ((max_w-w)/2, 0, polys * 4); /* 'polys' stops Z-fighting. */
420 glBegin (GL_LINE_LOOP);
421 glVertex3f (0, 0, 0);
422 glVertex3f (w, 0, 0);
423 glVertex3f (w, lh, 0);
424 glVertex3f (0, lh, 0);
430 print_texture_string (sp->texfont, line);
433 glTranslatef (0, -lh, 0);
441 if (! wire) glEnable (GL_DEPTH_TEST);
447 /* There's a disc with a hole in it around the screen, to act as a mask
448 preventing slightly off-screen bogies from showing up. This clips 'em.
451 draw_table (ModeInfo *mi)
453 /*sonar_configuration *sp = &sps[MI_SCREEN(mi)];*/
454 int wire = MI_IS_WIREFRAME(mi);
457 int th_steps = 36 * 4; /* same as in draw_screen */
459 static const GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
460 static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0};
461 static const GLfloat shiny = 0.0;
465 glDisable (GL_TEXTURE_2D);
467 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
468 glMateriali (GL_FRONT, GL_SHININESS, shiny);
469 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
471 glFrontFace (GL_CCW);
472 glBegin(wire ? GL_LINES : GL_QUAD_STRIP);
473 glNormal3f (0, 0, 1);
474 for (i = 0; i <= th_steps; i++)
476 double a = M_PI * 2 * i / th_steps;
479 glVertex3f (x, y, 0);
480 glVertex3f (x*10, y*10, 0);
490 draw_angles (ModeInfo *mi)
495 static const GLfloat text[4] = {0.15, 0.15, 0.15, 1.0};
496 static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0};
498 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
499 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, text);
500 glTranslatef (0, 0, 0.01);
501 for (i = 0; i < 360; i += 10)
504 GLfloat a = M_PI/2 - (i / 180.0 * M_PI);
505 sprintf (buf, "%d", i);
506 polys += draw_text (mi, buf, 1.07, a, 0, 10.0);
514 draw_bogies (ModeInfo *mi)
516 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
520 for (b = sp->displayed; b; b = b->next)
523 malloc (strlen (b->name) + (b->desc ? strlen(b->desc) : 0) + 3);
530 polys += draw_text (mi, s, b->r, b->th, b->ttl, -1);
533 /* Move *very slightly* forward so that the text is not all in the
534 same plane: this prevents flickering with overlapping text as
535 the textures fight for priority. */
536 glTranslatef(0, 0, 0.00002);
543 /* called from sonar-sim.c and sonar-icmp.c */
545 sonar_copy_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
547 sonar_bogie *b2 = (sonar_bogie *) calloc (1, sizeof(*b2));
548 b2->name = strdup (b->name);
549 b2->desc = b->desc ? strdup (b->desc) : 0;
553 /* does not copy b->closure */
555 /* Take this opportunity to normalize 'th' to the range [0-2pi). */
556 while (b2->th < 0) b2->th += M_PI*2;
557 while (b2->th >= M_PI*2) b2->th -= M_PI*2;
563 /* called from sonar-icmp.c */
565 sonar_free_bogie (sonar_sensor_data *ssd, sonar_bogie *b)
568 ssd->free_bogie_cb (ssd, b->closure);
570 if (b->desc) free (b->desc);
574 /* removes it from the list and frees it
577 delete_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
578 sonar_bogie **from_list)
580 sonar_bogie *ob, *prev;
581 for (prev = 0, ob = *from_list; ob; prev = ob, ob = ob->next)
585 prev->next = b->next;
587 (*from_list) = b->next;
588 sonar_free_bogie (ssd, b);
594 /* copies the bogie and adds it to the list.
595 if there's another bogie there with the same name, frees that one.
598 copy_and_insert_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
599 sonar_bogie **to_list)
601 sonar_bogie *ob, *next;
603 for (ob = *to_list, next = ob ? ob->next : 0;
605 ob = next, next = ob ? ob->next : 0)
607 if (ob == b) abort(); /* this will end badly */
608 if (!strcmp (ob->name, b->name)) /* match! */
610 delete_bogie (ssd, ob, to_list);
615 b = sonar_copy_bogie (ssd, b);
622 update_sensor_data (sonar_configuration *sp)
624 sonar_bogie *new_list = sp->ssd->scan_cb (sp->ssd);
627 /* If a bogie exists in 'new_list' but not 'pending', add it.
628 If a bogie exists in both, update it in 'pending'.
630 for (b2 = new_list; b2; b2 = b2->next)
633 fprintf (stderr, "%s: updated: %s (%5.2f %5.2f %5.2f)\n",
634 progname, b2->name, b2->r, b2->th, b2->ttl);
635 copy_and_insert_bogie (sp->ssd, b2, &sp->pending);
637 if (debug_p > 2) fprintf (stderr, "\n");
641 /* Returns whether the given angle lies between two other angles.
642 When those angles cross 0, it assumes the wedge is the smaller one.
643 That is: 5 lies between 10 and 350 degrees (a 20 degree wedge).
646 point_in_wedge (GLfloat th, GLfloat low, GLfloat high)
649 return (th > low && th <= high);
651 return (th <= high || th > low);
655 /* Returns the current time in seconds as a double.
661 # ifdef GETTIMEOFDAY_TWO_ARGS
663 gettimeofday(&now, &tzp);
668 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
673 sweep (sonar_configuration *sp)
677 /* Move the sweep forward (clockwise).
679 GLfloat prev_sweep, this_sweep, tick;
680 GLfloat cycle_secs = 30 / speed; /* default to one cycle every N seconds */
681 this_sweep = ((cycle_secs - fmod (double_time() - sp->start_time +
686 prev_sweep = sp->sweep_th;
687 tick = prev_sweep - this_sweep;
688 while (tick < 0) tick += M_PI*2;
690 sp->sweep_th = this_sweep;
692 if (this_sweep < 0 || this_sweep >= M_PI*2) abort();
693 if (prev_sweep < 0) /* skip first time */
696 if (tick < 0 || tick >= M_PI*2) abort();
699 /* Go through the 'pending' sensor data, find those bogies who are
700 just now being swept, and move them from 'pending' to 'displayed'.
701 (Leave bogies that have not yet been swept alone: we'll get to
702 them when the sweep moves forward.)
707 sonar_bogie *next = b->next;
708 if (point_in_wedge (b->th, this_sweep, prev_sweep))
711 time_t t = time((time_t *) 0);
713 "%s: sweep hit: %02d:%02d: %s: (%5.2f %5.2f %5.2f;"
714 " th=[%.2f < %.2f <= %.2f])\n",
716 (int) (t / 60) % 60, (int) t % 60,
717 b->name, b->r, b->th, b->ttl,
718 this_sweep, b->th, prev_sweep);
721 copy_and_insert_bogie (sp->ssd, b, &sp->displayed);
722 delete_bogie (sp->ssd, b, &sp->pending);
728 /* Update TTL on all currently-displayed bogies; delete the dead.
730 Request sensor updates on the ones just now being swept.
732 Any updates go into 'pending' and might not show up until
733 the next time the sweep comes around. This is to prevent
734 already-drawn bogies from jumping to a new position without
735 having faded out first.
740 sonar_bogie *next = b->next;
746 fprintf (stderr, "%s: TTL expired: %s (%5.2f %5.2f %5.2f)\n",
747 progname, b->name, b->r, b->th, b->ttl);
748 delete_bogie (sp->ssd, b, &sp->displayed);
753 update_sensor_data (sp);
758 draw_startup_blurb (ModeInfo *mi)
760 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
764 const char *msg = sp->error;
765 static const GLfloat color[4] = {0, 1, 0, 1};
767 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
768 glTranslatef (0, 0, 0.3);
769 draw_text (mi, msg, 0, 0, 0, 30.0);
771 /* only leave error message up for N seconds */
772 if (sp->start_time + 6 < double_time())
781 /* Window management, etc
784 reshape_sonar (ModeInfo *mi, int width, int height)
786 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
787 GLfloat h = (GLfloat) height / (GLfloat) width;
789 glViewport (0, 0, (GLint) width, (GLint) height);
791 glMatrixMode(GL_PROJECTION);
793 gluPerspective (30.0, 1/h, 1.0, 100.0);
795 glMatrixMode(GL_MODELVIEW);
797 gluLookAt( 0.0, 0.0, 30.0,
801 glClear(GL_COLOR_BUFFER_BIT);
803 sp->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
808 sonar_handle_event (ModeInfo *mi, XEvent *event)
810 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
812 if (gltrackball_event_handler (event, sp->trackball,
813 MI_WIDTH (mi), MI_HEIGHT (mi),
820 static void free_sonar (ModeInfo *mi);
823 init_sonar (ModeInfo *mi)
825 sonar_configuration *sp;
827 MI_INIT (mi, sps, free_sonar);
828 sp = &sps[MI_SCREEN(mi)];
829 sp->glx_context = init_GL(mi);
831 reshape_sonar (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
832 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
834 sp->trackball = gltrackball_init (False);
835 sp->rot = make_rotator (0, 0, 0, 0, speed * 0.003, True);
837 sp->texfont = load_texture_font (MI_DISPLAY(mi), "font");
838 check_gl_error ("loading font");
840 sp->table_list = glGenLists (1);
841 glNewList (sp->table_list, GL_COMPILE);
842 sp->table_polys = draw_table (mi);
845 sp->screen_list = glGenLists (1);
846 glNewList (sp->screen_list, GL_COMPILE);
847 sp->screen_polys = draw_screen (mi, False, False);
850 sp->grid_list = glGenLists (1);
851 glNewList (sp->grid_list, GL_COMPILE);
852 sp->grid_polys = draw_screen (mi, True, False);
855 sp->sweep_list = glGenLists (1);
856 glNewList (sp->sweep_list, GL_COMPILE);
857 sp->sweep_polys = draw_screen (mi, False, True);
860 sp->start_time = double_time ();
861 sp->sweep_offset = random() % 60;
867 # ifdef TEST_ASYNC_NETDB
869 # include <arpa/inet.h>
871 static void _print_sockaddr (void *addr, socklen_t addrlen, FILE *stream)
873 sa_family_t family = ((struct sockaddr *)addr)->sa_family;
878 fputs (inet_ntoa(((struct sockaddr_in *)addr)->sin_addr), stream);
881 inet_ntop(family, &((struct sockaddr_in6 *)addr)->sin6_addr,
891 static void _print_error (int gai_error, int errno_error, FILE *stream)
893 fputs (gai_error == EAI_SYSTEM ? strerror(errno_error) : gai_strerror(gai_error), stream);
896 # if ASYNC_NETDB_USE_GAI
898 static void _print_thread (pthread_t thread, FILE *stream)
901 fprintf (stream, "%#lx", thread);
902 # elif defined __APPLE__ && defined __MACH__
903 fprintf (stream, "%p", thread);
909 # endif /* ASYNC_NETDB_USE_GAI */
911 # endif /* TEST_ASYNC_NETDB */
915 init_sensor (ModeInfo *mi)
917 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
919 if (sp->ssd) abort();
921 if (!ping_arg || !*ping_arg ||
922 !strcmp(ping_arg, "default") ||
923 !!strcmp (ping_arg, "simulation"))
924 /* sonar_init_ping() always disavows privs, even on failure. */
925 sp->ssd = sonar_init_ping (MI_DISPLAY (mi), &sp->error, &sp->desc,
926 ping_arg, ping_timeout, resolve_p, times_p,
929 setuid(getuid()); /* Disavow privs if not pinging. */
931 sp->start_time = double_time (); /* for error message timing */
934 sp->ssd = sonar_init_simulation (MI_DISPLAY (mi), &sp->error, &sp->desc,
935 team_a_name, team_b_name,
936 team_a_count, team_b_count,
941 # if TEST_ASYNC_NETDB
943 For extremely mysterious reasons, setuid apparently causes
944 pthread_join(3) to deadlock.
945 A rough guess at the sequence of events:
946 1. Worker thread is created.
947 2. Worker thread exits.
948 3. setuid(getuid()) is called.
949 4. pthread_join is called slightly later.
951 This may have something to do with glibc's use of SIGSETXID.
956 # if !ASYNC_NETDB_USE_GAI
957 fputs ("Warning: getaddrinfo() was not available at compile time.\n", stderr);
961 static const unsigned long addresses[] =
967 struct sockaddr_in addr;
968 addr.sin_family = AF_INET;
970 addr.sin_addr.s_addr = htonl (addresses[random () % 3]);
972 sp->query0 = async_name_from_addr_start (MI_DISPLAY (mi), (void *)&addr,
977 fputs ("Looking up hostname from address: ", stderr);
978 _print_sockaddr (&addr, sizeof(addr), stderr);
979 # if ASYNC_NETDB_USE_GAI
980 fputs (" @ ", stderr);
981 _print_thread (sp->query0->io.thread, stderr);
986 if (!(random () & 3))
988 fputs ("Aborted hostname lookup (early)\n", stderr);
989 async_name_from_addr_cancel (sp->query0);
995 static const char *const hosts[] =
1001 const char *host = hosts[random () % 3];
1003 sp->query1 = async_addr_from_name_start (MI_DISPLAY(mi), host);
1005 assert (sp->query1);
1007 fprintf (stderr, "Looking up address from hostname: %s", host);
1008 # if ASYNC_NETDB_USE_GAI
1009 fputs (" @ ", stderr);
1010 _print_thread (sp->query1->io.thread, stderr);
1012 putc ('\n', stderr);
1014 if (!(random () & 3))
1016 fputs ("Aborted address lookup (early)\n", stderr);
1017 async_addr_from_name_cancel (sp->query1);
1028 draw_sonar (ModeInfo *mi)
1030 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
1031 Display *dpy = MI_DISPLAY(mi);
1032 Window window = MI_WINDOW(mi);
1033 int wire = MI_IS_WIREFRAME(mi);
1035 if (!sp->glx_context)
1038 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
1040 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1044 GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
1045 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
1046 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
1047 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
1049 glEnable(GL_TEXTURE_2D);
1050 glEnable(GL_LIGHTING);
1051 glEnable(GL_LIGHT0);
1052 glEnable(GL_CULL_FACE);
1053 glEnable(GL_DEPTH_TEST);
1054 glEnable(GL_NORMALIZE);
1055 glEnable(GL_LINE_SMOOTH);
1057 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1058 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
1059 glShadeModel(GL_SMOOTH);
1061 glLightfv(GL_LIGHT0, GL_POSITION, pos);
1062 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
1063 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
1064 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
1068 glRotatef(current_device_rotation(), 0, 0, 1);
1072 if (MI_WIDTH(mi) < MI_HEIGHT(mi))
1073 s *= (MI_WIDTH(mi) / (float) MI_HEIGHT(mi));
1077 gltrackball_rotate (sp->trackball);
1083 get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
1084 glRotatef (max/2 - x*max, 1, 0, 0);
1085 glRotatef (max/2 - z*max, 0, 1, 0);
1088 mi->polygon_count = 0;
1090 glPushMatrix(); /* table */
1091 glCallList (sp->table_list);
1092 mi->polygon_count += sp->table_polys;
1095 glPushMatrix(); /* text */
1096 glTranslatef (0, 0, -0.01);
1097 mi->polygon_count += draw_bogies (mi);
1100 glCallList (sp->screen_list); /* glass */
1101 mi->polygon_count += sp->screen_polys;
1103 glTranslatef (0, 0, 0.004); /* sweep */
1105 glRotatef ((sp->sweep_th * 180 / M_PI), 0, 0, 1);
1106 if (sp->sweep_th >= 0)
1107 glCallList (sp->sweep_list);
1108 mi->polygon_count += sp->sweep_polys;
1111 glTranslatef (0, 0, 0.004); /* grid */
1112 glCallList (sp->grid_list);
1113 mi->polygon_count += sp->screen_polys;
1116 mi->polygon_count += draw_angles (mi); /* angles */
1119 if (sp->desc) /* local subnet */
1122 glTranslatef (0, 0, 0.00002);
1123 mi->polygon_count += draw_text (mi, sp->desc, 1.35, M_PI * 0.75, 0, 10);
1124 /* glRotatef (45, 0, 0, 1); */
1125 /* mi->polygon_count += draw_text (mi, sp->desc, 1.2, M_PI/2, 0, 10); */
1132 switch (sp->state) {
1133 case MSG: /* Frame 1: get "Resolving Hosts" on screen. */
1134 draw_startup_blurb(mi);
1137 case RESOLVE: /* Frame 2: gethostbyaddr may take a while. */
1142 case RUN: /* Frame N: ping away */
1149 if (mi->fps_p) do_fps (mi);
1152 glXSwapBuffers(dpy, window);
1154 # if TEST_ASYNC_NETDB
1155 if(sp->query0 && async_name_from_addr_is_done (sp->query0))
1157 if (!(random () & 3))
1159 fputs ("Aborted hostname lookup (late)\n", stderr);
1160 async_name_from_addr_cancel (sp->query0);
1164 char *hostname = NULL;
1166 int gai_error = async_name_from_addr_finish (sp->query0, &hostname,
1171 fputs ("Couldn't get hostname: ", stderr);
1172 _print_error (gai_error, errno_error, stderr);
1173 putc ('\n', stderr);
1177 fprintf (stderr, "Got a hostname: %s\n", hostname);
1185 if(sp->query1 && async_addr_from_name_is_done (sp->query1))
1187 if (!(random () & 3))
1189 fputs ("Aborted address lookup (late)\n", stderr);
1190 async_addr_from_name_cancel (sp->query1);
1194 async_netdb_sockaddr_storage_t addr;
1197 int gai_error = async_addr_from_name_finish (sp->query1, &addr,
1198 &addrlen, &errno_error);
1202 fputs ("Couldn't get address: ", stderr);
1203 _print_error (gai_error, errno_error, stderr);
1204 putc ('\n', stderr);
1208 fputs ("Got an address: ", stderr);
1209 _print_sockaddr (&addr, addrlen, stderr);
1210 putc ('\n', stderr);
1218 # endif /* TEST_ASYNC_NETDB */
1222 free_sonar (ModeInfo *mi)
1225 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
1226 sonar_bogie *b = sp->displayed;
1229 sonar_bogie *next = b->next;
1230 free_bogie (sp->ssd, b);
1238 sonar_bogie *next = b->next;
1239 free_bogie (sp->ssd, b);
1244 sp->ssd->free_data_cb (sp->ssd, sp->ssd->closure);
1250 XSCREENSAVER_MODULE ("Sonar", sonar)