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.
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 "-*-lucidatypewriter-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 #define DEFAULTS "*delay: 30000 \n" \
70 "*font: " DEF_FONT "\n" \
71 "*showFPS: False \n" \
72 "*wireframe: False \n" \
75 # define refresh_sonar 0
77 #define countof(x) (sizeof((x))/sizeof((*x)))
80 # include <unistd.h> /* for setuid() */
83 #include "xlockmore.h"
85 #include "gltrackball.h"
90 #ifdef USE_GL /* whole file */
97 GLXContext *glx_context;
98 trackball_state *trackball;
103 GLfloat sweep_offset;
105 GLuint screen_list, grid_list, sweep_list, table_list;
106 int screen_polys, grid_polys, sweep_polys, table_polys;
108 GLfloat line_thickness;
110 texture_font_data *texfont;
112 enum { MSG, RESOLVE, RUN } state;
113 sonar_sensor_data *ssd;
117 sonar_bogie *displayed; /* on screen and fading */
118 sonar_bogie *pending; /* returned by sensor, not yet on screen */
120 } sonar_configuration;
122 static sonar_configuration *sps = NULL;
124 static GLfloat speed;
125 static GLfloat sweep_size;
126 static GLfloat font_size;
127 static Bool resolve_p;
129 static Bool wobble_p;
132 static char *team_a_name;
133 static char *team_b_name;
134 static int team_a_count;
135 static int team_b_count;
136 static int ping_timeout;
137 static char *ping_arg;
139 static XrmOptionDescRec opts[] = {
140 { "-speed", ".speed", XrmoptionSepArg, 0 },
141 { "-sweep-size", ".sweepSize", XrmoptionSepArg, 0 },
142 { "-font-size", ".fontSize", XrmoptionSepArg, 0 },
143 { "-team-a-name", ".teamAName", XrmoptionSepArg, 0 },
144 { "-team-b-name", ".teamBName", XrmoptionSepArg, 0 },
145 { "-team-a-count", ".teamACount", XrmoptionSepArg, 0 },
146 { "-team-b-count", ".teamBCount", XrmoptionSepArg, 0 },
147 { "-ping", ".ping", XrmoptionSepArg, 0 },
148 { "-ping-timeout", ".pingTimeout", XrmoptionSepArg, 0 },
149 { "-dns", ".resolve", XrmoptionNoArg, "True" },
150 { "+dns", ".resolve", XrmoptionNoArg, "False" },
151 { "-times", ".times", XrmoptionNoArg, "True" },
152 { "+times", ".times", XrmoptionNoArg, "False" },
153 { "-wobble", ".wobble", XrmoptionNoArg, "True" },
154 { "+wobble", ".wobble", XrmoptionNoArg, "False" },
155 { "-debug", ".debug", XrmoptionNoArg, "True" },
158 static argtype vars[] = {
159 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
160 {&sweep_size, "sweepSize", "SweepSize", DEF_SWEEP_SIZE, t_Float},
161 {&font_size, "fontSize", "FontSize", DEF_FONT_SIZE, t_Float},
162 {&team_a_name, "teamAName", "TeamName", DEF_TEAM_A_NAME, t_String},
163 {&team_b_name, "teamBName", "TeamName", DEF_TEAM_B_NAME, t_String},
164 {&team_a_count, "teamACount", "TeamCount", DEF_TEAM_A_COUNT, t_Int},
165 {&team_b_count, "teamBCount", "TeamCount", DEF_TEAM_A_COUNT, t_Int},
166 {&ping_arg, "ping", "Ping", DEF_PING, t_String},
167 {&ping_timeout, "pingTimeout", "PingTimeout", DEF_PING_TIMEOUT, t_Int},
168 {&resolve_p, "resolve", "Resolve", DEF_RESOLVE, t_Bool},
169 {×_p, "times", "Times", DEF_TIMES, t_Bool},
170 {&wobble_p, "wobble", "Wobble", DEF_WOBBLE, t_Bool},
171 {&debug_p, "debug", "Debug", DEF_DEBUG, t_Bool},
174 ENTRYPOINT ModeSpecOpt sonar_opts = {countof(opts), opts, countof(vars), vars, NULL};
178 draw_screen (ModeInfo *mi, Bool mesh_p, Bool sweep_p)
180 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
181 int wire = MI_IS_WIREFRAME(mi);
184 int th_steps, r_steps, r_skip, th_skip, th_skip2, outer_r;
185 GLfloat curvature = M_PI * 0.4;
186 GLfloat r0, r1, z0, z1, zoff;
189 static const GLfloat glass[4] = {0.0, 0.4, 0.0, 0.5};
190 static const GLfloat lines[4] = {0.0, 0.7, 0.0, 0.5};
191 static const GLfloat sweepc[4] = {0.2, 1.0, 0.2, 0.5};
192 static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
193 static const GLfloat shiny = 20.0;
195 if (wire && !(mesh_p || sweep_p)) return 0;
197 glDisable (GL_TEXTURE_2D);
199 glFrontFace (GL_CCW);
200 th_steps = 36 * 4; /* must be a multiple of th_skip2 divisor */
207 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
208 glMateriali (GL_FRONT, GL_SHININESS, shiny);
209 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mesh_p ? lines : glass);
210 if (wire) glColor3fv (lines);
214 th_skip = th_steps / 12;
215 th_skip2 = th_steps / 36;
216 r_skip = r_steps / 3;
217 outer_r = r_steps * 0.93;
220 glLineWidth (sp->line_thickness);
223 ring = (XYZ *) calloc (th_steps, sizeof(*ring));
225 for (i = 0; i < th_steps; i++)
227 double a = M_PI * 2 * i / th_steps;
232 /* place the bottom of the disc on the xy plane. */
233 zoff = cos (curvature/2 * (M_PI/2)) / 2;
235 for (i = r_steps; i > 0; i--)
239 r0 = i / (GLfloat) r_steps;
240 r1 = (i+1) / (GLfloat) r_steps;
242 if (r1 > 1) r1 = 1; /* avoid asin lossage */
244 z0 = cos (curvature/2 * asin (r0)) / 2 - zoff;
245 z1 = cos (curvature/2 * asin (r1)) / 2 - zoff;
247 glBegin(wire || mesh_p ? GL_LINES : GL_QUAD_STRIP);
248 for (j0 = 0; j0 <= th_steps; j0++)
252 ? (j0 % th_skip != 0)
253 : (j0 % th_skip2 != 0)))
259 GLfloat r = 1 - (j0 / (GLfloat) (th_steps * sweep_size));
261 color[0] = glass[0] + (sweepc[0] - glass[0]) * r;
262 color[1] = glass[1] + (sweepc[1] - glass[1]) * r;
263 color[2] = glass[2] + (sweepc[2] - glass[2]) * r;
266 color[0] = sweepc[0];
267 color[1] = sweepc[1];
268 color[2] = sweepc[2];
271 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
275 glNormal3f (r0 * ring[j1].x, r0 * ring[j1].y, z0);
276 glVertex3f (r0 * ring[j1].x, r0 * ring[j1].y, z0);
277 glNormal3f (r1 * ring[j1].x, r1 * ring[j1].y, z1);
278 glVertex3f (r1 * ring[j1].x, r1 * ring[j1].y, z1);
281 if (sweep_p && j0 >= th_steps * sweep_size)
292 i < r_steps - r_skip)))
294 glBegin(GL_LINE_LOOP);
295 for (j0 = 0; j0 < th_steps; j0++)
297 glNormal3f (r0 * ring[j0].x, r0 * ring[j0].y, z0);
298 glVertex3f (r0 * ring[j0].x, r0 * ring[j0].y, z0);
305 /* one more polygon for the middle */
306 if (!wire && !sweep_p)
308 glBegin(wire || mesh_p ? GL_LINE_LOOP : GL_POLYGON);
309 glNormal3f (0, 0, 1);
310 for (i = 0; i < th_steps; i++)
312 glNormal3f (r0 * ring[i].x, r0 * ring[i].y, z0);
313 glVertex3f (r0 * ring[i].x, r0 * ring[i].y, z0);
326 draw_text (ModeInfo *mi, const char *string, GLfloat r, GLfloat th,
327 GLfloat ttl, GLfloat size)
329 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
330 int wire = MI_IS_WIREFRAME(mi);
332 GLfloat font_scale = 0.001 * (size > 0 ? size : font_size) / 14.0;
333 int lines = 0, max_w = 0, lh = 0;
334 char *string2 = strdup (string);
335 char *token = string2;
338 if (size <= 0) /* if size not specified, draw in yellow with alpha */
344 color[3] = (ttl / (M_PI * 2)) * 1.2;
345 if (color[3] > 1) color[3] = 1;
347 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
349 glColor3f (color[0]*color[3], color[1]*color[3], color[2]*color[3]);
352 while ((line = strtok (token, "\r\n")))
354 int w = texture_string_width (sp->texfont, line, &lh);
355 if (w > max_w) max_w = w;
361 glTranslatef (r * cos (th), r * sin(th), 0);
362 glScalef (font_scale, font_scale, font_scale);
364 if (size <= 0) /* Draw the dot */
366 GLfloat s = font_size * 1.7;
367 glDisable (GL_TEXTURE_2D);
369 glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
370 glVertex3f (0, s, 0);
371 glVertex3f (s, s, 0);
372 glVertex3f (s, 0, 0);
373 glVertex3f (0, 0, 0);
375 glTranslatef (-max_w/2, -lh, 0);
378 glTranslatef (-max_w/2, -lh/2, 0);
380 /* draw each line, centered */
381 if (! wire) glEnable (GL_TEXTURE_2D);
383 string2 = strdup (string);
385 while ((line = strtok (token, "\r\n")))
387 int w = texture_string_width (sp->texfont, line, 0);
389 glTranslatef ((max_w-w)/2, 0, 0);
393 glBegin (GL_LINE_LOOP);
394 glVertex3f (0, 0, 0);
395 glVertex3f (w, 0, 0);
396 glVertex3f (w, lh, 0);
397 glVertex3f (0, lh, 0);
403 print_texture_string (sp->texfont, line);
406 glTranslatef (0, -lh, 0);
414 if (! wire) glEnable (GL_DEPTH_TEST);
420 /* There's a disc with a hole in it around the screen, to act as a mask
421 preventing slightly off-screen bogies from showing up. This clips 'em.
424 draw_table (ModeInfo *mi)
426 /*sonar_configuration *sp = &sps[MI_SCREEN(mi)];*/
427 int wire = MI_IS_WIREFRAME(mi);
430 int th_steps = 36 * 4; /* same as in draw_screen */
432 static const GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
433 static const GLfloat text[4] = {0.15, 0.15, 0.15, 1.0};
434 static const GLfloat spec[4] = {0.0, 0.0, 0.0, 1.0};
435 static const GLfloat shiny = 0.0;
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);
459 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, text);
460 glTranslatef (0, 0, 0.01);
461 for (i = 0; i < 360; i += 10)
464 GLfloat a = M_PI/2 - (i / 180.0 * M_PI);
465 sprintf (buf, "%d", i);
466 polys += draw_text (mi, buf, 1.07, a, 0, 10.0);
474 draw_bogies (ModeInfo *mi)
476 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
480 for (b = sp->displayed; b; b = b->next)
483 malloc (strlen (b->name) + (b->desc ? strlen(b->desc) : 0) + 3);
490 polys += draw_text (mi, s, b->r, b->th, b->ttl, -1);
493 /* Move *very slightly* forward so that the text is not all in the
494 same plane: this prevents flickering with overlapping text as
495 the textures fight for priority. */
496 glTranslatef(0, 0, 0.00002);
503 /* called from sonar-sim.c and sonar-icmp.c */
505 sonar_copy_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
507 sonar_bogie *b2 = (sonar_bogie *) calloc (1, sizeof(*b2));
508 b2->name = strdup (b->name);
509 b2->desc = b->desc ? strdup (b->desc) : 0;
513 /* does not copy b->closure */
515 /* Take this opportunity to normalize 'th' to the range [0-2pi). */
516 while (b2->th < 0) b2->th += M_PI*2;
517 while (b2->th >= M_PI*2) b2->th -= M_PI*2;
523 /* called from sonar-icmp.c */
525 sonar_free_bogie (sonar_sensor_data *ssd, sonar_bogie *b)
528 ssd->free_bogie_cb (ssd, b->closure);
530 if (b->desc) free (b->desc);
534 /* removes it from the list and frees it
537 delete_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
538 sonar_bogie **from_list)
540 sonar_bogie *ob, *prev;
541 for (prev = 0, ob = *from_list; ob; prev = ob, ob = ob->next)
545 prev->next = b->next;
547 (*from_list) = b->next;
548 sonar_free_bogie (ssd, b);
554 /* copies the bogie and adds it to the list.
555 if there's another bogie there with the same name, frees that one.
558 copy_and_insert_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
559 sonar_bogie **to_list)
561 sonar_bogie *ob, *next;
563 for (ob = *to_list, next = ob ? ob->next : 0;
565 ob = next, next = ob ? ob->next : 0)
567 if (ob == b) abort(); /* this will end badly */
568 if (!strcmp (ob->name, b->name)) /* match! */
570 delete_bogie (ssd, ob, to_list);
575 b = sonar_copy_bogie (ssd, b);
582 update_sensor_data (sonar_configuration *sp)
584 sonar_bogie *new_list = sp->ssd->scan_cb (sp->ssd);
587 /* If a bogie exists in 'new_list' but not 'pending', add it.
588 If a bogie exists in both, update it in 'pending'.
590 for (b2 = new_list; b2; b2 = b2->next)
593 fprintf (stderr, "%s: updated: %s (%5.2f %5.2f %5.2f)\n",
594 progname, b2->name, b2->r, b2->th, b2->ttl);
595 copy_and_insert_bogie (sp->ssd, b2, &sp->pending);
597 if (debug_p > 2) fprintf (stderr, "\n");
601 /* Returns whether the given angle lies between two other angles.
602 When those angles cross 0, it assumes the wedge is the smaller one.
603 That is: 5 lies between 10 and 350 degrees (a 20 degree wedge).
606 point_in_wedge (GLfloat th, GLfloat low, GLfloat high)
609 return (th > low && th <= high);
611 return (th <= high || th > low);
615 /* Returns the current time in seconds as a double.
621 # ifdef GETTIMEOFDAY_TWO_ARGS
623 gettimeofday(&now, &tzp);
628 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
633 sweep (sonar_configuration *sp)
637 /* Move the sweep forward (clockwise).
639 GLfloat prev_sweep, this_sweep, tick;
640 GLfloat cycle_secs = 30 / speed; /* default to one cycle every N seconds */
641 this_sweep = ((cycle_secs - fmod (double_time() - sp->start_time +
646 prev_sweep = sp->sweep_th;
647 tick = prev_sweep - this_sweep;
648 while (tick < 0) tick += M_PI*2;
650 sp->sweep_th = this_sweep;
652 if (this_sweep < 0 || this_sweep >= M_PI*2) abort();
653 if (prev_sweep < 0) /* skip first time */
656 if (tick < 0 || tick >= M_PI*2) abort();
659 /* Go through the 'pending' sensor data, find those bogies who are
660 just now being swept, and move them from 'pending' to 'displayed'.
661 (Leave bogies that have not yet been swept alone: we'll get to
662 them when the sweep moves forward.)
667 sonar_bogie *next = b->next;
668 if (point_in_wedge (b->th, this_sweep, prev_sweep))
671 time_t t = time((time_t *) 0);
673 "%s: sweep hit: %02d:%02d: %s: (%5.2f %5.2f %5.2f;"
674 " th=[%.2f < %.2f <= %.2f])\n",
676 (int) (t / 60) % 60, (int) t % 60,
677 b->name, b->r, b->th, b->ttl,
678 this_sweep, b->th, prev_sweep);
681 copy_and_insert_bogie (sp->ssd, b, &sp->displayed);
682 delete_bogie (sp->ssd, b, &sp->pending);
688 /* Update TTL on all currently-displayed bogies; delete the dead.
690 Request sensor updates on the ones just now being swept.
692 Any updates go into 'pending' and might not show up until
693 the next time the sweep comes around. This is to prevent
694 already-drawn bogies from jumping to a new position without
695 having faded out first.
700 sonar_bogie *next = b->next;
706 fprintf (stderr, "%s: TTL expired: %s (%5.2f %5.2f %5.2f)\n",
707 progname, b->name, b->r, b->th, b->ttl);
708 delete_bogie (sp->ssd, b, &sp->displayed);
713 update_sensor_data (sp);
718 draw_startup_blurb (ModeInfo *mi)
720 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
721 const char *msg = (sp->error ? sp->error : "Resolving hosts...");
722 static const GLfloat color[4] = {0, 1, 0, 1};
724 if (!sp->error && ping_arg && !strcmp (ping_arg, "simulation"))
725 return; /* don't bother */
727 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
728 glTranslatef (0, 0, 0.3);
729 draw_text (mi, msg, 0, 0, 0, 30.0);
731 /* only leave error message up for N seconds */
733 sp->start_time + 6 < double_time())
741 /* Window management, etc
744 reshape_sonar (ModeInfo *mi, int width, int height)
746 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
747 GLfloat h = (GLfloat) height / (GLfloat) width;
749 glViewport (0, 0, (GLint) width, (GLint) height);
751 glMatrixMode(GL_PROJECTION);
753 gluPerspective (30.0, 1/h, 1.0, 100.0);
755 glMatrixMode(GL_MODELVIEW);
757 gluLookAt( 0.0, 0.0, 30.0,
761 glClear(GL_COLOR_BUFFER_BIT);
763 sp->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
768 sonar_handle_event (ModeInfo *mi, XEvent *event)
770 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
772 if (event->xany.type == ButtonPress &&
773 event->xbutton.button == Button1)
775 sp->button_down_p = True;
776 gltrackball_start (sp->trackball,
777 event->xbutton.x, event->xbutton.y,
778 MI_WIDTH (mi), MI_HEIGHT (mi));
781 else if (event->xany.type == ButtonRelease &&
782 event->xbutton.button == Button1)
784 sp->button_down_p = False;
787 else if (event->xany.type == ButtonPress &&
788 (event->xbutton.button == Button4 ||
789 event->xbutton.button == Button5 ||
790 event->xbutton.button == Button6 ||
791 event->xbutton.button == Button7))
793 gltrackball_mousewheel (sp->trackball, event->xbutton.button, 10,
794 !!event->xbutton.state);
797 else if (event->xany.type == MotionNotify &&
800 gltrackball_track (sp->trackball,
801 event->xmotion.x, event->xmotion.y,
802 MI_WIDTH (mi), MI_HEIGHT (mi));
811 init_sonar (ModeInfo *mi)
813 sonar_configuration *sp;
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));
827 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
829 sp->trackball = gltrackball_init ();
830 sp->rot = make_rotator (0, 0, 0, 0, speed * 0.003, True);
832 sp->texfont = load_texture_font (MI_DISPLAY(mi), "font");
833 check_gl_error ("loading font");
835 sp->table_list = glGenLists (1);
836 glNewList (sp->table_list, GL_COMPILE);
837 sp->table_polys = draw_table (mi);
840 sp->screen_list = glGenLists (1);
841 glNewList (sp->screen_list, GL_COMPILE);
842 sp->screen_polys = draw_screen (mi, False, False);
845 sp->grid_list = glGenLists (1);
846 glNewList (sp->grid_list, GL_COMPILE);
847 sp->grid_polys = draw_screen (mi, True, False);
850 sp->sweep_list = glGenLists (1);
851 glNewList (sp->sweep_list, GL_COMPILE);
852 sp->sweep_polys = draw_screen (mi, False, True);
855 sp->start_time = double_time ();
856 sp->sweep_offset = random() % 60;
863 init_sensor (ModeInfo *mi)
865 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
867 if (sp->ssd) abort();
869 if (!ping_arg || !*ping_arg ||
870 !strcmp(ping_arg, "default") ||
871 !!strcmp (ping_arg, "simulation"))
872 sp->ssd = sonar_init_ping (MI_DISPLAY (mi), &sp->error, &sp->desc,
873 ping_arg, ping_timeout, resolve_p, times_p,
876 sp->start_time = double_time (); /* for error message timing */
878 /* Disavow privs. This was already done in init_ping(), but
879 we might not have called that at all, so do it again. */
883 sp->ssd = sonar_init_simulation (MI_DISPLAY (mi), &sp->error, &sp->desc,
884 team_a_name, team_b_name,
885 team_a_count, team_b_count,
893 draw_sonar (ModeInfo *mi)
895 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
896 Display *dpy = MI_DISPLAY(mi);
897 Window window = MI_WINDOW(mi);
898 int wire = MI_IS_WIREFRAME(mi);
900 if (!sp->glx_context)
903 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
905 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
909 GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
910 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
911 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
912 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
914 glEnable(GL_TEXTURE_2D);
915 glEnable(GL_LIGHTING);
917 glEnable(GL_CULL_FACE);
918 glEnable(GL_DEPTH_TEST);
919 glEnable(GL_NORMALIZE);
920 glEnable(GL_LINE_SMOOTH);
922 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
923 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
924 glShadeModel(GL_SMOOTH);
926 glLightfv(GL_LIGHT0, GL_POSITION, pos);
927 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
928 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
929 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
933 glRotatef(current_device_rotation(), 0, 0, 1);
937 if (MI_WIDTH(mi) < MI_HEIGHT(mi))
938 s *= (MI_WIDTH(mi) / (float) MI_HEIGHT(mi));
942 gltrackball_rotate (sp->trackball);
948 get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
949 glRotatef (max/2 - x*max, 1, 0, 0);
950 glRotatef (max/2 - z*max, 0, 1, 0);
953 mi->polygon_count = 0;
955 glPushMatrix(); /* table */
956 glCallList (sp->table_list);
957 mi->polygon_count += sp->table_polys;
960 glPushMatrix(); /* text */
961 glTranslatef (0, 0, -0.01);
962 mi->polygon_count += draw_bogies (mi);
965 glCallList (sp->screen_list); /* glass */
966 mi->polygon_count += sp->screen_polys;
968 glTranslatef (0, 0, 0.004); /* sweep */
970 glRotatef ((sp->sweep_th * 180 / M_PI), 0, 0, 1);
971 if (sp->sweep_th >= 0)
972 glCallList (sp->sweep_list);
973 mi->polygon_count += sp->sweep_polys;
976 glTranslatef (0, 0, 0.004); /* grid */
977 glCallList (sp->grid_list);
978 mi->polygon_count += sp->screen_polys;
980 if (sp->desc) /* local subnet */
983 glTranslatef (0, 0, 0.00002);
984 mi->polygon_count += draw_text (mi, sp->desc, 1.35, M_PI * 0.75, 0, 10);
985 /* glRotatef (45, 0, 0, 1); */
986 /* mi->polygon_count += draw_text (mi, sp->desc, 1.2, M_PI/2, 0, 10); */
994 case MSG: /* Frame 1: get "Resolving Hosts" on screen. */
995 draw_startup_blurb(mi);
998 case RESOLVE: /* Frame 2: gethostbyaddr may take a while. */
1003 case RUN: /* Frame N: ping away */
1010 if (mi->fps_p) do_fps (mi);
1013 glXSwapBuffers(dpy, window);
1017 release_sonar (ModeInfo *mi)
1020 sonar_configuration *sp = &sps[MI_SCREEN(mi)];
1021 sonar_bogie *b = sp->displayed;
1024 sonar_bogie *next = b->next;
1025 free_bogie (sp->ssd, b);
1033 sonar_bogie *next = b->next;
1034 free_bogie (sp->ssd, b);
1039 sp->ssd->free_data_cb (sp->ssd, sp->ssd->closure);
1045 XSCREENSAVER_MODULE ("Sonar", sonar)