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
81 #define countof(x) (sizeof((x))/sizeof((*x)))
84 # include <unistd.h> /* for setuid() */
87 #include "xlockmore.h"
89 #include "gltrackball.h"
94 #ifdef USE_GL /* whole file */
96 /* #define TEST_ASYNC_NETDB 1 */
99 # include "async_netdb.h"
102 # include <netinet/in.h>
104 # endif /* TEST_ASYNC_NETDB */
111 GLXContext *glx_context;
112 trackball_state *trackball;
117 GLfloat sweep_offset;
119 GLuint screen_list, grid_list, sweep_list, table_list;
120 int screen_polys, grid_polys, sweep_polys, table_polys;
122 GLfloat line_thickness;
124 texture_font_data *texfont;
126 enum { MSG, RESOLVE, RUN } state;
127 sonar_sensor_data *ssd;
131 sonar_bogie *displayed; /* on screen and fading */
132 sonar_bogie *pending; /* returned by sensor, not yet on screen */
134 # if TEST_ASYNC_NETDB
135 async_name_from_addr_t query0;
136 async_addr_from_name_t query1;
138 } sonar_configuration;
140 static sonar_configuration *sps = NULL;
142 static GLfloat speed;
143 static GLfloat sweep_size;
144 static GLfloat font_size;
145 static Bool resolve_p;
147 static Bool wobble_p;
150 static char *team_a_name;
151 static char *team_b_name;
152 static int team_a_count;
153 static int team_b_count;
154 static int ping_timeout;
155 static char *ping_arg;
157 static XrmOptionDescRec opts[] = {
158 { "-speed", ".speed", XrmoptionSepArg, 0 },
159 { "-sweep-size", ".sweepSize", XrmoptionSepArg, 0 },
160 { "-font-size", ".fontSize", XrmoptionSepArg, 0 },
161 { "-team-a-name", ".teamAName", XrmoptionSepArg, 0 },
162 { "-team-b-name", ".teamBName", XrmoptionSepArg, 0 },
163 { "-team-a-count", ".teamACount", XrmoptionSepArg, 0 },
164 { "-team-b-count", ".teamBCount", XrmoptionSepArg, 0 },
165 { "-ping", ".ping", XrmoptionSepArg, 0 },
166 { "-ping-timeout", ".pingTimeout", XrmoptionSepArg, 0 },
167 { "-dns", ".resolve", XrmoptionNoArg, "True" },
168 { "+dns", ".resolve", XrmoptionNoArg, "False" },
169 { "-times", ".times", XrmoptionNoArg, "True" },
170 { "+times", ".times", XrmoptionNoArg, "False" },
171 { "-wobble", ".wobble", XrmoptionNoArg, "True" },
172 { "+wobble", ".wobble", XrmoptionNoArg, "False" },
174 { "-debug", ".debug", XrmoptionNoArg, "True" },
177 static argtype vars[] = {
178 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
179 {&sweep_size, "sweepSize", "SweepSize", DEF_SWEEP_SIZE, t_Float},
180 {&font_size, "fontSize", "FontSize", DEF_FONT_SIZE, t_Float},
181 {&team_a_name, "teamAName", "TeamName", DEF_TEAM_A_NAME, t_String},
182 {&team_b_name, "teamBName", "TeamName", DEF_TEAM_B_NAME, t_String},
183 {&team_a_count, "teamACount", "TeamCount", DEF_TEAM_A_COUNT, t_Int},
184 {&team_b_count, "teamBCount", "TeamCount", DEF_TEAM_A_COUNT, t_Int},
185 {&ping_arg, "ping", "Ping", DEF_PING, t_String},
186 {&ping_timeout, "pingTimeout", "PingTimeout", DEF_PING_TIMEOUT, t_Int},
187 {&resolve_p, "resolve", "Resolve", DEF_RESOLVE, t_Bool},
188 {×_p, "times", "Times", DEF_TIMES, t_Bool},
189 {&wobble_p, "wobble", "Wobble", DEF_WOBBLE, t_Bool},
190 {&debug_p, "debug", "Debug", DEF_DEBUG, t_Bool},
193 ENTRYPOINT ModeSpecOpt sonar_opts = {countof(opts), opts, countof(vars), vars, NULL};
197 draw_screen (ModeInfo *mi, Bool mesh_p, Bool sweep_p)
199 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
200 int wire = MI_IS_WIREFRAME(mi);
203 int th_steps, r_steps, r_skip, th_skip, th_skip2, outer_r;
204 GLfloat curvature = M_PI * 0.4;
205 GLfloat r0, r1, z0, z1, zoff;
208 static const GLfloat glass[4] = {0.0, 0.4, 0.0, 0.5};
209 static const GLfloat lines[4] = {0.0, 0.7, 0.0, 0.5};
210 static const GLfloat sweepc[4] = {0.2, 1.0, 0.2, 0.5};
211 static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
212 static const GLfloat shiny = 20.0;
214 if (wire && !(mesh_p || sweep_p)) return 0;
216 glDisable (GL_TEXTURE_2D);
218 glFrontFace (GL_CCW);
219 th_steps = 36 * 4; /* must be a multiple of th_skip2 divisor */
226 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
227 glMateriali (GL_FRONT, GL_SHININESS, shiny);
228 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mesh_p ? lines : glass);
229 if (wire) glColor3fv (lines);
233 th_skip = th_steps / 12;
234 th_skip2 = th_steps / 36;
235 r_skip = r_steps / 3;
236 outer_r = r_steps * 0.93;
239 glLineWidth (sp->line_thickness);
242 ring = (XYZ *) calloc (th_steps, sizeof(*ring));
244 for (i = 0; i < th_steps; i++)
246 double a = M_PI * 2 * i / th_steps;
251 /* place the bottom of the disc on the xy plane. */
252 zoff = cos (curvature/2 * (M_PI/2)) / 2;
254 for (i = r_steps; i > 0; i--)
258 r0 = i / (GLfloat) r_steps;
259 r1 = (i+1) / (GLfloat) r_steps;
261 if (r1 > 1) r1 = 1; /* avoid asin lossage */
263 z0 = cos (curvature/2 * asin (r0)) / 2 - zoff;
264 z1 = cos (curvature/2 * asin (r1)) / 2 - zoff;
266 glBegin(wire || mesh_p ? GL_LINES : GL_QUAD_STRIP);
267 for (j0 = 0; j0 <= th_steps; j0++)
271 ? (j0 % th_skip != 0)
272 : (j0 % th_skip2 != 0)))
278 GLfloat r = 1 - (j0 / (GLfloat) (th_steps * sweep_size));
280 color[0] = glass[0] + (sweepc[0] - glass[0]) * r;
281 color[1] = glass[1] + (sweepc[1] - glass[1]) * r;
282 color[2] = glass[2] + (sweepc[2] - glass[2]) * r;
285 color[0] = sweepc[0];
286 color[1] = sweepc[1];
287 color[2] = sweepc[2];
290 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
294 glNormal3f (r0 * ring[j1].x, r0 * ring[j1].y, z0);
295 glVertex3f (r0 * ring[j1].x, r0 * ring[j1].y, z0);
296 glNormal3f (r1 * ring[j1].x, r1 * ring[j1].y, z1);
297 glVertex3f (r1 * ring[j1].x, r1 * ring[j1].y, z1);
300 if (sweep_p && j0 >= th_steps * sweep_size)
311 i < r_steps - r_skip)))
313 glBegin(GL_LINE_LOOP);
314 for (j0 = 0; j0 < th_steps; j0++)
316 glNormal3f (r0 * ring[j0].x, r0 * ring[j0].y, z0);
317 glVertex3f (r0 * ring[j0].x, r0 * ring[j0].y, z0);
324 /* one more polygon for the middle */
325 if (!wire && !sweep_p)
327 glBegin(wire || mesh_p ? GL_LINE_LOOP : GL_POLYGON);
328 glNormal3f (0, 0, 1);
329 for (i = 0; i < th_steps; i++)
331 glNormal3f (r0 * ring[i].x, r0 * ring[i].y, z0);
332 glVertex3f (r0 * ring[i].x, r0 * ring[i].y, z0);
345 draw_text (ModeInfo *mi, const char *string, GLfloat r, GLfloat th,
346 GLfloat ttl, GLfloat size)
348 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
349 int wire = MI_IS_WIREFRAME(mi);
351 GLfloat font_scale = 0.001 * (size > 0 ? size : font_size) / 14.0;
352 int lines = 0, max_w = 0, lh = 0;
353 char *string2 = strdup (string);
354 char *token = string2;
357 if (size <= 0) /* if size not specified, draw in yellow with alpha */
363 color[3] = (ttl / (M_PI * 2)) * 1.2;
364 if (color[3] > 1) color[3] = 1;
366 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
368 glColor3f (color[0]*color[3], color[1]*color[3], color[2]*color[3]);
371 while ((line = strtok (token, "\r\n")))
374 int w, ascent, descent;
375 texture_string_metrics (sp->texfont, line, &e, &ascent, &descent);
377 lh = ascent + descent;
378 if (w > max_w) max_w = w;
384 glTranslatef (r * cos (th), r * sin(th), 0);
385 glScalef (font_scale, font_scale, font_scale);
387 if (size <= 0) /* Draw the dot */
389 GLfloat s = font_size * 1.7;
390 glDisable (GL_TEXTURE_2D);
392 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
393 glVertex3f (0, s, 0);
394 glVertex3f (s, s, 0);
395 glVertex3f (s, 0, 0);
396 glVertex3f (0, 0, 0);
398 glTranslatef (-max_w/2, -lh, 0);
401 glTranslatef (-max_w/2, -lh/2, 0);
403 /* draw each line, centered */
404 if (! wire) glEnable (GL_TEXTURE_2D);
406 string2 = strdup (string);
408 while ((line = strtok (token, "\r\n")))
412 texture_string_metrics (sp->texfont, line, &e, 0, 0);
415 glTranslatef ((max_w-w)/2, 0, polys * 4); /* 'polys' stops Z-fighting. */
419 glBegin (GL_LINE_LOOP);
420 glVertex3f (0, 0, 0);
421 glVertex3f (w, 0, 0);
422 glVertex3f (w, lh, 0);
423 glVertex3f (0, lh, 0);
429 print_texture_string (sp->texfont, line);
432 glTranslatef (0, -lh, 0);
440 if (! wire) glEnable (GL_DEPTH_TEST);
446 /* There's a disc with a hole in it around the screen, to act as a mask
447 preventing slightly off-screen bogies from showing up. This clips 'em.
450 draw_table (ModeInfo *mi)
452 /*sonar_configuration *sp = &sps[MI_SCREEN(mi)];*/
453 int wire = MI_IS_WIREFRAME(mi);
456 int th_steps = 36 * 4; /* same as in draw_screen */
458 static const GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
459 static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0};
460 static const GLfloat shiny = 0.0;
464 glDisable (GL_TEXTURE_2D);
466 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
467 glMateriali (GL_FRONT, GL_SHININESS, shiny);
468 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
470 glFrontFace (GL_CCW);
471 glBegin(wire ? GL_LINES : GL_QUAD_STRIP);
472 glNormal3f (0, 0, 1);
473 for (i = 0; i <= th_steps; i++)
475 double a = M_PI * 2 * i / th_steps;
478 glVertex3f (x, y, 0);
479 glVertex3f (x*10, y*10, 0);
489 draw_angles (ModeInfo *mi)
494 static const GLfloat text[4] = {0.15, 0.15, 0.15, 1.0};
495 static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0};
497 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
498 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, text);
499 glTranslatef (0, 0, 0.01);
500 for (i = 0; i < 360; i += 10)
503 GLfloat a = M_PI/2 - (i / 180.0 * M_PI);
504 sprintf (buf, "%d", i);
505 polys += draw_text (mi, buf, 1.07, a, 0, 10.0);
513 draw_bogies (ModeInfo *mi)
515 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
519 for (b = sp->displayed; b; b = b->next)
522 malloc (strlen (b->name) + (b->desc ? strlen(b->desc) : 0) + 3);
529 polys += draw_text (mi, s, b->r, b->th, b->ttl, -1);
532 /* Move *very slightly* forward so that the text is not all in the
533 same plane: this prevents flickering with overlapping text as
534 the textures fight for priority. */
535 glTranslatef(0, 0, 0.00002);
542 /* called from sonar-sim.c and sonar-icmp.c */
544 sonar_copy_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
546 sonar_bogie *b2 = (sonar_bogie *) calloc (1, sizeof(*b2));
547 b2->name = strdup (b->name);
548 b2->desc = b->desc ? strdup (b->desc) : 0;
552 /* does not copy b->closure */
554 /* Take this opportunity to normalize 'th' to the range [0-2pi). */
555 while (b2->th < 0) b2->th += M_PI*2;
556 while (b2->th >= M_PI*2) b2->th -= M_PI*2;
562 /* called from sonar-icmp.c */
564 sonar_free_bogie (sonar_sensor_data *ssd, sonar_bogie *b)
567 ssd->free_bogie_cb (ssd, b->closure);
569 if (b->desc) free (b->desc);
573 /* removes it from the list and frees it
576 delete_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
577 sonar_bogie **from_list)
579 sonar_bogie *ob, *prev;
580 for (prev = 0, ob = *from_list; ob; prev = ob, ob = ob->next)
584 prev->next = b->next;
586 (*from_list) = b->next;
587 sonar_free_bogie (ssd, b);
593 /* copies the bogie and adds it to the list.
594 if there's another bogie there with the same name, frees that one.
597 copy_and_insert_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
598 sonar_bogie **to_list)
600 sonar_bogie *ob, *next;
602 for (ob = *to_list, next = ob ? ob->next : 0;
604 ob = next, next = ob ? ob->next : 0)
606 if (ob == b) abort(); /* this will end badly */
607 if (!strcmp (ob->name, b->name)) /* match! */
609 delete_bogie (ssd, ob, to_list);
614 b = sonar_copy_bogie (ssd, b);
621 update_sensor_data (sonar_configuration *sp)
623 sonar_bogie *new_list = sp->ssd->scan_cb (sp->ssd);
626 /* If a bogie exists in 'new_list' but not 'pending', add it.
627 If a bogie exists in both, update it in 'pending'.
629 for (b2 = new_list; b2; b2 = b2->next)
632 fprintf (stderr, "%s: updated: %s (%5.2f %5.2f %5.2f)\n",
633 progname, b2->name, b2->r, b2->th, b2->ttl);
634 copy_and_insert_bogie (sp->ssd, b2, &sp->pending);
636 if (debug_p > 2) fprintf (stderr, "\n");
640 /* Returns whether the given angle lies between two other angles.
641 When those angles cross 0, it assumes the wedge is the smaller one.
642 That is: 5 lies between 10 and 350 degrees (a 20 degree wedge).
645 point_in_wedge (GLfloat th, GLfloat low, GLfloat high)
648 return (th > low && th <= high);
650 return (th <= high || th > low);
654 /* Returns the current time in seconds as a double.
660 # ifdef GETTIMEOFDAY_TWO_ARGS
662 gettimeofday(&now, &tzp);
667 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
672 sweep (sonar_configuration *sp)
676 /* Move the sweep forward (clockwise).
678 GLfloat prev_sweep, this_sweep, tick;
679 GLfloat cycle_secs = 30 / speed; /* default to one cycle every N seconds */
680 this_sweep = ((cycle_secs - fmod (double_time() - sp->start_time +
685 prev_sweep = sp->sweep_th;
686 tick = prev_sweep - this_sweep;
687 while (tick < 0) tick += M_PI*2;
689 sp->sweep_th = this_sweep;
691 if (this_sweep < 0 || this_sweep >= M_PI*2) abort();
692 if (prev_sweep < 0) /* skip first time */
695 if (tick < 0 || tick >= M_PI*2) abort();
698 /* Go through the 'pending' sensor data, find those bogies who are
699 just now being swept, and move them from 'pending' to 'displayed'.
700 (Leave bogies that have not yet been swept alone: we'll get to
701 them when the sweep moves forward.)
706 sonar_bogie *next = b->next;
707 if (point_in_wedge (b->th, this_sweep, prev_sweep))
710 time_t t = time((time_t *) 0);
712 "%s: sweep hit: %02d:%02d: %s: (%5.2f %5.2f %5.2f;"
713 " th=[%.2f < %.2f <= %.2f])\n",
715 (int) (t / 60) % 60, (int) t % 60,
716 b->name, b->r, b->th, b->ttl,
717 this_sweep, b->th, prev_sweep);
720 copy_and_insert_bogie (sp->ssd, b, &sp->displayed);
721 delete_bogie (sp->ssd, b, &sp->pending);
727 /* Update TTL on all currently-displayed bogies; delete the dead.
729 Request sensor updates on the ones just now being swept.
731 Any updates go into 'pending' and might not show up until
732 the next time the sweep comes around. This is to prevent
733 already-drawn bogies from jumping to a new position without
734 having faded out first.
739 sonar_bogie *next = b->next;
745 fprintf (stderr, "%s: TTL expired: %s (%5.2f %5.2f %5.2f)\n",
746 progname, b->name, b->r, b->th, b->ttl);
747 delete_bogie (sp->ssd, b, &sp->displayed);
752 update_sensor_data (sp);
757 draw_startup_blurb (ModeInfo *mi)
759 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
763 const char *msg = sp->error;
764 static const GLfloat color[4] = {0, 1, 0, 1};
766 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
767 glTranslatef (0, 0, 0.3);
768 draw_text (mi, msg, 0, 0, 0, 30.0);
770 /* only leave error message up for N seconds */
771 if (sp->start_time + 6 < double_time())
780 /* Window management, etc
783 reshape_sonar (ModeInfo *mi, int width, int height)
785 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
786 GLfloat h = (GLfloat) height / (GLfloat) width;
788 glViewport (0, 0, (GLint) width, (GLint) height);
790 glMatrixMode(GL_PROJECTION);
792 gluPerspective (30.0, 1/h, 1.0, 100.0);
794 glMatrixMode(GL_MODELVIEW);
796 gluLookAt( 0.0, 0.0, 30.0,
800 glClear(GL_COLOR_BUFFER_BIT);
802 sp->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
807 sonar_handle_event (ModeInfo *mi, XEvent *event)
809 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
811 if (gltrackball_event_handler (event, sp->trackball,
812 MI_WIDTH (mi), MI_HEIGHT (mi),
821 init_sonar (ModeInfo *mi)
823 sonar_configuration *sp;
826 sps = (sonar_configuration *)
827 calloc (MI_NUM_SCREENS(mi), sizeof (sonar_configuration));
829 fprintf(stderr, "%s: out of memory\n", progname);
833 sp = &sps[MI_SCREEN(mi)];
834 sp->glx_context = init_GL(mi);
836 reshape_sonar (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
837 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
839 sp->trackball = gltrackball_init (False);
840 sp->rot = make_rotator (0, 0, 0, 0, speed * 0.003, True);
842 sp->texfont = load_texture_font (MI_DISPLAY(mi), "font");
843 check_gl_error ("loading font");
845 sp->table_list = glGenLists (1);
846 glNewList (sp->table_list, GL_COMPILE);
847 sp->table_polys = draw_table (mi);
850 sp->screen_list = glGenLists (1);
851 glNewList (sp->screen_list, GL_COMPILE);
852 sp->screen_polys = draw_screen (mi, False, False);
855 sp->grid_list = glGenLists (1);
856 glNewList (sp->grid_list, GL_COMPILE);
857 sp->grid_polys = draw_screen (mi, True, False);
860 sp->sweep_list = glGenLists (1);
861 glNewList (sp->sweep_list, GL_COMPILE);
862 sp->sweep_polys = draw_screen (mi, False, True);
865 sp->start_time = double_time ();
866 sp->sweep_offset = random() % 60;
872 # ifdef TEST_ASYNC_NETDB
874 # include <arpa/inet.h>
876 static void _print_sockaddr (void *addr, socklen_t addrlen, FILE *stream)
878 sa_family_t family = ((struct sockaddr *)addr)->sa_family;
883 fputs (inet_ntoa(((struct sockaddr_in *)addr)->sin_addr), stream);
886 inet_ntop(family, &((struct sockaddr_in6 *)addr)->sin6_addr,
896 static void _print_error (int gai_error, int errno_error, FILE *stream)
898 fputs (gai_error == EAI_SYSTEM ? strerror(errno_error) : gai_strerror(gai_error), stream);
901 # if ASYNC_NETDB_USE_GAI
903 static void _print_thread (pthread_t thread, FILE *stream)
906 fprintf (stream, "%#lx", thread);
907 # elif defined __APPLE__ && defined __MACH__
908 fprintf (stream, "%p", thread);
914 # endif /* ASYNC_NETDB_USE_GAI */
916 # endif /* TEST_ASYNC_NETDB */
920 init_sensor (ModeInfo *mi)
922 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
924 if (sp->ssd) abort();
926 if (!ping_arg || !*ping_arg ||
927 !strcmp(ping_arg, "default") ||
928 !!strcmp (ping_arg, "simulation"))
929 /* sonar_init_ping() always disavows privs, even on failure. */
930 sp->ssd = sonar_init_ping (MI_DISPLAY (mi), &sp->error, &sp->desc,
931 ping_arg, ping_timeout, resolve_p, times_p,
934 setuid(getuid()); /* Disavow privs if not pinging. */
936 sp->start_time = double_time (); /* for error message timing */
939 sp->ssd = sonar_init_simulation (MI_DISPLAY (mi), &sp->error, &sp->desc,
940 team_a_name, team_b_name,
941 team_a_count, team_b_count,
946 # if TEST_ASYNC_NETDB
948 For extremely mysterious reasons, setuid apparently causes
949 pthread_join(3) to deadlock.
950 A rough guess at the sequence of events:
951 1. Worker thread is created.
952 2. Worker thread exits.
953 3. setuid(getuid()) is called.
954 4. pthread_join is called slightly later.
956 This may have something to do with glibc's use of SIGSETXID.
961 # if !ASYNC_NETDB_USE_GAI
962 fputs ("Warning: getaddrinfo() was not available at compile time.\n", stderr);
966 static const unsigned long addresses[] =
972 struct sockaddr_in addr;
973 addr.sin_family = AF_INET;
975 addr.sin_addr.s_addr = htonl (addresses[random () % 3]);
977 sp->query0 = async_name_from_addr_start (MI_DISPLAY (mi), (void *)&addr,
982 fputs ("Looking up hostname from address: ", stderr);
983 _print_sockaddr (&addr, sizeof(addr), stderr);
984 # if ASYNC_NETDB_USE_GAI
985 fputs (" @ ", stderr);
986 _print_thread (sp->query0->io.thread, stderr);
991 if (!(random () & 3))
993 fputs ("Aborted hostname lookup (early)\n", stderr);
994 async_name_from_addr_cancel (sp->query0);
1000 static const char *const hosts[] =
1006 const char *host = hosts[random () % 3];
1008 sp->query1 = async_addr_from_name_start (MI_DISPLAY(mi), host);
1010 assert (sp->query1);
1012 fprintf (stderr, "Looking up address from hostname: %s", host);
1013 # if ASYNC_NETDB_USE_GAI
1014 fputs (" @ ", stderr);
1015 _print_thread (sp->query1->io.thread, stderr);
1017 putc ('\n', stderr);
1019 if (!(random () & 3))
1021 fputs ("Aborted address lookup (early)\n", stderr);
1022 async_addr_from_name_cancel (sp->query1);
1033 draw_sonar (ModeInfo *mi)
1035 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
1036 Display *dpy = MI_DISPLAY(mi);
1037 Window window = MI_WINDOW(mi);
1038 int wire = MI_IS_WIREFRAME(mi);
1040 if (!sp->glx_context)
1043 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
1045 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1049 GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
1050 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
1051 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
1052 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
1054 glEnable(GL_TEXTURE_2D);
1055 glEnable(GL_LIGHTING);
1056 glEnable(GL_LIGHT0);
1057 glEnable(GL_CULL_FACE);
1058 glEnable(GL_DEPTH_TEST);
1059 glEnable(GL_NORMALIZE);
1060 glEnable(GL_LINE_SMOOTH);
1062 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1063 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
1064 glShadeModel(GL_SMOOTH);
1066 glLightfv(GL_LIGHT0, GL_POSITION, pos);
1067 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
1068 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
1069 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
1073 glRotatef(current_device_rotation(), 0, 0, 1);
1077 if (MI_WIDTH(mi) < MI_HEIGHT(mi))
1078 s *= (MI_WIDTH(mi) / (float) MI_HEIGHT(mi));
1082 gltrackball_rotate (sp->trackball);
1088 get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
1089 glRotatef (max/2 - x*max, 1, 0, 0);
1090 glRotatef (max/2 - z*max, 0, 1, 0);
1093 mi->polygon_count = 0;
1095 glPushMatrix(); /* table */
1096 glCallList (sp->table_list);
1097 mi->polygon_count += sp->table_polys;
1100 glPushMatrix(); /* text */
1101 glTranslatef (0, 0, -0.01);
1102 mi->polygon_count += draw_bogies (mi);
1105 glCallList (sp->screen_list); /* glass */
1106 mi->polygon_count += sp->screen_polys;
1108 glTranslatef (0, 0, 0.004); /* sweep */
1110 glRotatef ((sp->sweep_th * 180 / M_PI), 0, 0, 1);
1111 if (sp->sweep_th >= 0)
1112 glCallList (sp->sweep_list);
1113 mi->polygon_count += sp->sweep_polys;
1116 glTranslatef (0, 0, 0.004); /* grid */
1117 glCallList (sp->grid_list);
1118 mi->polygon_count += sp->screen_polys;
1121 mi->polygon_count += draw_angles (mi); /* angles */
1124 if (sp->desc) /* local subnet */
1127 glTranslatef (0, 0, 0.00002);
1128 mi->polygon_count += draw_text (mi, sp->desc, 1.35, M_PI * 0.75, 0, 10);
1129 /* glRotatef (45, 0, 0, 1); */
1130 /* mi->polygon_count += draw_text (mi, sp->desc, 1.2, M_PI/2, 0, 10); */
1137 switch (sp->state) {
1138 case MSG: /* Frame 1: get "Resolving Hosts" on screen. */
1139 draw_startup_blurb(mi);
1142 case RESOLVE: /* Frame 2: gethostbyaddr may take a while. */
1147 case RUN: /* Frame N: ping away */
1154 if (mi->fps_p) do_fps (mi);
1157 glXSwapBuffers(dpy, window);
1159 # if TEST_ASYNC_NETDB
1160 if(sp->query0 && async_name_from_addr_is_done (sp->query0))
1162 if (!(random () & 3))
1164 fputs ("Aborted hostname lookup (late)\n", stderr);
1165 async_name_from_addr_cancel (sp->query0);
1169 char *hostname = NULL;
1171 int gai_error = async_name_from_addr_finish (sp->query0, &hostname,
1176 fputs ("Couldn't get hostname: ", stderr);
1177 _print_error (gai_error, errno_error, stderr);
1178 putc ('\n', stderr);
1182 fprintf (stderr, "Got a hostname: %s\n", hostname);
1190 if(sp->query1 && async_addr_from_name_is_done (sp->query1))
1192 if (!(random () & 3))
1194 fputs ("Aborted address lookup (late)\n", stderr);
1195 async_addr_from_name_cancel (sp->query1);
1199 async_netdb_sockaddr_storage_t addr;
1202 int gai_error = async_addr_from_name_finish (sp->query1, &addr,
1203 &addrlen, &errno_error);
1207 fputs ("Couldn't get address: ", stderr);
1208 _print_error (gai_error, errno_error, stderr);
1209 putc ('\n', stderr);
1213 fputs ("Got an address: ", stderr);
1214 _print_sockaddr (&addr, addrlen, stderr);
1215 putc ('\n', stderr);
1223 # endif /* TEST_ASYNC_NETDB */
1227 release_sonar (ModeInfo *mi)
1230 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
1231 sonar_bogie *b = sp->displayed;
1234 sonar_bogie *next = b->next;
1235 free_bogie (sp->ssd, b);
1243 sonar_bogie *next = b->next;
1244 free_bogie (sp->ssd, b);
1249 sp->ssd->free_data_cb (sp->ssd, sp->ssd->closure);
1255 XSCREENSAVER_MODULE ("Sonar", sonar)