1 /* sonar, Copyright (c) 1998-2008 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.
46 * - search the output of "netstat" for the list of hosts to ping;
47 * - plot the contents of /proc/interrupts;
48 * - plot the process table, by process size, cpu usage, or total time;
49 * - plot the logged on users by idle time or cpu usage.
53 #define DEF_FONT "-*-lucidatypewriter-bold-r-normal-*-*-480-*-*-*-*-iso8859-1"
54 #define DEF_SPEED "1.0"
55 #define DEF_SWEEP_SIZE "0.3"
56 #define DEF_FONT_SIZE "12"
57 #define DEF_TEAM_A_NAME "F18"
58 #define DEF_TEAM_B_NAME "MIG"
59 #define DEF_TEAM_A_COUNT "4"
60 #define DEF_TEAM_B_COUNT "4"
61 #define DEF_PING "default"
62 #define DEF_PING_TIMEOUT "3000"
63 #define DEF_RESOLVE "True"
64 #define DEF_TIMES "True"
65 #define DEF_WOBBLE "True"
66 #define DEF_DEBUG "False"
68 #define DEFAULTS "*delay: 30000 \n" \
69 "*font: " DEF_FONT "\n" \
70 "*showFPS: False \n" \
71 "*wireframe: False \n" \
74 # define refresh_sonar 0
76 #define countof(x) (sizeof((x))/sizeof((*x)))
79 # include <unistd.h> /* for setuid() */
82 #include "xlockmore.h"
84 #include "gltrackball.h"
89 #ifdef USE_GL /* whole file */
96 GLXContext *glx_context;
97 trackball_state *trackball;
102 GLfloat sweep_offset;
104 GLuint screen_list, grid_list, sweep_list, table_list;
105 int screen_polys, grid_polys, sweep_polys, table_polys;
107 GLfloat line_thickness;
109 texture_font_data *texfont;
111 sonar_sensor_data *ssd;
114 sonar_bogie *displayed; /* on screen and fading */
115 sonar_bogie *pending; /* returned by sensor, not yet on screen */
117 } sonar_configuration;
119 static sonar_configuration *sps = NULL;
121 static GLfloat speed;
122 static GLfloat sweep_size;
123 static GLfloat font_size;
124 static Bool resolve_p;
126 static Bool wobble_p;
129 static char *team_a_name;
130 static char *team_b_name;
131 static int team_a_count;
132 static int team_b_count;
133 static int ping_timeout;
134 static char *ping_arg;
136 static XrmOptionDescRec opts[] = {
137 { "-speed", ".speed", XrmoptionSepArg, 0 },
138 { "-sweep-size", ".sweepSize", XrmoptionSepArg, 0 },
139 { "-font-size", ".fontSize", XrmoptionSepArg, 0 },
140 { "-team-a-name", ".teamAName", XrmoptionSepArg, 0 },
141 { "-team-b-name", ".teamBName", XrmoptionSepArg, 0 },
142 { "-team-a-count", ".teamACount", XrmoptionSepArg, 0 },
143 { "-team-b-count", ".teamBCount", XrmoptionSepArg, 0 },
144 { "-ping", ".ping", XrmoptionSepArg, 0 },
145 { "-ping-timeout", ".pingTimeout", XrmoptionSepArg, 0 },
146 { "-dns", ".resolve", XrmoptionNoArg, "True" },
147 { "+dns", ".resolve", XrmoptionNoArg, "False" },
148 { "-times", ".times", XrmoptionNoArg, "True" },
149 { "+times", ".times", XrmoptionNoArg, "False" },
150 { "-wobble", ".wobble", XrmoptionNoArg, "True" },
151 { "+wobble", ".wobble", XrmoptionNoArg, "False" },
152 { "-debug", ".debug", XrmoptionNoArg, "True" },
155 static argtype vars[] = {
156 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
157 {&sweep_size, "sweepSize", "SweepSize", DEF_SWEEP_SIZE, t_Float},
158 {&font_size, "fontSize", "FontSize", DEF_FONT_SIZE, t_Float},
159 {&team_a_name, "teamAName", "TeamName", DEF_TEAM_A_NAME, t_String},
160 {&team_b_name, "teamBName", "TeamName", DEF_TEAM_B_NAME, t_String},
161 {&team_a_count, "teamACount", "TeamCount", DEF_TEAM_A_COUNT, t_Int},
162 {&team_b_count, "teamBCount", "TeamCount", DEF_TEAM_A_COUNT, t_Int},
163 {&ping_arg, "ping", "Ping", DEF_PING, t_String},
164 {&ping_timeout, "pingTimeout", "PingTimeout", DEF_PING_TIMEOUT, t_Int},
165 {&resolve_p, "resolve", "Resolve", DEF_RESOLVE, t_Bool},
166 {×_p, "times", "Times", DEF_TIMES, t_Bool},
167 {&wobble_p, "wobble", "Wobble", DEF_WOBBLE, t_Bool},
168 {&debug_p, "debug", "Debug", DEF_DEBUG, t_Bool},
171 ENTRYPOINT ModeSpecOpt sonar_opts = {countof(opts), opts, countof(vars), vars, NULL};
175 draw_screen (ModeInfo *mi, Bool mesh_p, Bool sweep_p)
177 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
178 int wire = MI_IS_WIREFRAME(mi);
181 int th_steps, r_steps, r_skip, th_skip, th_skip2, outer_r;
182 GLfloat curvature = M_PI * 0.4;
183 GLfloat r0, r1, z0, z1, zoff;
186 static const GLfloat glass[4] = {0.0, 0.4, 0.0, 0.5};
187 static const GLfloat lines[4] = {0.0, 0.7, 0.0, 0.5};
188 static const GLfloat sweepc[4] = {0.2, 1.0, 0.2, 0.5};
189 static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
190 static const GLfloat shiny = 20.0;
192 if (wire && !(mesh_p || sweep_p)) return 0;
194 glPushAttrib (GL_ENABLE_BIT);
195 glDisable (GL_TEXTURE_2D);
197 glFrontFace (GL_CCW);
198 th_steps = 36 * 4; /* must be a multiple of th_skip2 divisor */
205 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
206 glMateriali (GL_FRONT, GL_SHININESS, shiny);
207 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mesh_p ? lines : glass);
208 if (wire) glColor3fv (lines);
212 th_skip = th_steps / 12;
213 th_skip2 = th_steps / 36;
214 r_skip = r_steps / 3;
215 outer_r = r_steps * 0.93;
218 glLineWidth (sp->line_thickness);
221 ring = (XYZ *) calloc (th_steps, sizeof(*ring));
223 for (i = 0; i < th_steps; i++)
225 double a = M_PI * 2 * i / th_steps;
230 /* place the bottom of the disc on the xy plane. */
231 zoff = cos (curvature/2 * (M_PI/2)) / 2;
233 for (i = r_steps; i > 0; i--)
237 r0 = i / (GLfloat) r_steps;
238 r1 = (i+1) / (GLfloat) r_steps;
240 if (r1 > 1) r1 = 1; /* avoid asin lossage */
242 z0 = cos (curvature/2 * asin (r0)) / 2 - zoff;
243 z1 = cos (curvature/2 * asin (r1)) / 2 - zoff;
245 glBegin(wire || mesh_p ? GL_LINES : GL_QUAD_STRIP);
246 for (j0 = 0; j0 <= th_steps; j0++)
250 ? (j0 % th_skip != 0)
251 : (j0 % th_skip2 != 0)))
257 GLfloat r = 1 - (j0 / (GLfloat) (th_steps * sweep_size));
259 color[0] = glass[0] + (sweepc[0] - glass[0]) * r;
260 color[1] = glass[1] + (sweepc[1] - glass[1]) * r;
261 color[2] = glass[2] + (sweepc[2] - glass[2]) * r;
264 color[0] = sweepc[0];
265 color[1] = sweepc[1];
266 color[2] = sweepc[2];
269 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
273 glNormal3f (r0 * ring[j1].x, r0 * ring[j1].y, z0);
274 glVertex3f (r0 * ring[j1].x, r0 * ring[j1].y, z0);
275 glNormal3f (r1 * ring[j1].x, r1 * ring[j1].y, z1);
276 glVertex3f (r1 * ring[j1].x, r1 * ring[j1].y, z1);
279 if (sweep_p && j0 >= th_steps * sweep_size)
290 i < r_steps - r_skip)))
292 glBegin(GL_LINE_LOOP);
293 for (j0 = 0; j0 < th_steps; j0++)
295 glNormal3f (r0 * ring[j0].x, r0 * ring[j0].y, z0);
296 glVertex3f (r0 * ring[j0].x, r0 * ring[j0].y, z0);
303 /* one more polygon for the middle */
304 if (!wire && !sweep_p)
306 glBegin(wire || mesh_p ? GL_LINE_LOOP : GL_POLYGON);
307 glNormal3f (0, 0, 1);
308 for (i = 0; i < th_steps; i++)
310 glNormal3f (r0 * ring[i].x, r0 * ring[i].y, z0);
311 glVertex3f (r0 * ring[i].x, r0 * ring[i].y, z0);
325 draw_text (ModeInfo *mi, const char *string, GLfloat r, GLfloat th,
326 GLfloat ttl, GLfloat size)
328 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
329 int wire = MI_IS_WIREFRAME(mi);
331 GLfloat font_scale = 0.001 * (size > 0 ? size : font_size) / 14.0;
332 int lines = 0, max_w = 0, lh = 0;
333 char *string2 = strdup (string);
334 char *token = string2;
338 if (size <= 0) /* if size not specified, draw in yellow with alpha */
343 color[3] = (ttl / (M_PI * 2)) * 1.2;
344 if (color[3] > 1) color[3] = 1;
346 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
348 glColor3f (color[0]*color[3], color[1]*color[3], color[2]*color[3]);
351 while ((line = strtok (token, "\r\n")))
353 int w = texture_string_width (sp->texfont, line, &lh);
354 if (w > max_w) max_w = w;
360 glTranslatef (r * cos (th), r * sin(th), 0);
361 glScalef (font_scale, font_scale, font_scale);
363 if (size <= 0) /* Draw the dot */
365 GLfloat s = font_size * 1.7;
366 glDisable (GL_TEXTURE_2D);
368 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
369 glVertex3f (0, s, 0);
370 glVertex3f (s, s, 0);
371 glVertex3f (s, 0, 0);
372 glVertex3f (0, 0, 0);
374 glTranslatef (-max_w/2, -lh, 0);
377 glTranslatef (-max_w/2, -lh/2, 0);
379 /* draw each line, centered */
380 if (! wire) glEnable (GL_TEXTURE_2D);
382 string2 = strdup (string);
384 while ((line = strtok (token, "\r\n")))
386 int w = texture_string_width (sp->texfont, line, 0);
388 glTranslatef ((max_w-w)/2, 0, 0);
392 glBegin (GL_LINE_LOOP);
393 glVertex3f (0, 0, 0);
394 glVertex3f (w, 0, 0);
395 glVertex3f (w, lh, 0);
396 glVertex3f (0, lh, 0);
402 print_texture_string (sp->texfont, line);
405 glTranslatef (0, -lh, 0);
413 if (! wire) glEnable (GL_DEPTH_TEST);
419 /* There's a disc with a hole in it around the screen, to act as a mask
420 preventing slightly off-screen bogies from showing up. This clips 'em.
423 draw_table (ModeInfo *mi)
425 /*sonar_configuration *sp = &sps[MI_SCREEN(mi)];*/
426 int wire = MI_IS_WIREFRAME(mi);
429 int th_steps = 36 * 4; /* same as in draw_screen */
431 static const GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
432 static const GLfloat text[4] = {0.15, 0.15, 0.15, 1.0};
433 static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0};
434 static const GLfloat shiny = 0.0;
438 glPushAttrib (GL_ENABLE_BIT);
439 glDisable (GL_TEXTURE_2D);
441 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
442 glMateriali (GL_FRONT, GL_SHININESS, shiny);
443 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
445 glFrontFace (GL_CCW);
446 glBegin(wire ? GL_LINES : GL_QUAD_STRIP);
447 glNormal3f (0, 0, 1);
448 for (i = 0; i <= th_steps; i++)
450 double a = M_PI * 2 * i / th_steps;
453 glVertex3f (x, y, 0);
454 glVertex3f (x*10, y*10, 0);
460 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, text);
461 glTranslatef (0, 0, 0.01);
462 for (i = 0; i < 360; i += 10)
465 GLfloat a = M_PI/2 - (i / 180.0 * M_PI);
466 sprintf (buf, "%d", i);
467 polys += draw_text (mi, buf, 1.07, a, 0, 10.0);
475 draw_bogies (ModeInfo *mi)
477 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
481 for (b = sp->displayed; b; b = b->next)
484 malloc (strlen (b->name) + (b->desc ? strlen(b->desc) : 0) + 3);
491 polys += draw_text (mi, s, b->r, b->th, b->ttl, -1);
494 /* Move *very slightly* forward so that the text is not all in the
495 same plane: this prevents flickering with overlapping text as
496 the textures fight for priority. */
497 glTranslatef(0, 0, 0.00002);
504 /* called from sonar-sim.c and sonar-icmp.c */
506 copy_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
508 sonar_bogie *b2 = (sonar_bogie *) calloc (1, sizeof(*b2));
509 b2->name = strdup (b->name);
510 b2->desc = b->desc ? strdup (b->desc) : 0;
514 /* does not copy b->closure */
516 /* Take this opportunity to normalize 'th' to the range [0-2pi). */
517 while (b2->th < 0) b2->th += M_PI*2;
518 while (b2->th >= M_PI*2) b2->th -= M_PI*2;
524 /* called from sonar-icmp.c */
526 free_bogie (sonar_sensor_data *ssd, sonar_bogie *b)
529 ssd->free_bogie_cb (ssd, b->closure);
531 if (b->desc) free (b->desc);
535 /* removes it from the list and frees it
538 delete_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
539 sonar_bogie **from_list)
541 sonar_bogie *ob, *prev;
542 for (prev = 0, ob = *from_list; ob; prev = ob, ob = ob->next)
546 prev->next = b->next;
548 (*from_list) = b->next;
555 /* copies the bogie and adds it to the list.
556 if there's another bogie there with the same name, frees that one.
559 copy_and_insert_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
560 sonar_bogie **to_list)
562 sonar_bogie *ob, *prev;
564 for (prev = 0, ob = *to_list; ob; prev = ob, ob = ob->next)
566 if (ob == b) abort(); /* this will end badly */
567 if (!strcmp (ob->name, b->name)) /* match! */
569 delete_bogie (ssd, ob, to_list);
574 b = copy_bogie (ssd, b);
581 update_sensor_data (sonar_configuration *sp)
583 sonar_bogie *new_list = sp->ssd->scan_cb (sp->ssd);
586 /* If a bogie exists in 'new_list' but not 'pending', add it.
587 If a bogie exists in both, update it in 'pending'.
589 for (b2 = new_list; b2; b2 = b2->next)
592 fprintf (stderr, "%s: updated: %s (%5.2f %5.2f %5.2f)\n",
593 progname, b2->name, b2->r, b2->th, b2->ttl);
594 copy_and_insert_bogie (sp->ssd, b2, &sp->pending);
596 if (debug_p > 2) fprintf (stderr, "\n");
600 /* Returns whether the given angle lies between two other angles.
601 When those angles cross 0, it assumes the wedge is the smaller one.
602 That is: 5 lies between 10 and 350 degrees (a 20 degree wedge).
605 point_in_wedge (GLfloat th, GLfloat low, GLfloat high)
608 return (th > low && th <= high);
610 return (th <= high || th > low);
614 /* Returns the current time in seconds as a double.
620 # ifdef GETTIMEOFDAY_TWO_ARGS
622 gettimeofday(&now, &tzp);
627 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
632 sweep (sonar_configuration *sp)
636 /* Move the sweep forward (clockwise).
638 GLfloat prev_sweep, this_sweep, tick;
639 GLfloat cycle_secs = 30 / speed; /* default to one cycle every N seconds */
640 this_sweep = ((cycle_secs - fmod (double_time() - sp->start_time +
645 prev_sweep = sp->sweep_th;
646 tick = prev_sweep - this_sweep;
647 while (tick < 0) tick += M_PI*2;
649 sp->sweep_th = this_sweep;
651 if (this_sweep < 0 || this_sweep >= M_PI*2) abort();
652 if (prev_sweep < 0) /* skip first time */
655 if (tick < 0 || tick >= M_PI*2) abort();
658 /* Go through the 'pending' sensor data, find those bogies who are
659 just now being swept, and move them from 'pending' to 'displayed'.
660 (Leave bogies that have not yet been swept alone: we'll get to
661 them when the sweep moves forward.)
666 sonar_bogie *next = b->next;
667 if (point_in_wedge (b->th, this_sweep, prev_sweep))
670 time_t t = time((time_t *) 0);
672 "%s: sweep hit: %02d:%02d: %s: (%5.2f %5.2f %5.2f;"
673 " th=[%.2f < %.2f <= %.2f])\n",
675 (int) (t / 60) % 60, (int) t % 60,
676 b->name, b->r, b->th, b->ttl,
677 this_sweep, b->th, prev_sweep);
680 copy_and_insert_bogie (sp->ssd, b, &sp->displayed);
681 delete_bogie (sp->ssd, b, &sp->pending);
687 /* Update TTL on all currently-displayed bogies; delete the dead.
689 Request sensor updates on the ones just now being swept.
691 Any updates go into 'pending' and might not show up until
692 the next time the sweep comes around. This is to prevent
693 already-drawn bogies from jumping to a new position without
694 having faded out first.
699 sonar_bogie *next = b->next;
705 fprintf (stderr, "%s: TTL expired: %s (%5.2f %5.2f %5.2f)\n",
706 progname, b->name, b->r, b->th, b->ttl);
707 delete_bogie (sp->ssd, b, &sp->displayed);
712 update_sensor_data (sp);
717 draw_startup_blurb (ModeInfo *mi)
719 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
720 const char *msg = (sp->error ? sp->error : "Resolving hosts...");
721 static const GLfloat color[4] = {0, 1, 0, 1};
723 if (!sp->error && ping_arg && !strcmp (ping_arg, "simulation"))
724 return; /* don't bother */
726 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
727 glTranslatef (0, 0, 0.3);
728 draw_text (mi, msg, 0, 0, 0, 30.0);
730 /* only leave error message up for N seconds */
732 sp->start_time + 4 < double_time())
740 /* Window management, etc
743 reshape_sonar (ModeInfo *mi, int width, int height)
745 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
746 GLfloat h = (GLfloat) height / (GLfloat) width;
748 glViewport (0, 0, (GLint) width, (GLint) height);
750 glMatrixMode(GL_PROJECTION);
752 gluPerspective (30.0, 1/h, 1.0, 100.0);
754 glMatrixMode(GL_MODELVIEW);
756 gluLookAt( 0.0, 0.0, 30.0,
760 glClear(GL_COLOR_BUFFER_BIT);
762 sp->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
767 sonar_handle_event (ModeInfo *mi, XEvent *event)
769 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
771 if (event->xany.type == ButtonPress &&
772 event->xbutton.button == Button1)
774 sp->button_down_p = True;
775 gltrackball_start (sp->trackball,
776 event->xbutton.x, event->xbutton.y,
777 MI_WIDTH (mi), MI_HEIGHT (mi));
780 else if (event->xany.type == ButtonRelease &&
781 event->xbutton.button == Button1)
783 sp->button_down_p = False;
786 else if (event->xany.type == ButtonPress &&
787 (event->xbutton.button == Button4 ||
788 event->xbutton.button == Button5 ||
789 event->xbutton.button == Button6 ||
790 event->xbutton.button == Button7))
792 gltrackball_mousewheel (sp->trackball, event->xbutton.button, 10,
793 !!event->xbutton.state);
796 else if (event->xany.type == MotionNotify &&
799 gltrackball_track (sp->trackball,
800 event->xmotion.x, event->xmotion.y,
801 MI_WIDTH (mi), MI_HEIGHT (mi));
810 init_sonar (ModeInfo *mi)
812 sonar_configuration *sp;
813 int wire = MI_IS_WIREFRAME(mi);
816 sps = (sonar_configuration *)
817 calloc (MI_NUM_SCREENS(mi), sizeof (sonar_configuration));
819 fprintf(stderr, "%s: out of memory\n", progname);
823 sp = &sps[MI_SCREEN(mi)];
824 sp->glx_context = init_GL(mi);
826 reshape_sonar (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
830 GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
831 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
832 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
833 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
835 glEnable(GL_TEXTURE_2D);
836 glEnable(GL_LIGHTING);
838 glEnable(GL_CULL_FACE);
839 glEnable(GL_DEPTH_TEST);
840 glEnable(GL_NORMALIZE);
841 glEnable(GL_LINE_SMOOTH);
843 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
844 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
845 glShadeModel(GL_SMOOTH);
847 glLightfv(GL_LIGHT0, GL_POSITION, pos);
848 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
849 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
850 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
853 sp->trackball = gltrackball_init ();
854 sp->rot = make_rotator (0, 0, 0, 0, speed * 0.003, True);
856 sp->texfont = load_texture_font (MI_DISPLAY(mi), "font");
857 check_gl_error ("loading font");
859 sp->table_list = glGenLists (1);
860 glNewList (sp->table_list, GL_COMPILE);
861 sp->table_polys = draw_table (mi);
864 sp->screen_list = glGenLists (1);
865 glNewList (sp->screen_list, GL_COMPILE);
866 sp->screen_polys = draw_screen (mi, False, False);
869 sp->grid_list = glGenLists (1);
870 glNewList (sp->grid_list, GL_COMPILE);
871 sp->grid_polys = draw_screen (mi, True, False);
874 sp->sweep_list = glGenLists (1);
875 glNewList (sp->sweep_list, GL_COMPILE);
876 sp->sweep_polys = draw_screen (mi, False, True);
879 sp->start_time = double_time ();
880 sp->sweep_offset = random() % 60;
886 init_sensor (ModeInfo *mi)
888 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
890 if (sp->ssd) abort();
892 if (!ping_arg || !*ping_arg ||
893 !strcmp(ping_arg, "default") ||
894 !!strcmp (ping_arg, "simulation"))
895 sp->ssd = init_ping (MI_DISPLAY (mi), &sp->error, ping_arg,
896 ping_timeout, resolve_p, times_p, debug_p);
898 /* Disavow privs. This was already done in init_ping(), but
899 we might not have called that at all, so do it again. */
903 sp->ssd = init_simulation (MI_DISPLAY (mi), &sp->error,
904 team_a_name, team_b_name,
905 team_a_count, team_b_count,
913 draw_sonar (ModeInfo *mi)
915 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
916 Display *dpy = MI_DISPLAY(mi);
917 Window window = MI_WINDOW(mi);
919 if (!sp->glx_context)
922 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
924 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
927 { GLfloat s = 7; glScalef (s,s,s); }
929 gltrackball_rotate (sp->trackball);
935 get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
936 glRotatef (max/2 - x*max, 1, 0, 0);
937 glRotatef (max/2 - z*max, 0, 1, 0);
940 mi->polygon_count = 0;
942 glPushMatrix(); /* table */
943 glCallList (sp->table_list);
944 mi->polygon_count += sp->table_polys;
947 glPushMatrix(); /* text */
948 glTranslatef (0, 0, -0.01);
949 mi->polygon_count += draw_bogies (mi);
952 glCallList (sp->screen_list); /* glass */
953 mi->polygon_count += sp->screen_polys;
955 glTranslatef (0, 0, 0.004); /* sweep */
957 glRotatef ((sp->sweep_th * 180 / M_PI), 0, 0, 1);
958 if (sp->sweep_th >= 0)
959 glCallList (sp->sweep_list);
960 mi->polygon_count += sp->sweep_polys;
963 glTranslatef (0, 0, 0.004); /* grid */
964 glCallList (sp->grid_list);
965 mi->polygon_count += sp->screen_polys;
967 if (! sp->ssd || sp->error)
968 draw_startup_blurb(mi);
972 if (mi->fps_p) do_fps (mi);
975 glXSwapBuffers(dpy, window);
978 /* Just starting up. "Resolving hosts" text printed. Go stall. */
985 release_sonar (ModeInfo *mi)
988 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
989 sonar_bogie *b = sp->displayed;
992 sonar_bogie *next = b->next;
993 free_bogie (sp->ssd, b);
1001 sonar_bogie *next = b->next;
1002 free_bogie (sp->ssd, b);
1007 sp->ssd->free_data_cb (sp->ssd, sp->ssd->closure);
1013 XSCREENSAVER_MODULE ("Sonar", sonar)