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;
239 z0 = cos (curvature/2 * asin (r0)) / 2 - zoff;
240 z1 = cos (curvature/2 * asin (r1)) / 2 - zoff;
242 glBegin(wire || mesh_p ? GL_LINES : GL_QUAD_STRIP);
243 for (j0 = 0; j0 <= th_steps; j0++)
247 ? (j0 % th_skip != 0)
248 : (j0 % th_skip2 != 0)))
254 GLfloat r = 1 - (j0 / (GLfloat) (th_steps * sweep_size));
256 color[0] = glass[0] + (sweepc[0] - glass[0]) * r;
257 color[1] = glass[1] + (sweepc[1] - glass[1]) * r;
258 color[2] = glass[2] + (sweepc[2] - glass[2]) * r;
261 color[0] = sweepc[0];
262 color[1] = sweepc[1];
263 color[2] = sweepc[2];
266 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
270 glNormal3f (r0 * ring[j1].x, r0 * ring[j1].y, z0);
271 glVertex3f (r0 * ring[j1].x, r0 * ring[j1].y, z0);
272 glNormal3f (r1 * ring[j1].x, r1 * ring[j1].y, z1);
273 glVertex3f (r1 * ring[j1].x, r1 * ring[j1].y, z1);
276 if (sweep_p && j0 >= th_steps * sweep_size)
287 i < r_steps - r_skip)))
289 glBegin(GL_LINE_LOOP);
290 for (j0 = 0; j0 < th_steps; j0++)
292 glNormal3f (r0 * ring[j0].x, r0 * ring[j0].y, z0);
293 glVertex3f (r0 * ring[j0].x, r0 * ring[j0].y, z0);
300 /* one more polygon for the middle */
301 if (!wire && !sweep_p)
303 glBegin(wire || mesh_p ? GL_LINE_LOOP : GL_POLYGON);
304 glNormal3f (0, 0, 1);
305 for (i = 0; i < th_steps; i++)
307 glNormal3f (r0 * ring[i].x, r0 * ring[i].y, z0);
308 glVertex3f (r0 * ring[i].x, r0 * ring[i].y, z0);
322 draw_text (ModeInfo *mi, const char *string, GLfloat r, GLfloat th,
323 GLfloat ttl, GLfloat size)
325 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
326 int wire = MI_IS_WIREFRAME(mi);
328 GLfloat font_scale = 0.001 * (size > 0 ? size : font_size) / 14.0;
329 int lines = 0, max_w = 0, lh = 0;
330 char *string2 = strdup (string);
331 char *token = string2;
335 if (size <= 0) /* if size not specified, draw in yellow with alpha */
340 color[3] = (ttl / (M_PI * 2)) * 1.2;
341 if (color[3] > 1) color[3] = 1;
343 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
345 glColor3f (color[0]*color[3], color[1]*color[3], color[2]*color[3]);
348 while ((line = strtok (token, "\r\n")))
350 int w = texture_string_width (sp->texfont, line, &lh);
351 if (w > max_w) max_w = w;
357 glTranslatef (r * cos (th), r * sin(th), 0);
358 glScalef (font_scale, font_scale, font_scale);
360 if (size <= 0) /* Draw the dot */
362 GLfloat s = font_size * 1.7;
363 glDisable (GL_TEXTURE_2D);
365 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
366 glVertex3f (0, s, 0);
367 glVertex3f (s, s, 0);
368 glVertex3f (s, 0, 0);
369 glVertex3f (0, 0, 0);
371 glTranslatef (-max_w/2, -lh, 0);
374 glTranslatef (-max_w/2, -lh/2, 0);
376 /* draw each line, centered */
377 if (! wire) glEnable (GL_TEXTURE_2D);
379 string2 = strdup (string);
381 while ((line = strtok (token, "\r\n")))
383 int w = texture_string_width (sp->texfont, line, 0);
385 glTranslatef ((max_w-w)/2, 0, 0);
389 glBegin (GL_LINE_LOOP);
390 glVertex3f (0, 0, 0);
391 glVertex3f (w, 0, 0);
392 glVertex3f (w, lh, 0);
393 glVertex3f (0, lh, 0);
399 print_texture_string (sp->texfont, line);
402 glTranslatef (0, -lh, 0);
410 if (! wire) glEnable (GL_DEPTH_TEST);
416 /* There's a disc with a hole in it around the screen, to act as a mask
417 preventing slightly off-screen bogies from showing up. This clips 'em.
420 draw_table (ModeInfo *mi)
422 /*sonar_configuration *sp = &sps[MI_SCREEN(mi)];*/
423 int wire = MI_IS_WIREFRAME(mi);
426 int th_steps = 36 * 4; /* same as in draw_screen */
428 static const GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
429 static const GLfloat text[4] = {0.15, 0.15, 0.15, 1.0};
430 static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0};
431 static const GLfloat shiny = 0.0;
435 glPushAttrib (GL_ENABLE_BIT);
436 glDisable (GL_TEXTURE_2D);
438 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
439 glMateriali (GL_FRONT, GL_SHININESS, shiny);
440 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
442 glFrontFace (GL_CCW);
443 glBegin(wire ? GL_LINES : GL_QUAD_STRIP);
444 glNormal3f (0, 0, 1);
445 for (i = 0; i <= th_steps; i++)
447 double a = M_PI * 2 * i / th_steps;
450 glVertex3f (x, y, 0);
451 glVertex3f (x*10, y*10, 0);
457 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, text);
458 glTranslatef (0, 0, 0.01);
459 for (i = 0; i < 360; i += 10)
462 GLfloat a = M_PI/2 - (i / 180.0 * M_PI);
463 sprintf (buf, "%d", i);
464 polys += draw_text (mi, buf, 1.07, a, 0, 10.0);
472 draw_bogies (ModeInfo *mi)
474 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
478 for (b = sp->displayed; b; b = b->next)
481 malloc (strlen (b->name) + (b->desc ? strlen(b->desc) : 0) + 3);
488 polys += draw_text (mi, s, b->r, b->th, b->ttl, -1);
491 /* Move *very slightly* forward so that the text is not all in the
492 same plane: this prevents flickering with overlapping text as
493 the textures fight for priority. */
494 glTranslatef(0, 0, 0.00002);
501 /* called from sonar-sim.c and sonar-icmp.c */
503 copy_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
505 sonar_bogie *b2 = (sonar_bogie *) calloc (1, sizeof(*b2));
506 b2->name = strdup (b->name);
507 b2->desc = b->desc ? strdup (b->desc) : 0;
511 /* does not copy b->closure */
513 /* Take this opportunity to normalize 'th' to the range [0-2pi). */
514 while (b2->th < 0) b2->th += M_PI*2;
515 while (b2->th >= M_PI*2) b2->th -= M_PI*2;
521 /* called from sonar-icmp.c */
523 free_bogie (sonar_sensor_data *ssd, sonar_bogie *b)
526 ssd->free_bogie_cb (ssd, b->closure);
528 if (b->desc) free (b->desc);
532 /* removes it from the list and frees it
535 delete_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
536 sonar_bogie **from_list)
538 sonar_bogie *ob, *prev;
539 for (prev = 0, ob = *from_list; ob; prev = ob, ob = ob->next)
543 prev->next = b->next;
545 (*from_list) = b->next;
552 /* copies the bogie and adds it to the list.
553 if there's another bogie there with the same name, frees that one.
556 copy_and_insert_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
557 sonar_bogie **to_list)
559 sonar_bogie *ob, *prev;
561 for (prev = 0, ob = *to_list; ob; prev = ob, ob = ob->next)
563 if (ob == b) abort(); /* this will end badly */
564 if (!strcmp (ob->name, b->name)) /* match! */
566 delete_bogie (ssd, ob, to_list);
571 b = copy_bogie (ssd, b);
578 update_sensor_data (sonar_configuration *sp)
580 sonar_bogie *new_list = sp->ssd->scan_cb (sp->ssd);
583 /* If a bogie exists in 'new_list' but not 'pending', add it.
584 If a bogie exists in both, update it in 'pending'.
586 for (b2 = new_list; b2; b2 = b2->next)
589 fprintf (stderr, "%s: updated: %s (%5.2f %5.2f %5.2f)\n",
590 progname, b2->name, b2->r, b2->th, b2->ttl);
591 copy_and_insert_bogie (sp->ssd, b2, &sp->pending);
593 if (debug_p > 2) fprintf (stderr, "\n");
597 /* Returns whether the given angle lies between two other angles.
598 When those angles cross 0, it assumes the wedge is the smaller one.
599 That is: 5 lies between 10 and 350 degrees (a 20 degree wedge).
602 point_in_wedge (GLfloat th, GLfloat low, GLfloat high)
605 return (th > low && th <= high);
607 return (th <= high || th > low);
611 /* Returns the current time in seconds as a double.
617 # ifdef GETTIMEOFDAY_TWO_ARGS
619 gettimeofday(&now, &tzp);
624 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
629 sweep (sonar_configuration *sp)
633 /* Move the sweep forward (clockwise).
635 GLfloat prev_sweep, this_sweep, tick;
636 GLfloat cycle_secs = 30 / speed; /* default to one cycle every N seconds */
637 this_sweep = ((cycle_secs - fmod (double_time() - sp->start_time +
642 prev_sweep = sp->sweep_th;
643 tick = prev_sweep - this_sweep;
644 while (tick < 0) tick += M_PI*2;
646 sp->sweep_th = this_sweep;
648 if (this_sweep < 0 || this_sweep >= M_PI*2) abort();
649 if (prev_sweep < 0) /* skip first time */
652 if (tick < 0 || tick >= M_PI*2) abort();
655 /* Go through the 'pending' sensor data, find those bogies who are
656 just now being swept, and move them from 'pending' to 'displayed'.
657 (Leave bogies that have not yet been swept alone: we'll get to
658 them when the sweep moves forward.)
663 sonar_bogie *next = b->next;
664 if (point_in_wedge (b->th, this_sweep, prev_sweep))
667 time_t t = time((time_t *) 0);
669 "%s: sweep hit: %02d:%02d: %s: (%5.2f %5.2f %5.2f;"
670 " th=[%.2f < %.2f <= %.2f])\n",
672 (int) (t / 60) % 60, (int) t % 60,
673 b->name, b->r, b->th, b->ttl,
674 this_sweep, b->th, prev_sweep);
677 copy_and_insert_bogie (sp->ssd, b, &sp->displayed);
678 delete_bogie (sp->ssd, b, &sp->pending);
684 /* Update TTL on all currently-displayed bogies; delete the dead.
686 Request sensor updates on the ones just now being swept.
688 Any updates go into 'pending' and might not show up until
689 the next time the sweep comes around. This is to prevent
690 already-drawn bogies from jumping to a new position without
691 having faded out first.
696 sonar_bogie *next = b->next;
702 fprintf (stderr, "%s: TTL expired: %s (%5.2f %5.2f %5.2f)\n",
703 progname, b->name, b->r, b->th, b->ttl);
704 delete_bogie (sp->ssd, b, &sp->displayed);
709 update_sensor_data (sp);
714 draw_startup_blurb (ModeInfo *mi)
716 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
717 const char *msg = (sp->error ? sp->error : "Resolving hosts...");
718 static const GLfloat color[4] = {0, 1, 0, 1};
720 if (!sp->error && ping_arg && !strcmp (ping_arg, "simulation"))
721 return; /* don't bother */
723 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
724 glTranslatef (0, 0, 0.3);
725 draw_text (mi, msg, 0, 0, 0, 30.0);
727 /* only leave error message up for N seconds */
729 sp->start_time + 4 < double_time())
737 /* Window management, etc
740 reshape_sonar (ModeInfo *mi, int width, int height)
742 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
743 GLfloat h = (GLfloat) height / (GLfloat) width;
745 glViewport (0, 0, (GLint) width, (GLint) height);
747 glMatrixMode(GL_PROJECTION);
749 gluPerspective (30.0, 1/h, 1.0, 100.0);
751 glMatrixMode(GL_MODELVIEW);
753 gluLookAt( 0.0, 0.0, 30.0,
757 glClear(GL_COLOR_BUFFER_BIT);
759 sp->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
764 sonar_handle_event (ModeInfo *mi, XEvent *event)
766 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
768 if (event->xany.type == ButtonPress &&
769 event->xbutton.button == Button1)
771 sp->button_down_p = True;
772 gltrackball_start (sp->trackball,
773 event->xbutton.x, event->xbutton.y,
774 MI_WIDTH (mi), MI_HEIGHT (mi));
777 else if (event->xany.type == ButtonRelease &&
778 event->xbutton.button == Button1)
780 sp->button_down_p = False;
783 else if (event->xany.type == ButtonPress &&
784 (event->xbutton.button == Button4 ||
785 event->xbutton.button == Button5 ||
786 event->xbutton.button == Button6 ||
787 event->xbutton.button == Button7))
789 gltrackball_mousewheel (sp->trackball, event->xbutton.button, 10,
790 !!event->xbutton.state);
793 else if (event->xany.type == MotionNotify &&
796 gltrackball_track (sp->trackball,
797 event->xmotion.x, event->xmotion.y,
798 MI_WIDTH (mi), MI_HEIGHT (mi));
807 init_sonar (ModeInfo *mi)
809 sonar_configuration *sp;
810 int wire = MI_IS_WIREFRAME(mi);
813 sps = (sonar_configuration *)
814 calloc (MI_NUM_SCREENS(mi), sizeof (sonar_configuration));
816 fprintf(stderr, "%s: out of memory\n", progname);
820 sp = &sps[MI_SCREEN(mi)];
821 sp->glx_context = init_GL(mi);
823 reshape_sonar (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
827 GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
828 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
829 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
830 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
832 glEnable(GL_TEXTURE_2D);
833 glEnable(GL_LIGHTING);
835 glEnable(GL_CULL_FACE);
836 glEnable(GL_DEPTH_TEST);
837 glEnable(GL_NORMALIZE);
838 glEnable(GL_LINE_SMOOTH);
840 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
841 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
842 glShadeModel(GL_SMOOTH);
844 glLightfv(GL_LIGHT0, GL_POSITION, pos);
845 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
846 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
847 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
850 sp->trackball = gltrackball_init ();
851 sp->rot = make_rotator (0, 0, 0, 0, speed * 0.003, True);
853 sp->texfont = load_texture_font (MI_DISPLAY(mi), "font");
854 check_gl_error ("loading font");
856 sp->table_list = glGenLists (1);
857 glNewList (sp->table_list, GL_COMPILE);
858 sp->table_polys = draw_table (mi);
861 sp->screen_list = glGenLists (1);
862 glNewList (sp->screen_list, GL_COMPILE);
863 sp->screen_polys = draw_screen (mi, False, False);
866 sp->grid_list = glGenLists (1);
867 glNewList (sp->grid_list, GL_COMPILE);
868 sp->grid_polys = draw_screen (mi, True, False);
871 sp->sweep_list = glGenLists (1);
872 glNewList (sp->sweep_list, GL_COMPILE);
873 sp->sweep_polys = draw_screen (mi, False, True);
876 sp->start_time = double_time ();
877 sp->sweep_offset = random() % 60;
883 init_sensor (ModeInfo *mi)
885 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
887 if (sp->ssd) abort();
889 if (!ping_arg || !*ping_arg ||
890 !strcmp(ping_arg, "default") ||
891 !!strcmp (ping_arg, "simulation"))
892 sp->ssd = init_ping (MI_DISPLAY (mi), &sp->error, ping_arg,
893 ping_timeout, resolve_p, times_p, debug_p);
895 /* Disavow privs. This was already done in init_ping(), but
896 we might not have called that at all, so do it again. */
900 sp->ssd = init_simulation (MI_DISPLAY (mi), &sp->error,
901 team_a_name, team_b_name,
902 team_a_count, team_b_count,
910 draw_sonar (ModeInfo *mi)
912 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
913 Display *dpy = MI_DISPLAY(mi);
914 Window window = MI_WINDOW(mi);
916 if (!sp->glx_context)
919 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
921 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
924 { GLfloat s = 7; glScalef (s,s,s); }
926 gltrackball_rotate (sp->trackball);
932 get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
933 glRotatef (max/2 - x*max, 1, 0, 0);
934 glRotatef (max/2 - z*max, 0, 1, 0);
937 mi->polygon_count = 0;
939 glPushMatrix(); /* table */
940 glCallList (sp->table_list);
941 mi->polygon_count += sp->table_polys;
944 glPushMatrix(); /* text */
945 glTranslatef (0, 0, -0.01);
946 mi->polygon_count += draw_bogies (mi);
949 glCallList (sp->screen_list); /* glass */
950 mi->polygon_count += sp->screen_polys;
952 glTranslatef (0, 0, 0.004); /* sweep */
954 glRotatef ((sp->sweep_th * 180 / M_PI), 0, 0, 1);
955 if (sp->sweep_th >= 0)
956 glCallList (sp->sweep_list);
957 mi->polygon_count += sp->sweep_polys;
960 glTranslatef (0, 0, 0.004); /* grid */
961 glCallList (sp->grid_list);
962 mi->polygon_count += sp->screen_polys;
964 if (! sp->ssd || sp->error)
965 draw_startup_blurb(mi);
969 if (mi->fps_p) do_fps (mi);
972 glXSwapBuffers(dpy, window);
975 /* Just starting up. "Resolving hosts" text printed. Go stall. */
982 release_sonar (ModeInfo *mi)
985 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
986 sonar_bogie *b = sp->displayed;
989 sonar_bogie *next = b->next;
990 free_bogie (sp->ssd, b);
998 sonar_bogie *next = b->next;
999 free_bogie (sp->ssd, b);
1004 sp->ssd->free_data_cb (sp->ssd, sp->ssd->closure);
1010 XSCREENSAVER_MODULE ("Sonar", sonar)