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, *next;
564 for (ob = *to_list, next = ob ? ob->next : 0;
566 ob = next, next = ob ? ob->next : 0)
568 if (ob == b) abort(); /* this will end badly */
569 if (!strcmp (ob->name, b->name)) /* match! */
571 delete_bogie (ssd, ob, to_list);
576 b = copy_bogie (ssd, b);
583 update_sensor_data (sonar_configuration *sp)
585 sonar_bogie *new_list = sp->ssd->scan_cb (sp->ssd);
588 /* If a bogie exists in 'new_list' but not 'pending', add it.
589 If a bogie exists in both, update it in 'pending'.
591 for (b2 = new_list; b2; b2 = b2->next)
594 fprintf (stderr, "%s: updated: %s (%5.2f %5.2f %5.2f)\n",
595 progname, b2->name, b2->r, b2->th, b2->ttl);
596 copy_and_insert_bogie (sp->ssd, b2, &sp->pending);
598 if (debug_p > 2) fprintf (stderr, "\n");
602 /* Returns whether the given angle lies between two other angles.
603 When those angles cross 0, it assumes the wedge is the smaller one.
604 That is: 5 lies between 10 and 350 degrees (a 20 degree wedge).
607 point_in_wedge (GLfloat th, GLfloat low, GLfloat high)
610 return (th > low && th <= high);
612 return (th <= high || th > low);
616 /* Returns the current time in seconds as a double.
622 # ifdef GETTIMEOFDAY_TWO_ARGS
624 gettimeofday(&now, &tzp);
629 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
634 sweep (sonar_configuration *sp)
638 /* Move the sweep forward (clockwise).
640 GLfloat prev_sweep, this_sweep, tick;
641 GLfloat cycle_secs = 30 / speed; /* default to one cycle every N seconds */
642 this_sweep = ((cycle_secs - fmod (double_time() - sp->start_time +
647 prev_sweep = sp->sweep_th;
648 tick = prev_sweep - this_sweep;
649 while (tick < 0) tick += M_PI*2;
651 sp->sweep_th = this_sweep;
653 if (this_sweep < 0 || this_sweep >= M_PI*2) abort();
654 if (prev_sweep < 0) /* skip first time */
657 if (tick < 0 || tick >= M_PI*2) abort();
660 /* Go through the 'pending' sensor data, find those bogies who are
661 just now being swept, and move them from 'pending' to 'displayed'.
662 (Leave bogies that have not yet been swept alone: we'll get to
663 them when the sweep moves forward.)
668 sonar_bogie *next = b->next;
669 if (point_in_wedge (b->th, this_sweep, prev_sweep))
672 time_t t = time((time_t *) 0);
674 "%s: sweep hit: %02d:%02d: %s: (%5.2f %5.2f %5.2f;"
675 " th=[%.2f < %.2f <= %.2f])\n",
677 (int) (t / 60) % 60, (int) t % 60,
678 b->name, b->r, b->th, b->ttl,
679 this_sweep, b->th, prev_sweep);
682 copy_and_insert_bogie (sp->ssd, b, &sp->displayed);
683 delete_bogie (sp->ssd, b, &sp->pending);
689 /* Update TTL on all currently-displayed bogies; delete the dead.
691 Request sensor updates on the ones just now being swept.
693 Any updates go into 'pending' and might not show up until
694 the next time the sweep comes around. This is to prevent
695 already-drawn bogies from jumping to a new position without
696 having faded out first.
701 sonar_bogie *next = b->next;
707 fprintf (stderr, "%s: TTL expired: %s (%5.2f %5.2f %5.2f)\n",
708 progname, b->name, b->r, b->th, b->ttl);
709 delete_bogie (sp->ssd, b, &sp->displayed);
714 update_sensor_data (sp);
719 draw_startup_blurb (ModeInfo *mi)
721 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
722 const char *msg = (sp->error ? sp->error : "Resolving hosts...");
723 static const GLfloat color[4] = {0, 1, 0, 1};
725 if (!sp->error && ping_arg && !strcmp (ping_arg, "simulation"))
726 return; /* don't bother */
728 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
729 glTranslatef (0, 0, 0.3);
730 draw_text (mi, msg, 0, 0, 0, 30.0);
732 /* only leave error message up for N seconds */
734 sp->start_time + 4 < double_time())
742 /* Window management, etc
745 reshape_sonar (ModeInfo *mi, int width, int height)
747 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
748 GLfloat h = (GLfloat) height / (GLfloat) width;
750 glViewport (0, 0, (GLint) width, (GLint) height);
752 glMatrixMode(GL_PROJECTION);
754 gluPerspective (30.0, 1/h, 1.0, 100.0);
756 glMatrixMode(GL_MODELVIEW);
758 gluLookAt( 0.0, 0.0, 30.0,
762 glClear(GL_COLOR_BUFFER_BIT);
764 sp->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
769 sonar_handle_event (ModeInfo *mi, XEvent *event)
771 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
773 if (event->xany.type == ButtonPress &&
774 event->xbutton.button == Button1)
776 sp->button_down_p = True;
777 gltrackball_start (sp->trackball,
778 event->xbutton.x, event->xbutton.y,
779 MI_WIDTH (mi), MI_HEIGHT (mi));
782 else if (event->xany.type == ButtonRelease &&
783 event->xbutton.button == Button1)
785 sp->button_down_p = False;
788 else if (event->xany.type == ButtonPress &&
789 (event->xbutton.button == Button4 ||
790 event->xbutton.button == Button5 ||
791 event->xbutton.button == Button6 ||
792 event->xbutton.button == Button7))
794 gltrackball_mousewheel (sp->trackball, event->xbutton.button, 10,
795 !!event->xbutton.state);
798 else if (event->xany.type == MotionNotify &&
801 gltrackball_track (sp->trackball,
802 event->xmotion.x, event->xmotion.y,
803 MI_WIDTH (mi), MI_HEIGHT (mi));
812 init_sonar (ModeInfo *mi)
814 sonar_configuration *sp;
815 int wire = MI_IS_WIREFRAME(mi);
818 sps = (sonar_configuration *)
819 calloc (MI_NUM_SCREENS(mi), sizeof (sonar_configuration));
821 fprintf(stderr, "%s: out of memory\n", progname);
825 sp = &sps[MI_SCREEN(mi)];
826 sp->glx_context = init_GL(mi);
828 reshape_sonar (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
832 GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
833 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
834 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
835 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
837 glEnable(GL_TEXTURE_2D);
838 glEnable(GL_LIGHTING);
840 glEnable(GL_CULL_FACE);
841 glEnable(GL_DEPTH_TEST);
842 glEnable(GL_NORMALIZE);
843 glEnable(GL_LINE_SMOOTH);
845 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
846 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
847 glShadeModel(GL_SMOOTH);
849 glLightfv(GL_LIGHT0, GL_POSITION, pos);
850 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
851 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
852 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
855 sp->trackball = gltrackball_init ();
856 sp->rot = make_rotator (0, 0, 0, 0, speed * 0.003, True);
858 sp->texfont = load_texture_font (MI_DISPLAY(mi), "font");
859 check_gl_error ("loading font");
861 sp->table_list = glGenLists (1);
862 glNewList (sp->table_list, GL_COMPILE);
863 sp->table_polys = draw_table (mi);
866 sp->screen_list = glGenLists (1);
867 glNewList (sp->screen_list, GL_COMPILE);
868 sp->screen_polys = draw_screen (mi, False, False);
871 sp->grid_list = glGenLists (1);
872 glNewList (sp->grid_list, GL_COMPILE);
873 sp->grid_polys = draw_screen (mi, True, False);
876 sp->sweep_list = glGenLists (1);
877 glNewList (sp->sweep_list, GL_COMPILE);
878 sp->sweep_polys = draw_screen (mi, False, True);
881 sp->start_time = double_time ();
882 sp->sweep_offset = random() % 60;
888 init_sensor (ModeInfo *mi)
890 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
892 if (sp->ssd) abort();
894 if (!ping_arg || !*ping_arg ||
895 !strcmp(ping_arg, "default") ||
896 !!strcmp (ping_arg, "simulation"))
897 sp->ssd = init_ping (MI_DISPLAY (mi), &sp->error, ping_arg,
898 ping_timeout, resolve_p, times_p, debug_p);
900 /* Disavow privs. This was already done in init_ping(), but
901 we might not have called that at all, so do it again. */
905 sp->ssd = init_simulation (MI_DISPLAY (mi), &sp->error,
906 team_a_name, team_b_name,
907 team_a_count, team_b_count,
915 draw_sonar (ModeInfo *mi)
917 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
918 Display *dpy = MI_DISPLAY(mi);
919 Window window = MI_WINDOW(mi);
921 if (!sp->glx_context)
924 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
926 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
929 { GLfloat s = 7; glScalef (s,s,s); }
931 gltrackball_rotate (sp->trackball);
937 get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
938 glRotatef (max/2 - x*max, 1, 0, 0);
939 glRotatef (max/2 - z*max, 0, 1, 0);
942 mi->polygon_count = 0;
944 glPushMatrix(); /* table */
945 glCallList (sp->table_list);
946 mi->polygon_count += sp->table_polys;
949 glPushMatrix(); /* text */
950 glTranslatef (0, 0, -0.01);
951 mi->polygon_count += draw_bogies (mi);
954 glCallList (sp->screen_list); /* glass */
955 mi->polygon_count += sp->screen_polys;
957 glTranslatef (0, 0, 0.004); /* sweep */
959 glRotatef ((sp->sweep_th * 180 / M_PI), 0, 0, 1);
960 if (sp->sweep_th >= 0)
961 glCallList (sp->sweep_list);
962 mi->polygon_count += sp->sweep_polys;
965 glTranslatef (0, 0, 0.004); /* grid */
966 glCallList (sp->grid_list);
967 mi->polygon_count += sp->screen_polys;
969 if (! sp->ssd || sp->error)
970 draw_startup_blurb(mi);
974 if (mi->fps_p) do_fps (mi);
977 glXSwapBuffers(dpy, window);
980 /* Just starting up. "Resolving hosts" text printed. Go stall. */
987 release_sonar (ModeInfo *mi)
990 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
991 sonar_bogie *b = sp->displayed;
994 sonar_bogie *next = b->next;
995 free_bogie (sp->ssd, b);
1003 sonar_bogie *next = b->next;
1004 free_bogie (sp->ssd, b);
1009 sp->ssd->free_data_cb (sp->ssd, sp->ssd->closure);
1015 XSCREENSAVER_MODULE ("Sonar", sonar)