1 /* sonar, Copyright (c) 1998-2018 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 release_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 (MI_WIDTH(mi) > 2560) font_scale /= 2; /* Retina displays */
359 if (size <= 0) /* if size not specified, draw in yellow with alpha */
365 color[3] = (ttl / (M_PI * 2)) * 1.2;
366 if (color[3] > 1) color[3] = 1;
368 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
370 glColor3f (color[0]*color[3], color[1]*color[3], color[2]*color[3]);
373 while ((line = strtok (token, "\r\n")))
376 int w, ascent, descent;
377 texture_string_metrics (sp->texfont, line, &e, &ascent, &descent);
379 lh = ascent + descent;
380 if (w > max_w) max_w = w;
386 glTranslatef (r * cos (th), r * sin(th), 0);
387 glScalef (font_scale, font_scale, font_scale);
389 if (size <= 0) /* Draw the dot */
391 GLfloat s = font_size * 1.7;
392 glDisable (GL_TEXTURE_2D);
394 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
395 glVertex3f (0, s, 0);
396 glVertex3f (s, s, 0);
397 glVertex3f (s, 0, 0);
398 glVertex3f (0, 0, 0);
400 glTranslatef (-max_w/2, -lh, 0);
403 glTranslatef (-max_w/2, -lh/2, 0);
405 /* draw each line, centered */
406 if (! wire) glEnable (GL_TEXTURE_2D);
408 string2 = strdup (string);
410 while ((line = strtok (token, "\r\n")))
414 texture_string_metrics (sp->texfont, line, &e, 0, 0);
417 glTranslatef ((max_w-w)/2, 0, polys * 4); /* 'polys' stops Z-fighting. */
421 glBegin (GL_LINE_LOOP);
422 glVertex3f (0, 0, 0);
423 glVertex3f (w, 0, 0);
424 glVertex3f (w, lh, 0);
425 glVertex3f (0, lh, 0);
431 print_texture_string (sp->texfont, line);
434 glTranslatef (0, -lh, 0);
442 if (! wire) glEnable (GL_DEPTH_TEST);
448 /* There's a disc with a hole in it around the screen, to act as a mask
449 preventing slightly off-screen bogies from showing up. This clips 'em.
452 draw_table (ModeInfo *mi)
454 /*sonar_configuration *sp = &sps[MI_SCREEN(mi)];*/
455 int wire = MI_IS_WIREFRAME(mi);
458 int th_steps = 36 * 4; /* same as in draw_screen */
460 static const GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
461 static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0};
462 static const GLfloat shiny = 0.0;
466 glDisable (GL_TEXTURE_2D);
468 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
469 glMateriali (GL_FRONT, GL_SHININESS, shiny);
470 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
472 glFrontFace (GL_CCW);
473 glBegin(wire ? GL_LINES : GL_QUAD_STRIP);
474 glNormal3f (0, 0, 1);
475 for (i = 0; i <= th_steps; i++)
477 double a = M_PI * 2 * i / th_steps;
480 glVertex3f (x, y, 0);
481 glVertex3f (x*10, y*10, 0);
491 draw_angles (ModeInfo *mi)
496 static const GLfloat text[4] = {0.15, 0.15, 0.15, 1.0};
497 static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0};
499 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
500 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, text);
501 glTranslatef (0, 0, 0.01);
502 for (i = 0; i < 360; i += 10)
505 GLfloat a = M_PI/2 - (i / 180.0 * M_PI);
506 sprintf (buf, "%d", i);
507 polys += draw_text (mi, buf, 1.07, a, 0, 10.0);
515 draw_bogies (ModeInfo *mi)
517 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
521 for (b = sp->displayed; b; b = b->next)
524 malloc (strlen (b->name) + (b->desc ? strlen(b->desc) : 0) + 3);
531 polys += draw_text (mi, s, b->r, b->th, b->ttl, -1);
534 /* Move *very slightly* forward so that the text is not all in the
535 same plane: this prevents flickering with overlapping text as
536 the textures fight for priority. */
537 glTranslatef(0, 0, 0.00002);
544 /* called from sonar-sim.c and sonar-icmp.c */
546 sonar_copy_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
548 sonar_bogie *b2 = (sonar_bogie *) calloc (1, sizeof(*b2));
549 b2->name = strdup (b->name);
550 b2->desc = b->desc ? strdup (b->desc) : 0;
554 /* does not copy b->closure */
556 /* Take this opportunity to normalize 'th' to the range [0-2pi). */
557 while (b2->th < 0) b2->th += M_PI*2;
558 while (b2->th >= M_PI*2) b2->th -= M_PI*2;
564 /* called from sonar-icmp.c */
566 sonar_free_bogie (sonar_sensor_data *ssd, sonar_bogie *b)
569 ssd->free_bogie_cb (ssd, b->closure);
571 if (b->desc) free (b->desc);
575 /* removes it from the list and frees it
578 delete_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
579 sonar_bogie **from_list)
581 sonar_bogie *ob, *prev;
582 for (prev = 0, ob = *from_list; ob; prev = ob, ob = ob->next)
586 prev->next = b->next;
588 (*from_list) = b->next;
589 sonar_free_bogie (ssd, b);
595 /* copies the bogie and adds it to the list.
596 if there's another bogie there with the same name, frees that one.
599 copy_and_insert_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
600 sonar_bogie **to_list)
602 sonar_bogie *ob, *next;
604 for (ob = *to_list, next = ob ? ob->next : 0;
606 ob = next, next = ob ? ob->next : 0)
608 if (ob == b) abort(); /* this will end badly */
609 if (!strcmp (ob->name, b->name)) /* match! */
611 delete_bogie (ssd, ob, to_list);
616 b = sonar_copy_bogie (ssd, b);
623 update_sensor_data (sonar_configuration *sp)
625 sonar_bogie *new_list = sp->ssd->scan_cb (sp->ssd);
628 /* If a bogie exists in 'new_list' but not 'pending', add it.
629 If a bogie exists in both, update it in 'pending'.
631 for (b2 = new_list; b2; b2 = b2->next)
634 fprintf (stderr, "%s: updated: %s (%5.2f %5.2f %5.2f)\n",
635 progname, b2->name, b2->r, b2->th, b2->ttl);
636 copy_and_insert_bogie (sp->ssd, b2, &sp->pending);
638 if (debug_p > 2) fprintf (stderr, "\n");
642 /* Returns whether the given angle lies between two other angles.
643 When those angles cross 0, it assumes the wedge is the smaller one.
644 That is: 5 lies between 10 and 350 degrees (a 20 degree wedge).
647 point_in_wedge (GLfloat th, GLfloat low, GLfloat high)
650 return (th > low && th <= high);
652 return (th <= high || th > low);
656 /* Returns the current time in seconds as a double.
662 # ifdef GETTIMEOFDAY_TWO_ARGS
664 gettimeofday(&now, &tzp);
669 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
674 sweep (sonar_configuration *sp)
678 /* Move the sweep forward (clockwise).
680 GLfloat prev_sweep, this_sweep, tick;
681 GLfloat cycle_secs = 30 / speed; /* default to one cycle every N seconds */
682 this_sweep = ((cycle_secs - fmod (double_time() - sp->start_time +
687 prev_sweep = sp->sweep_th;
688 tick = prev_sweep - this_sweep;
689 while (tick < 0) tick += M_PI*2;
691 sp->sweep_th = this_sweep;
693 if (this_sweep < 0 || this_sweep >= M_PI*2) abort();
694 if (prev_sweep < 0) /* skip first time */
697 if (tick < 0 || tick >= M_PI*2) abort();
700 /* Go through the 'pending' sensor data, find those bogies who are
701 just now being swept, and move them from 'pending' to 'displayed'.
702 (Leave bogies that have not yet been swept alone: we'll get to
703 them when the sweep moves forward.)
708 sonar_bogie *next = b->next;
709 if (point_in_wedge (b->th, this_sweep, prev_sweep))
712 time_t t = time((time_t *) 0);
714 "%s: sweep hit: %02d:%02d: %s: (%5.2f %5.2f %5.2f;"
715 " th=[%.2f < %.2f <= %.2f])\n",
717 (int) (t / 60) % 60, (int) t % 60,
718 b->name, b->r, b->th, b->ttl,
719 this_sweep, b->th, prev_sweep);
722 copy_and_insert_bogie (sp->ssd, b, &sp->displayed);
723 delete_bogie (sp->ssd, b, &sp->pending);
729 /* Update TTL on all currently-displayed bogies; delete the dead.
731 Request sensor updates on the ones just now being swept.
733 Any updates go into 'pending' and might not show up until
734 the next time the sweep comes around. This is to prevent
735 already-drawn bogies from jumping to a new position without
736 having faded out first.
741 sonar_bogie *next = b->next;
747 fprintf (stderr, "%s: TTL expired: %s (%5.2f %5.2f %5.2f)\n",
748 progname, b->name, b->r, b->th, b->ttl);
749 delete_bogie (sp->ssd, b, &sp->displayed);
754 update_sensor_data (sp);
759 draw_startup_blurb (ModeInfo *mi)
761 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
765 const char *msg = sp->error;
766 static const GLfloat color[4] = {0, 1, 0, 1};
768 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
769 glTranslatef (0, 0, 0.3);
770 draw_text (mi, msg, 0, 0, 0, 30.0);
772 /* only leave error message up for N seconds */
773 if (sp->start_time + 6 < double_time())
782 /* Window management, etc
785 reshape_sonar (ModeInfo *mi, int width, int height)
787 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
788 GLfloat h = (GLfloat) height / (GLfloat) width;
791 if (width > height * 5) { /* tiny window: show middle */
792 height = width * 9/16;
794 h = height / (GLfloat) width;
797 glViewport (0, y, (GLint) width, (GLint) height);
799 glMatrixMode(GL_PROJECTION);
801 gluPerspective (30.0, 1/h, 1.0, 100.0);
803 glMatrixMode(GL_MODELVIEW);
805 gluLookAt( 0.0, 0.0, 30.0,
809 glClear(GL_COLOR_BUFFER_BIT);
811 sp->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
816 sonar_handle_event (ModeInfo *mi, XEvent *event)
818 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
820 if (gltrackball_event_handler (event, sp->trackball,
821 MI_WIDTH (mi), MI_HEIGHT (mi),
829 init_sonar (ModeInfo *mi)
831 sonar_configuration *sp;
834 sp = &sps[MI_SCREEN(mi)];
835 sp->glx_context = init_GL(mi);
837 reshape_sonar (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
838 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
840 sp->trackball = gltrackball_init (False);
841 sp->rot = make_rotator (0, 0, 0, 0, speed * 0.003, True);
843 sp->texfont = load_texture_font (MI_DISPLAY(mi), "font");
844 check_gl_error ("loading font");
846 sp->table_list = glGenLists (1);
847 glNewList (sp->table_list, GL_COMPILE);
848 sp->table_polys = draw_table (mi);
851 sp->screen_list = glGenLists (1);
852 glNewList (sp->screen_list, GL_COMPILE);
853 sp->screen_polys = draw_screen (mi, False, False);
856 sp->grid_list = glGenLists (1);
857 glNewList (sp->grid_list, GL_COMPILE);
858 sp->grid_polys = draw_screen (mi, True, False);
861 sp->sweep_list = glGenLists (1);
862 glNewList (sp->sweep_list, GL_COMPILE);
863 sp->sweep_polys = draw_screen (mi, False, True);
866 sp->start_time = double_time ();
867 sp->sweep_offset = random() % 60;
873 # ifdef TEST_ASYNC_NETDB
875 # include <arpa/inet.h>
877 static void _print_sockaddr (void *addr, socklen_t addrlen, FILE *stream)
879 sa_family_t family = ((struct sockaddr *)addr)->sa_family;
884 fputs (inet_ntoa(((struct sockaddr_in *)addr)->sin_addr), stream);
887 inet_ntop(family, &((struct sockaddr_in6 *)addr)->sin6_addr,
897 static void _print_error (int gai_error, int errno_error, FILE *stream)
899 fputs (gai_error == EAI_SYSTEM ? strerror(errno_error) : gai_strerror(gai_error), stream);
902 # if ASYNC_NETDB_USE_GAI
904 static void _print_thread (pthread_t thread, FILE *stream)
907 fprintf (stream, "%#lx", thread);
908 # elif defined __APPLE__ && defined __MACH__
909 fprintf (stream, "%p", thread);
915 # endif /* ASYNC_NETDB_USE_GAI */
917 # endif /* TEST_ASYNC_NETDB */
921 init_sensor (ModeInfo *mi)
923 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
925 if (sp->ssd) abort();
927 if (!ping_arg || !*ping_arg ||
928 !strcmp(ping_arg, "default") ||
929 !!strcmp (ping_arg, "simulation"))
930 /* sonar_init_ping() always disavows privs, even on failure. */
931 sp->ssd = sonar_init_ping (MI_DISPLAY (mi), &sp->error, &sp->desc,
932 ping_arg, ping_timeout, resolve_p, times_p,
935 setuid(getuid()); /* Disavow privs if not pinging. */
937 sp->start_time = double_time (); /* for error message timing */
940 sp->ssd = sonar_init_simulation (MI_DISPLAY (mi), &sp->error, &sp->desc,
941 team_a_name, team_b_name,
942 team_a_count, team_b_count,
947 # if TEST_ASYNC_NETDB
949 For extremely mysterious reasons, setuid apparently causes
950 pthread_join(3) to deadlock.
951 A rough guess at the sequence of events:
952 1. Worker thread is created.
953 2. Worker thread exits.
954 3. setuid(getuid()) is called.
955 4. pthread_join is called slightly later.
957 This may have something to do with glibc's use of SIGSETXID.
962 # if !ASYNC_NETDB_USE_GAI
963 fputs ("Warning: getaddrinfo() was not available at compile time.\n", stderr);
967 static const unsigned long addresses[] =
973 struct sockaddr_in addr;
974 addr.sin_family = AF_INET;
976 addr.sin_addr.s_addr = htonl (addresses[random () % 3]);
978 sp->query0 = async_name_from_addr_start (MI_DISPLAY (mi), (void *)&addr,
983 fputs ("Looking up hostname from address: ", stderr);
984 _print_sockaddr (&addr, sizeof(addr), stderr);
985 # if ASYNC_NETDB_USE_GAI
986 fputs (" @ ", stderr);
987 _print_thread (sp->query0->io.thread, stderr);
992 if (!(random () & 3))
994 fputs ("Aborted hostname lookup (early)\n", stderr);
995 async_name_from_addr_cancel (sp->query0);
1001 static const char *const hosts[] =
1007 const char *host = hosts[random () % 3];
1009 sp->query1 = async_addr_from_name_start (MI_DISPLAY(mi), host);
1011 assert (sp->query1);
1013 fprintf (stderr, "Looking up address from hostname: %s", host);
1014 # if ASYNC_NETDB_USE_GAI
1015 fputs (" @ ", stderr);
1016 _print_thread (sp->query1->io.thread, stderr);
1018 putc ('\n', stderr);
1020 if (!(random () & 3))
1022 fputs ("Aborted address lookup (early)\n", stderr);
1023 async_addr_from_name_cancel (sp->query1);
1034 draw_sonar (ModeInfo *mi)
1036 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
1037 Display *dpy = MI_DISPLAY(mi);
1038 Window window = MI_WINDOW(mi);
1039 int wire = MI_IS_WIREFRAME(mi);
1041 if (!sp->glx_context)
1044 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
1046 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1050 GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
1051 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
1052 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
1053 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
1055 glEnable(GL_TEXTURE_2D);
1056 glEnable(GL_LIGHTING);
1057 glEnable(GL_LIGHT0);
1058 glEnable(GL_CULL_FACE);
1059 glEnable(GL_DEPTH_TEST);
1060 glEnable(GL_NORMALIZE);
1061 glEnable(GL_LINE_SMOOTH);
1063 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1064 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
1065 glShadeModel(GL_SMOOTH);
1067 glLightfv(GL_LIGHT0, GL_POSITION, pos);
1068 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
1069 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
1070 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
1074 glRotatef(current_device_rotation(), 0, 0, 1);
1078 if (MI_WIDTH(mi) < MI_HEIGHT(mi))
1079 s *= (MI_WIDTH(mi) / (float) MI_HEIGHT(mi));
1083 gltrackball_rotate (sp->trackball);
1089 get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
1090 glRotatef (max/2 - x*max, 1, 0, 0);
1091 glRotatef (max/2 - z*max, 0, 1, 0);
1094 mi->polygon_count = 0;
1096 glPushMatrix(); /* table */
1097 glCallList (sp->table_list);
1098 mi->polygon_count += sp->table_polys;
1101 glPushMatrix(); /* text */
1102 glTranslatef (0, 0, -0.01);
1103 mi->polygon_count += draw_bogies (mi);
1106 glCallList (sp->screen_list); /* glass */
1107 mi->polygon_count += sp->screen_polys;
1109 glTranslatef (0, 0, 0.004); /* sweep */
1111 glRotatef ((sp->sweep_th * 180 / M_PI), 0, 0, 1);
1112 if (sp->sweep_th >= 0)
1113 glCallList (sp->sweep_list);
1114 mi->polygon_count += sp->sweep_polys;
1117 glTranslatef (0, 0, 0.004); /* grid */
1118 glCallList (sp->grid_list);
1119 mi->polygon_count += sp->screen_polys;
1122 mi->polygon_count += draw_angles (mi); /* angles */
1125 if (sp->desc) /* local subnet */
1128 glTranslatef (0, 0, 0.00002);
1129 mi->polygon_count += draw_text (mi, sp->desc, 1.35, M_PI * 0.75, 0, 10);
1130 /* glRotatef (45, 0, 0, 1); */
1131 /* mi->polygon_count += draw_text (mi, sp->desc, 1.2, M_PI/2, 0, 10); */
1138 switch (sp->state) {
1139 case MSG: /* Frame 1: get "Resolving Hosts" on screen. */
1140 draw_startup_blurb(mi);
1143 case RESOLVE: /* Frame 2: gethostbyaddr may take a while. */
1148 case RUN: /* Frame N: ping away */
1155 if (mi->fps_p) do_fps (mi);
1158 glXSwapBuffers(dpy, window);
1160 # if TEST_ASYNC_NETDB
1161 if(sp->query0 && async_name_from_addr_is_done (sp->query0))
1163 if (!(random () & 3))
1165 fputs ("Aborted hostname lookup (late)\n", stderr);
1166 async_name_from_addr_cancel (sp->query0);
1170 char *hostname = NULL;
1172 int gai_error = async_name_from_addr_finish (sp->query0, &hostname,
1177 fputs ("Couldn't get hostname: ", stderr);
1178 _print_error (gai_error, errno_error, stderr);
1179 putc ('\n', stderr);
1183 fprintf (stderr, "Got a hostname: %s\n", hostname);
1191 if(sp->query1 && async_addr_from_name_is_done (sp->query1))
1193 if (!(random () & 3))
1195 fputs ("Aborted address lookup (late)\n", stderr);
1196 async_addr_from_name_cancel (sp->query1);
1200 async_netdb_sockaddr_storage_t addr;
1203 int gai_error = async_addr_from_name_finish (sp->query1, &addr,
1204 &addrlen, &errno_error);
1208 fputs ("Couldn't get address: ", stderr);
1209 _print_error (gai_error, errno_error, stderr);
1210 putc ('\n', stderr);
1214 fputs ("Got an address: ", stderr);
1215 _print_sockaddr (&addr, addrlen, stderr);
1216 putc ('\n', stderr);
1224 # endif /* TEST_ASYNC_NETDB */
1228 free_sonar (ModeInfo *mi)
1231 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
1232 sonar_bogie *b = sp->displayed;
1235 sonar_bogie *next = b->next;
1236 free_bogie (sp->ssd, b);
1244 sonar_bogie *next = b->next;
1245 free_bogie (sp->ssd, b);
1250 sp->ssd->free_data_cb (sp->ssd, sp->ssd->closure);
1256 XSCREENSAVER_MODULE ("Sonar", sonar)