1 /* sonar, Copyright (c) 1998-2012 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 glDisable (GL_TEXTURE_2D);
196 glFrontFace (GL_CCW);
197 th_steps = 36 * 4; /* must be a multiple of th_skip2 divisor */
204 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
205 glMateriali (GL_FRONT, GL_SHININESS, shiny);
206 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mesh_p ? lines : glass);
207 if (wire) glColor3fv (lines);
211 th_skip = th_steps / 12;
212 th_skip2 = th_steps / 36;
213 r_skip = r_steps / 3;
214 outer_r = r_steps * 0.93;
217 glLineWidth (sp->line_thickness);
220 ring = (XYZ *) calloc (th_steps, sizeof(*ring));
222 for (i = 0; i < th_steps; i++)
224 double a = M_PI * 2 * i / th_steps;
229 /* place the bottom of the disc on the xy plane. */
230 zoff = cos (curvature/2 * (M_PI/2)) / 2;
232 for (i = r_steps; i > 0; i--)
236 r0 = i / (GLfloat) r_steps;
237 r1 = (i+1) / (GLfloat) r_steps;
239 if (r1 > 1) r1 = 1; /* avoid asin lossage */
241 z0 = cos (curvature/2 * asin (r0)) / 2 - zoff;
242 z1 = cos (curvature/2 * asin (r1)) / 2 - zoff;
244 glBegin(wire || mesh_p ? GL_LINES : GL_QUAD_STRIP);
245 for (j0 = 0; j0 <= th_steps; j0++)
249 ? (j0 % th_skip != 0)
250 : (j0 % th_skip2 != 0)))
256 GLfloat r = 1 - (j0 / (GLfloat) (th_steps * sweep_size));
258 color[0] = glass[0] + (sweepc[0] - glass[0]) * r;
259 color[1] = glass[1] + (sweepc[1] - glass[1]) * r;
260 color[2] = glass[2] + (sweepc[2] - glass[2]) * r;
263 color[0] = sweepc[0];
264 color[1] = sweepc[1];
265 color[2] = sweepc[2];
268 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
272 glNormal3f (r0 * ring[j1].x, r0 * ring[j1].y, z0);
273 glVertex3f (r0 * ring[j1].x, r0 * ring[j1].y, z0);
274 glNormal3f (r1 * ring[j1].x, r1 * ring[j1].y, z1);
275 glVertex3f (r1 * ring[j1].x, r1 * ring[j1].y, z1);
278 if (sweep_p && j0 >= th_steps * sweep_size)
289 i < r_steps - r_skip)))
291 glBegin(GL_LINE_LOOP);
292 for (j0 = 0; j0 < th_steps; j0++)
294 glNormal3f (r0 * ring[j0].x, r0 * ring[j0].y, z0);
295 glVertex3f (r0 * ring[j0].x, r0 * ring[j0].y, z0);
302 /* one more polygon for the middle */
303 if (!wire && !sweep_p)
305 glBegin(wire || mesh_p ? GL_LINE_LOOP : GL_POLYGON);
306 glNormal3f (0, 0, 1);
307 for (i = 0; i < th_steps; i++)
309 glNormal3f (r0 * ring[i].x, r0 * ring[i].y, z0);
310 glVertex3f (r0 * ring[i].x, r0 * ring[i].y, z0);
323 draw_text (ModeInfo *mi, const char *string, GLfloat r, GLfloat th,
324 GLfloat ttl, GLfloat size)
326 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
327 int wire = MI_IS_WIREFRAME(mi);
329 GLfloat font_scale = 0.001 * (size > 0 ? size : font_size) / 14.0;
330 int lines = 0, max_w = 0, lh = 0;
331 char *string2 = strdup (string);
332 char *token = string2;
336 if (size <= 0) /* if size not specified, draw in yellow with alpha */
341 color[3] = (ttl / (M_PI * 2)) * 1.2;
342 if (color[3] > 1) color[3] = 1;
344 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
346 glColor3f (color[0]*color[3], color[1]*color[3], color[2]*color[3]);
349 while ((line = strtok (token, "\r\n")))
351 int w = texture_string_width (sp->texfont, line, &lh);
352 if (w > max_w) max_w = w;
358 glTranslatef (r * cos (th), r * sin(th), 0);
359 glScalef (font_scale, font_scale, font_scale);
361 if (size <= 0) /* Draw the dot */
363 GLfloat s = font_size * 1.7;
364 glDisable (GL_TEXTURE_2D);
366 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
367 glVertex3f (0, s, 0);
368 glVertex3f (s, s, 0);
369 glVertex3f (s, 0, 0);
370 glVertex3f (0, 0, 0);
372 glTranslatef (-max_w/2, -lh, 0);
375 glTranslatef (-max_w/2, -lh/2, 0);
377 /* draw each line, centered */
378 if (! wire) glEnable (GL_TEXTURE_2D);
380 string2 = strdup (string);
382 while ((line = strtok (token, "\r\n")))
384 int w = texture_string_width (sp->texfont, line, 0);
386 glTranslatef ((max_w-w)/2, 0, 0);
390 glBegin (GL_LINE_LOOP);
391 glVertex3f (0, 0, 0);
392 glVertex3f (w, 0, 0);
393 glVertex3f (w, lh, 0);
394 glVertex3f (0, lh, 0);
400 print_texture_string (sp->texfont, line);
403 glTranslatef (0, -lh, 0);
411 if (! wire) glEnable (GL_DEPTH_TEST);
417 /* There's a disc with a hole in it around the screen, to act as a mask
418 preventing slightly off-screen bogies from showing up. This clips 'em.
421 draw_table (ModeInfo *mi)
423 /*sonar_configuration *sp = &sps[MI_SCREEN(mi)];*/
424 int wire = MI_IS_WIREFRAME(mi);
427 int th_steps = 36 * 4; /* same as in draw_screen */
429 static const GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
430 static const GLfloat text[4] = {0.15, 0.15, 0.15, 1.0};
431 static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0};
432 static const GLfloat shiny = 0.0;
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);
456 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, text);
457 glTranslatef (0, 0, 0.01);
458 for (i = 0; i < 360; i += 10)
461 GLfloat a = M_PI/2 - (i / 180.0 * M_PI);
462 sprintf (buf, "%d", i);
463 polys += draw_text (mi, buf, 1.07, a, 0, 10.0);
471 draw_bogies (ModeInfo *mi)
473 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
477 for (b = sp->displayed; b; b = b->next)
480 malloc (strlen (b->name) + (b->desc ? strlen(b->desc) : 0) + 3);
487 polys += draw_text (mi, s, b->r, b->th, b->ttl, -1);
490 /* Move *very slightly* forward so that the text is not all in the
491 same plane: this prevents flickering with overlapping text as
492 the textures fight for priority. */
493 glTranslatef(0, 0, 0.00002);
500 /* called from sonar-sim.c and sonar-icmp.c */
502 copy_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
504 sonar_bogie *b2 = (sonar_bogie *) calloc (1, sizeof(*b2));
505 b2->name = strdup (b->name);
506 b2->desc = b->desc ? strdup (b->desc) : 0;
510 /* does not copy b->closure */
512 /* Take this opportunity to normalize 'th' to the range [0-2pi). */
513 while (b2->th < 0) b2->th += M_PI*2;
514 while (b2->th >= M_PI*2) b2->th -= M_PI*2;
520 /* called from sonar-icmp.c */
522 free_bogie (sonar_sensor_data *ssd, sonar_bogie *b)
525 ssd->free_bogie_cb (ssd, b->closure);
527 if (b->desc) free (b->desc);
531 /* removes it from the list and frees it
534 delete_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
535 sonar_bogie **from_list)
537 sonar_bogie *ob, *prev;
538 for (prev = 0, ob = *from_list; ob; prev = ob, ob = ob->next)
542 prev->next = b->next;
544 (*from_list) = b->next;
551 /* copies the bogie and adds it to the list.
552 if there's another bogie there with the same name, frees that one.
555 copy_and_insert_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
556 sonar_bogie **to_list)
558 sonar_bogie *ob, *next;
560 for (ob = *to_list, next = ob ? ob->next : 0;
562 ob = next, next = ob ? ob->next : 0)
564 if (ob == b) abort(); /* this will end badly */
565 if (!strcmp (ob->name, b->name)) /* match! */
567 delete_bogie (ssd, ob, to_list);
572 b = copy_bogie (ssd, b);
579 update_sensor_data (sonar_configuration *sp)
581 sonar_bogie *new_list = sp->ssd->scan_cb (sp->ssd);
584 /* If a bogie exists in 'new_list' but not 'pending', add it.
585 If a bogie exists in both, update it in 'pending'.
587 for (b2 = new_list; b2; b2 = b2->next)
590 fprintf (stderr, "%s: updated: %s (%5.2f %5.2f %5.2f)\n",
591 progname, b2->name, b2->r, b2->th, b2->ttl);
592 copy_and_insert_bogie (sp->ssd, b2, &sp->pending);
594 if (debug_p > 2) fprintf (stderr, "\n");
598 /* Returns whether the given angle lies between two other angles.
599 When those angles cross 0, it assumes the wedge is the smaller one.
600 That is: 5 lies between 10 and 350 degrees (a 20 degree wedge).
603 point_in_wedge (GLfloat th, GLfloat low, GLfloat high)
606 return (th > low && th <= high);
608 return (th <= high || th > low);
612 /* Returns the current time in seconds as a double.
618 # ifdef GETTIMEOFDAY_TWO_ARGS
620 gettimeofday(&now, &tzp);
625 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
630 sweep (sonar_configuration *sp)
634 /* Move the sweep forward (clockwise).
636 GLfloat prev_sweep, this_sweep, tick;
637 GLfloat cycle_secs = 30 / speed; /* default to one cycle every N seconds */
638 this_sweep = ((cycle_secs - fmod (double_time() - sp->start_time +
643 prev_sweep = sp->sweep_th;
644 tick = prev_sweep - this_sweep;
645 while (tick < 0) tick += M_PI*2;
647 sp->sweep_th = this_sweep;
649 if (this_sweep < 0 || this_sweep >= M_PI*2) abort();
650 if (prev_sweep < 0) /* skip first time */
653 if (tick < 0 || tick >= M_PI*2) abort();
656 /* Go through the 'pending' sensor data, find those bogies who are
657 just now being swept, and move them from 'pending' to 'displayed'.
658 (Leave bogies that have not yet been swept alone: we'll get to
659 them when the sweep moves forward.)
664 sonar_bogie *next = b->next;
665 if (point_in_wedge (b->th, this_sweep, prev_sweep))
668 time_t t = time((time_t *) 0);
670 "%s: sweep hit: %02d:%02d: %s: (%5.2f %5.2f %5.2f;"
671 " th=[%.2f < %.2f <= %.2f])\n",
673 (int) (t / 60) % 60, (int) t % 60,
674 b->name, b->r, b->th, b->ttl,
675 this_sweep, b->th, prev_sweep);
678 copy_and_insert_bogie (sp->ssd, b, &sp->displayed);
679 delete_bogie (sp->ssd, b, &sp->pending);
685 /* Update TTL on all currently-displayed bogies; delete the dead.
687 Request sensor updates on the ones just now being swept.
689 Any updates go into 'pending' and might not show up until
690 the next time the sweep comes around. This is to prevent
691 already-drawn bogies from jumping to a new position without
692 having faded out first.
697 sonar_bogie *next = b->next;
703 fprintf (stderr, "%s: TTL expired: %s (%5.2f %5.2f %5.2f)\n",
704 progname, b->name, b->r, b->th, b->ttl);
705 delete_bogie (sp->ssd, b, &sp->displayed);
710 update_sensor_data (sp);
715 draw_startup_blurb (ModeInfo *mi)
717 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
718 const char *msg = (sp->error ? sp->error : "Resolving hosts...");
719 static const GLfloat color[4] = {0, 1, 0, 1};
721 if (!sp->error && ping_arg && !strcmp (ping_arg, "simulation"))
722 return; /* don't bother */
724 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
725 glTranslatef (0, 0, 0.3);
726 draw_text (mi, msg, 0, 0, 0, 30.0);
728 /* only leave error message up for N seconds */
730 sp->start_time + 4 < double_time())
738 /* Window management, etc
741 reshape_sonar (ModeInfo *mi, int width, int height)
743 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
744 GLfloat h = (GLfloat) height / (GLfloat) width;
746 glViewport (0, 0, (GLint) width, (GLint) height);
748 glMatrixMode(GL_PROJECTION);
750 gluPerspective (30.0, 1/h, 1.0, 100.0);
752 glMatrixMode(GL_MODELVIEW);
754 gluLookAt( 0.0, 0.0, 30.0,
758 glClear(GL_COLOR_BUFFER_BIT);
760 sp->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
765 sonar_handle_event (ModeInfo *mi, XEvent *event)
767 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
769 if (event->xany.type == ButtonPress &&
770 event->xbutton.button == Button1)
772 sp->button_down_p = True;
773 gltrackball_start (sp->trackball,
774 event->xbutton.x, event->xbutton.y,
775 MI_WIDTH (mi), MI_HEIGHT (mi));
778 else if (event->xany.type == ButtonRelease &&
779 event->xbutton.button == Button1)
781 sp->button_down_p = False;
784 else if (event->xany.type == ButtonPress &&
785 (event->xbutton.button == Button4 ||
786 event->xbutton.button == Button5 ||
787 event->xbutton.button == Button6 ||
788 event->xbutton.button == Button7))
790 gltrackball_mousewheel (sp->trackball, event->xbutton.button, 10,
791 !!event->xbutton.state);
794 else if (event->xany.type == MotionNotify &&
797 gltrackball_track (sp->trackball,
798 event->xmotion.x, event->xmotion.y,
799 MI_WIDTH (mi), MI_HEIGHT (mi));
808 init_sonar (ModeInfo *mi)
810 sonar_configuration *sp;
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));
824 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
826 sp->trackball = gltrackball_init ();
827 sp->rot = make_rotator (0, 0, 0, 0, speed * 0.003, True);
829 sp->texfont = load_texture_font (MI_DISPLAY(mi), "font");
830 check_gl_error ("loading font");
832 sp->table_list = glGenLists (1);
833 glNewList (sp->table_list, GL_COMPILE);
834 sp->table_polys = draw_table (mi);
837 sp->screen_list = glGenLists (1);
838 glNewList (sp->screen_list, GL_COMPILE);
839 sp->screen_polys = draw_screen (mi, False, False);
842 sp->grid_list = glGenLists (1);
843 glNewList (sp->grid_list, GL_COMPILE);
844 sp->grid_polys = draw_screen (mi, True, False);
847 sp->sweep_list = glGenLists (1);
848 glNewList (sp->sweep_list, GL_COMPILE);
849 sp->sweep_polys = draw_screen (mi, False, True);
852 sp->start_time = double_time ();
853 sp->sweep_offset = random() % 60;
859 init_sensor (ModeInfo *mi)
861 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
863 if (sp->ssd) abort();
865 if (!ping_arg || !*ping_arg ||
866 !strcmp(ping_arg, "default") ||
867 !!strcmp (ping_arg, "simulation"))
868 sp->ssd = init_ping (MI_DISPLAY (mi), &sp->error, ping_arg,
869 ping_timeout, resolve_p, times_p, debug_p);
871 /* Disavow privs. This was already done in init_ping(), but
872 we might not have called that at all, so do it again. */
876 sp->ssd = init_simulation (MI_DISPLAY (mi), &sp->error,
877 team_a_name, team_b_name,
878 team_a_count, team_b_count,
886 draw_sonar (ModeInfo *mi)
888 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
889 Display *dpy = MI_DISPLAY(mi);
890 Window window = MI_WINDOW(mi);
891 int wire = MI_IS_WIREFRAME(mi);
893 if (!sp->glx_context)
896 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
898 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
902 GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
903 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
904 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
905 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
907 glEnable(GL_TEXTURE_2D);
908 glEnable(GL_LIGHTING);
910 glEnable(GL_CULL_FACE);
911 glEnable(GL_DEPTH_TEST);
912 glEnable(GL_NORMALIZE);
913 glEnable(GL_LINE_SMOOTH);
915 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
916 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
917 glShadeModel(GL_SMOOTH);
919 glLightfv(GL_LIGHT0, GL_POSITION, pos);
920 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
921 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
922 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
928 if (MI_WIDTH(mi) < MI_HEIGHT(mi))
929 s *= (MI_WIDTH(mi) / (float) MI_HEIGHT(mi));
933 gltrackball_rotate (sp->trackball);
934 glRotatef(current_device_rotation(), 0, 0, 1);
940 get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
941 glRotatef (max/2 - x*max, 1, 0, 0);
942 glRotatef (max/2 - z*max, 0, 1, 0);
945 mi->polygon_count = 0;
947 glPushMatrix(); /* table */
948 glCallList (sp->table_list);
949 mi->polygon_count += sp->table_polys;
952 glPushMatrix(); /* text */
953 glTranslatef (0, 0, -0.01);
954 mi->polygon_count += draw_bogies (mi);
957 glCallList (sp->screen_list); /* glass */
958 mi->polygon_count += sp->screen_polys;
960 glTranslatef (0, 0, 0.004); /* sweep */
962 glRotatef ((sp->sweep_th * 180 / M_PI), 0, 0, 1);
963 if (sp->sweep_th >= 0)
964 glCallList (sp->sweep_list);
965 mi->polygon_count += sp->sweep_polys;
968 glTranslatef (0, 0, 0.004); /* grid */
969 glCallList (sp->grid_list);
970 mi->polygon_count += sp->screen_polys;
972 if (! sp->ssd || sp->error)
973 draw_startup_blurb(mi);
977 if (mi->fps_p) do_fps (mi);
980 glXSwapBuffers(dpy, window);
983 /* Just starting up. "Resolving hosts" text printed. Go stall. */
990 release_sonar (ModeInfo *mi)
993 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
994 sonar_bogie *b = sp->displayed;
997 sonar_bogie *next = b->next;
998 free_bogie (sp->ssd, b);
1006 sonar_bogie *next = b->next;
1007 free_bogie (sp->ssd, b);
1012 sp->ssd->free_data_cb (sp->ssd, sp->ssd->closure);
1018 XSCREENSAVER_MODULE ("Sonar", sonar)