1 /* sonar, Copyright (c) 1998-2014 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")))
373 int w = texture_string_width (sp->texfont, line, &lh);
374 if (w > max_w) max_w = w;
380 glTranslatef (r * cos (th), r * sin(th), 0);
381 glScalef (font_scale, font_scale, font_scale);
383 if (size <= 0) /* Draw the dot */
385 GLfloat s = font_size * 1.7;
386 glDisable (GL_TEXTURE_2D);
388 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
389 glVertex3f (0, s, 0);
390 glVertex3f (s, s, 0);
391 glVertex3f (s, 0, 0);
392 glVertex3f (0, 0, 0);
394 glTranslatef (-max_w/2, -lh, 0);
397 glTranslatef (-max_w/2, -lh/2, 0);
399 /* draw each line, centered */
400 if (! wire) glEnable (GL_TEXTURE_2D);
402 string2 = strdup (string);
404 while ((line = strtok (token, "\r\n")))
406 int w = texture_string_width (sp->texfont, line, 0);
408 glTranslatef ((max_w-w)/2, 0, polys * 4); /* 'polys' stops Z-fighting. */
412 glBegin (GL_LINE_LOOP);
413 glVertex3f (0, 0, 0);
414 glVertex3f (w, 0, 0);
415 glVertex3f (w, lh, 0);
416 glVertex3f (0, lh, 0);
422 print_texture_string (sp->texfont, line);
425 glTranslatef (0, -lh, 0);
433 if (! wire) glEnable (GL_DEPTH_TEST);
439 /* There's a disc with a hole in it around the screen, to act as a mask
440 preventing slightly off-screen bogies from showing up. This clips 'em.
443 draw_table (ModeInfo *mi)
445 /*sonar_configuration *sp = &sps[MI_SCREEN(mi)];*/
446 int wire = MI_IS_WIREFRAME(mi);
449 int th_steps = 36 * 4; /* same as in draw_screen */
451 static const GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
452 static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0};
453 static const GLfloat shiny = 0.0;
457 glDisable (GL_TEXTURE_2D);
459 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
460 glMateriali (GL_FRONT, GL_SHININESS, shiny);
461 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
463 glFrontFace (GL_CCW);
464 glBegin(wire ? GL_LINES : GL_QUAD_STRIP);
465 glNormal3f (0, 0, 1);
466 for (i = 0; i <= th_steps; i++)
468 double a = M_PI * 2 * i / th_steps;
471 glVertex3f (x, y, 0);
472 glVertex3f (x*10, y*10, 0);
482 draw_angles (ModeInfo *mi)
487 static const GLfloat text[4] = {0.15, 0.15, 0.15, 1.0};
488 static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0};
490 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
491 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, text);
492 glTranslatef (0, 0, 0.01);
493 for (i = 0; i < 360; i += 10)
496 GLfloat a = M_PI/2 - (i / 180.0 * M_PI);
497 sprintf (buf, "%d", i);
498 polys += draw_text (mi, buf, 1.07, a, 0, 10.0);
506 draw_bogies (ModeInfo *mi)
508 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
512 for (b = sp->displayed; b; b = b->next)
515 malloc (strlen (b->name) + (b->desc ? strlen(b->desc) : 0) + 3);
522 polys += draw_text (mi, s, b->r, b->th, b->ttl, -1);
525 /* Move *very slightly* forward so that the text is not all in the
526 same plane: this prevents flickering with overlapping text as
527 the textures fight for priority. */
528 glTranslatef(0, 0, 0.00002);
535 /* called from sonar-sim.c and sonar-icmp.c */
537 sonar_copy_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
539 sonar_bogie *b2 = (sonar_bogie *) calloc (1, sizeof(*b2));
540 b2->name = strdup (b->name);
541 b2->desc = b->desc ? strdup (b->desc) : 0;
545 /* does not copy b->closure */
547 /* Take this opportunity to normalize 'th' to the range [0-2pi). */
548 while (b2->th < 0) b2->th += M_PI*2;
549 while (b2->th >= M_PI*2) b2->th -= M_PI*2;
555 /* called from sonar-icmp.c */
557 sonar_free_bogie (sonar_sensor_data *ssd, sonar_bogie *b)
560 ssd->free_bogie_cb (ssd, b->closure);
562 if (b->desc) free (b->desc);
566 /* removes it from the list and frees it
569 delete_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
570 sonar_bogie **from_list)
572 sonar_bogie *ob, *prev;
573 for (prev = 0, ob = *from_list; ob; prev = ob, ob = ob->next)
577 prev->next = b->next;
579 (*from_list) = b->next;
580 sonar_free_bogie (ssd, b);
586 /* copies the bogie and adds it to the list.
587 if there's another bogie there with the same name, frees that one.
590 copy_and_insert_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
591 sonar_bogie **to_list)
593 sonar_bogie *ob, *next;
595 for (ob = *to_list, next = ob ? ob->next : 0;
597 ob = next, next = ob ? ob->next : 0)
599 if (ob == b) abort(); /* this will end badly */
600 if (!strcmp (ob->name, b->name)) /* match! */
602 delete_bogie (ssd, ob, to_list);
607 b = sonar_copy_bogie (ssd, b);
614 update_sensor_data (sonar_configuration *sp)
616 sonar_bogie *new_list = sp->ssd->scan_cb (sp->ssd);
619 /* If a bogie exists in 'new_list' but not 'pending', add it.
620 If a bogie exists in both, update it in 'pending'.
622 for (b2 = new_list; b2; b2 = b2->next)
625 fprintf (stderr, "%s: updated: %s (%5.2f %5.2f %5.2f)\n",
626 progname, b2->name, b2->r, b2->th, b2->ttl);
627 copy_and_insert_bogie (sp->ssd, b2, &sp->pending);
629 if (debug_p > 2) fprintf (stderr, "\n");
633 /* Returns whether the given angle lies between two other angles.
634 When those angles cross 0, it assumes the wedge is the smaller one.
635 That is: 5 lies between 10 and 350 degrees (a 20 degree wedge).
638 point_in_wedge (GLfloat th, GLfloat low, GLfloat high)
641 return (th > low && th <= high);
643 return (th <= high || th > low);
647 /* Returns the current time in seconds as a double.
653 # ifdef GETTIMEOFDAY_TWO_ARGS
655 gettimeofday(&now, &tzp);
660 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
665 sweep (sonar_configuration *sp)
669 /* Move the sweep forward (clockwise).
671 GLfloat prev_sweep, this_sweep, tick;
672 GLfloat cycle_secs = 30 / speed; /* default to one cycle every N seconds */
673 this_sweep = ((cycle_secs - fmod (double_time() - sp->start_time +
678 prev_sweep = sp->sweep_th;
679 tick = prev_sweep - this_sweep;
680 while (tick < 0) tick += M_PI*2;
682 sp->sweep_th = this_sweep;
684 if (this_sweep < 0 || this_sweep >= M_PI*2) abort();
685 if (prev_sweep < 0) /* skip first time */
688 if (tick < 0 || tick >= M_PI*2) abort();
691 /* Go through the 'pending' sensor data, find those bogies who are
692 just now being swept, and move them from 'pending' to 'displayed'.
693 (Leave bogies that have not yet been swept alone: we'll get to
694 them when the sweep moves forward.)
699 sonar_bogie *next = b->next;
700 if (point_in_wedge (b->th, this_sweep, prev_sweep))
703 time_t t = time((time_t *) 0);
705 "%s: sweep hit: %02d:%02d: %s: (%5.2f %5.2f %5.2f;"
706 " th=[%.2f < %.2f <= %.2f])\n",
708 (int) (t / 60) % 60, (int) t % 60,
709 b->name, b->r, b->th, b->ttl,
710 this_sweep, b->th, prev_sweep);
713 copy_and_insert_bogie (sp->ssd, b, &sp->displayed);
714 delete_bogie (sp->ssd, b, &sp->pending);
720 /* Update TTL on all currently-displayed bogies; delete the dead.
722 Request sensor updates on the ones just now being swept.
724 Any updates go into 'pending' and might not show up until
725 the next time the sweep comes around. This is to prevent
726 already-drawn bogies from jumping to a new position without
727 having faded out first.
732 sonar_bogie *next = b->next;
738 fprintf (stderr, "%s: TTL expired: %s (%5.2f %5.2f %5.2f)\n",
739 progname, b->name, b->r, b->th, b->ttl);
740 delete_bogie (sp->ssd, b, &sp->displayed);
745 update_sensor_data (sp);
750 draw_startup_blurb (ModeInfo *mi)
752 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
753 const char *msg = (sp->error ? sp->error : "Resolving hosts...");
754 static const GLfloat color[4] = {0, 1, 0, 1};
756 if (!sp->error && ping_arg && !strcmp (ping_arg, "simulation"))
757 return; /* don't bother */
759 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
760 glTranslatef (0, 0, 0.3);
761 draw_text (mi, msg, 0, 0, 0, 30.0);
763 /* only leave error message up for N seconds */
765 sp->start_time + 6 < double_time())
773 /* Window management, etc
776 reshape_sonar (ModeInfo *mi, int width, int height)
778 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
779 GLfloat h = (GLfloat) height / (GLfloat) width;
781 glViewport (0, 0, (GLint) width, (GLint) height);
783 glMatrixMode(GL_PROJECTION);
785 gluPerspective (30.0, 1/h, 1.0, 100.0);
787 glMatrixMode(GL_MODELVIEW);
789 gluLookAt( 0.0, 0.0, 30.0,
793 glClear(GL_COLOR_BUFFER_BIT);
795 sp->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
800 sonar_handle_event (ModeInfo *mi, XEvent *event)
802 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
804 if (gltrackball_event_handler (event, sp->trackball,
805 MI_WIDTH (mi), MI_HEIGHT (mi),
814 init_sonar (ModeInfo *mi)
816 sonar_configuration *sp;
819 sps = (sonar_configuration *)
820 calloc (MI_NUM_SCREENS(mi), sizeof (sonar_configuration));
822 fprintf(stderr, "%s: out of memory\n", progname);
826 sp = &sps[MI_SCREEN(mi)];
827 sp->glx_context = init_GL(mi);
829 reshape_sonar (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
830 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
832 sp->trackball = gltrackball_init (False);
833 sp->rot = make_rotator (0, 0, 0, 0, speed * 0.003, True);
835 sp->texfont = load_texture_font (MI_DISPLAY(mi), "font");
836 check_gl_error ("loading font");
838 sp->table_list = glGenLists (1);
839 glNewList (sp->table_list, GL_COMPILE);
840 sp->table_polys = draw_table (mi);
843 sp->screen_list = glGenLists (1);
844 glNewList (sp->screen_list, GL_COMPILE);
845 sp->screen_polys = draw_screen (mi, False, False);
848 sp->grid_list = glGenLists (1);
849 glNewList (sp->grid_list, GL_COMPILE);
850 sp->grid_polys = draw_screen (mi, True, False);
853 sp->sweep_list = glGenLists (1);
854 glNewList (sp->sweep_list, GL_COMPILE);
855 sp->sweep_polys = draw_screen (mi, False, True);
858 sp->start_time = double_time ();
859 sp->sweep_offset = random() % 60;
865 # ifdef TEST_ASYNC_NETDB
867 # include <arpa/inet.h>
869 static void _print_sockaddr (void *addr, socklen_t addrlen, FILE *stream)
871 sa_family_t family = ((struct sockaddr *)addr)->sa_family;
876 fputs (inet_ntoa(((struct sockaddr_in *)addr)->sin_addr), stream);
879 inet_ntop(family, &((struct sockaddr_in6 *)addr)->sin6_addr,
889 static void _print_error (int gai_error, int errno_error, FILE *stream)
891 fputs (gai_error == EAI_SYSTEM ? strerror(errno_error) : gai_strerror(gai_error), stream);
894 # if ASYNC_NETDB_USE_GAI
896 static void _print_thread (pthread_t thread, FILE *stream)
899 fprintf (stream, "%#lx", thread);
900 # elif defined __APPLE__ && defined __MACH__
901 fprintf (stream, "%p", thread);
907 # endif /* ASYNC_NETDB_USE_GAI */
909 # endif /* TEST_ASYNC_NETDB */
913 init_sensor (ModeInfo *mi)
915 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
917 if (sp->ssd) abort();
919 if (!ping_arg || !*ping_arg ||
920 !strcmp(ping_arg, "default") ||
921 !!strcmp (ping_arg, "simulation"))
922 sp->ssd = sonar_init_ping (MI_DISPLAY (mi), &sp->error, &sp->desc,
923 ping_arg, ping_timeout, resolve_p, times_p,
926 sp->start_time = double_time (); /* for error message timing */
928 /* Disavow privs. This was already done in init_ping(), but
929 we might not have called that at all, so do it again. */
933 sp->ssd = sonar_init_simulation (MI_DISPLAY (mi), &sp->error, &sp->desc,
934 team_a_name, team_b_name,
935 team_a_count, team_b_count,
940 # if TEST_ASYNC_NETDB
942 For extremely mysterious reasons, setuid apparently causes
943 pthread_join(3) to deadlock.
944 A rough guess at the sequence of events:
945 1. Worker thread is created.
946 2. Worker thread exits.
947 3. setuid(getuid()) is called.
948 4. pthread_join is called slightly later.
950 This may have something to do with glibc's use of SIGSETXID.
955 # if !ASYNC_NETDB_USE_GAI
956 fputs ("Warning: getaddrinfo() was not available at compile time.\n", stderr);
960 static const unsigned long addresses[] =
966 struct sockaddr_in addr;
967 addr.sin_family = AF_INET;
969 addr.sin_addr.s_addr = htonl (addresses[random () % 3]);
971 sp->query0 = async_name_from_addr_start (MI_DISPLAY (mi), (void *)&addr,
976 fputs ("Looking up hostname from address: ", stderr);
977 _print_sockaddr (&addr, sizeof(addr), stderr);
978 # if ASYNC_NETDB_USE_GAI
979 fputs (" @ ", stderr);
980 _print_thread (sp->query0->io.thread, stderr);
985 if (!(random () & 3))
987 fputs ("Aborted hostname lookup (early)\n", stderr);
988 async_name_from_addr_cancel (sp->query0);
994 static const char *const hosts[] =
1000 const char *host = hosts[random () % 3];
1002 sp->query1 = async_addr_from_name_start (MI_DISPLAY(mi), host);
1004 assert (sp->query1);
1006 fprintf (stderr, "Looking up address from hostname: %s", host);
1007 # if ASYNC_NETDB_USE_GAI
1008 fputs (" @ ", stderr);
1009 _print_thread (sp->query1->io.thread, stderr);
1011 putc ('\n', stderr);
1013 if (!(random () & 3))
1015 fputs ("Aborted address lookup (early)\n", stderr);
1016 async_addr_from_name_cancel (sp->query1);
1027 draw_sonar (ModeInfo *mi)
1029 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
1030 Display *dpy = MI_DISPLAY(mi);
1031 Window window = MI_WINDOW(mi);
1032 int wire = MI_IS_WIREFRAME(mi);
1034 if (!sp->glx_context)
1037 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
1039 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1043 GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
1044 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
1045 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
1046 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
1048 glEnable(GL_TEXTURE_2D);
1049 glEnable(GL_LIGHTING);
1050 glEnable(GL_LIGHT0);
1051 glEnable(GL_CULL_FACE);
1052 glEnable(GL_DEPTH_TEST);
1053 glEnable(GL_NORMALIZE);
1054 glEnable(GL_LINE_SMOOTH);
1056 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1057 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
1058 glShadeModel(GL_SMOOTH);
1060 glLightfv(GL_LIGHT0, GL_POSITION, pos);
1061 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
1062 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
1063 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
1067 glRotatef(current_device_rotation(), 0, 0, 1);
1071 if (MI_WIDTH(mi) < MI_HEIGHT(mi))
1072 s *= (MI_WIDTH(mi) / (float) MI_HEIGHT(mi));
1076 gltrackball_rotate (sp->trackball);
1082 get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
1083 glRotatef (max/2 - x*max, 1, 0, 0);
1084 glRotatef (max/2 - z*max, 0, 1, 0);
1087 mi->polygon_count = 0;
1089 glPushMatrix(); /* table */
1090 glCallList (sp->table_list);
1091 mi->polygon_count += sp->table_polys;
1094 glPushMatrix(); /* text */
1095 glTranslatef (0, 0, -0.01);
1096 mi->polygon_count += draw_bogies (mi);
1099 glCallList (sp->screen_list); /* glass */
1100 mi->polygon_count += sp->screen_polys;
1102 glTranslatef (0, 0, 0.004); /* sweep */
1104 glRotatef ((sp->sweep_th * 180 / M_PI), 0, 0, 1);
1105 if (sp->sweep_th >= 0)
1106 glCallList (sp->sweep_list);
1107 mi->polygon_count += sp->sweep_polys;
1110 glTranslatef (0, 0, 0.004); /* grid */
1111 glCallList (sp->grid_list);
1112 mi->polygon_count += sp->screen_polys;
1115 mi->polygon_count += draw_angles (mi); /* angles */
1118 if (sp->desc) /* local subnet */
1121 glTranslatef (0, 0, 0.00002);
1122 mi->polygon_count += draw_text (mi, sp->desc, 1.35, M_PI * 0.75, 0, 10);
1123 /* glRotatef (45, 0, 0, 1); */
1124 /* mi->polygon_count += draw_text (mi, sp->desc, 1.2, M_PI/2, 0, 10); */
1131 switch (sp->state) {
1132 case MSG: /* Frame 1: get "Resolving Hosts" on screen. */
1133 draw_startup_blurb(mi);
1136 case RESOLVE: /* Frame 2: gethostbyaddr may take a while. */
1141 case RUN: /* Frame N: ping away */
1148 if (mi->fps_p) do_fps (mi);
1151 glXSwapBuffers(dpy, window);
1153 # if TEST_ASYNC_NETDB
1154 if(sp->query0 && async_name_from_addr_is_done (sp->query0))
1156 if (!(random () & 3))
1158 fputs ("Aborted hostname lookup (late)\n", stderr);
1159 async_name_from_addr_cancel (sp->query0);
1163 char *hostname = NULL;
1165 int gai_error = async_name_from_addr_finish (sp->query0, &hostname,
1170 fputs ("Couldn't get hostname: ", stderr);
1171 _print_error (gai_error, errno_error, stderr);
1172 putc ('\n', stderr);
1176 fprintf (stderr, "Got a hostname: %s\n", hostname);
1184 if(sp->query1 && async_addr_from_name_is_done (sp->query1))
1186 if (!(random () & 3))
1188 fputs ("Aborted address lookup (late)\n", stderr);
1189 async_addr_from_name_cancel (sp->query1);
1193 async_netdb_sockaddr_storage_t addr;
1196 int gai_error = async_addr_from_name_finish (sp->query1, &addr,
1197 &addrlen, &errno_error);
1201 fputs ("Couldn't get address: ", stderr);
1202 _print_error (gai_error, errno_error, stderr);
1203 putc ('\n', stderr);
1207 fputs ("Got an address: ", stderr);
1208 _print_sockaddr (&addr, addrlen, stderr);
1209 putc ('\n', stderr);
1217 # endif /* TEST_ASYNC_NETDB */
1221 release_sonar (ModeInfo *mi)
1224 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
1225 sonar_bogie *b = sp->displayed;
1228 sonar_bogie *next = b->next;
1229 free_bogie (sp->ssd, b);
1237 sonar_bogie *next = b->next;
1238 free_bogie (sp->ssd, b);
1243 sp->ssd->free_data_cb (sp->ssd, sp->ssd->closure);
1249 XSCREENSAVER_MODULE ("Sonar", sonar)