From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / glx / sonar.c
index ac51a2ce1feb3a1a55ff186048482ba9b6834f5f..42bcdc8e7d6cc4ddb3d8afff039dff1535c00f2a 100644 (file)
@@ -1,4 +1,4 @@
-/* sonar, Copyright (c) 1998-2012 Jamie Zawinski and Stephen Martin
+/* sonar, Copyright (c) 1998-2015 Jamie Zawinski and Stephen Martin
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
  *
  * It should be easy to extend this code to support other sorts of sensors.
  * Some ideas:
+ *
  *   - search the output of "netstat" for the list of hosts to ping;
  *   - plot the contents of /proc/interrupts;
  *   - plot the process table, by process size, cpu usage, or total time;
  *   - plot the logged on users by idle time or cpu usage.
- *
+ *   - plot IM contacts or Facebook friends and their last-activity times.
  */
 
-#define DEF_FONT "-*-lucidatypewriter-bold-r-normal-*-*-480-*-*-*-*-iso8859-1"
+#define DEF_FONT "-*-courier-bold-r-normal-*-*-480-*-*-*-*-iso8859-1"
 #define DEF_SPEED        "1.0"
 #define DEF_SWEEP_SIZE   "0.3"
 #define DEF_FONT_SIZE    "12"
 #define DEF_WOBBLE       "True"
 #define DEF_DEBUG        "False"
 
+#include "thread_util.h"
+
 #define DEFAULTS       "*delay:        30000       \n" \
                        "*font:       " DEF_FONT   "\n" \
                        "*showFPS:      False       \n" \
                        "*wireframe:    False       \n" \
+                       "*texFontCacheSize: 300     \n" \
+                       THREAD_DEFAULTS_XLOCK
 
 
 # define refresh_sonar 0
+# define release_sonar 0
 #undef countof
 #define countof(x) (sizeof((x))/sizeof((*x)))
 
 
 #ifdef USE_GL /* whole file */
 
+/* #define TEST_ASYNC_NETDB 1 */
+
+# if TEST_ASYNC_NETDB
+#   include "async_netdb.h"
+
+#   include <assert.h>
+#   include <netinet/in.h>
+#   include <stdio.h>
+# endif /* TEST_ASYNC_NETDB */
+
 typedef struct {
   double x,y,z;
 } XYZ;
@@ -108,12 +124,18 @@ typedef struct {
 
   texture_font_data *texfont;
 
+  enum { MSG, RESOLVE, RUN } state;
   sonar_sensor_data *ssd;
   char *error;
+  char *desc;
 
   sonar_bogie *displayed;      /* on screen and fading */
   sonar_bogie *pending;                /* returned by sensor, not yet on screen */
 
+# if TEST_ASYNC_NETDB
+  async_name_from_addr_t query0;
+  async_addr_from_name_t query1;
+# endif
 } sonar_configuration;
 
 static sonar_configuration *sps = NULL;
@@ -149,6 +171,7 @@ static XrmOptionDescRec opts[] = {
   { "+times",        ".times",       XrmoptionNoArg, "False" },
   { "-wobble",       ".wobble",      XrmoptionNoArg, "True" },
   { "+wobble",       ".wobble",      XrmoptionNoArg, "False" },
+  THREAD_OPTIONS
   { "-debug",        ".debug",       XrmoptionNoArg, "True" },
 };
 
@@ -331,10 +354,10 @@ draw_text (ModeInfo *mi, const char *string, GLfloat r, GLfloat th,
   char *string2 = strdup (string);
   char *token = string2;
   char *line;
-  GLfloat color[4];
 
   if (size <= 0)   /* if size not specified, draw in yellow with alpha */
     {
+      GLfloat color[4];
       color[0] = 1;
       color[1] = 1;
       color[2] = 0;
@@ -348,7 +371,11 @@ draw_text (ModeInfo *mi, const char *string, GLfloat r, GLfloat th,
 
   while ((line = strtok (token, "\r\n")))
     {
-      int w = texture_string_width (sp->texfont, line, &lh);
+      XCharStruct e;
+      int w, ascent, descent;
+      texture_string_metrics (sp->texfont, line, &e, &ascent, &descent);
+      w = e.width;
+      lh = ascent + descent;
       if (w > max_w) max_w = w;
       lines++;
       token = 0;
@@ -381,9 +408,12 @@ draw_text (ModeInfo *mi, const char *string, GLfloat r, GLfloat th,
   token = string2;
   while ((line = strtok (token, "\r\n")))
     {
-      int w = texture_string_width (sp->texfont, line, 0);
+      XCharStruct e;
+      int w;
+      texture_string_metrics (sp->texfont, line, &e, 0, 0);
+      w = e.width;
       glPushMatrix();
-      glTranslatef ((max_w-w)/2, 0, 0);
+      glTranslatef ((max_w-w)/2, 0, polys * 4); /* 'polys' stops Z-fighting. */
 
       if (wire)
         {
@@ -427,7 +457,6 @@ draw_table (ModeInfo *mi)
   int th_steps = 36 * 4;    /* same as in draw_screen */
 
   static const GLfloat color[4]  = {0.0, 0.0, 0.0, 1.0};
-  static const GLfloat text[4]   = {0.15, 0.15, 0.15, 1.0};
   static const GLfloat spec[4]   = {0.0, 0.0, 0.0, 1.0};
   static const GLfloat shiny     = 0.0;
 
@@ -453,6 +482,20 @@ draw_table (ModeInfo *mi)
     }
   glEnd();
 
+  return polys;
+}
+
+
+static int
+draw_angles (ModeInfo *mi)
+{
+  int i;
+  int polys = 0;
+
+  static const GLfloat text[4]   = {0.15, 0.15, 0.15, 1.0};
+  static const GLfloat spec[4]   = {0.0, 0.0, 0.0, 1.0};
+
+  glMaterialfv (GL_FRONT, GL_SPECULAR,  spec);
   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, text);
   glTranslatef (0, 0, 0.01);
   for (i = 0; i < 360; i += 10)
@@ -499,7 +542,7 @@ draw_bogies (ModeInfo *mi)
 
 /* called from sonar-sim.c and sonar-icmp.c */
 sonar_bogie *
-copy_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
+sonar_copy_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
 {
   sonar_bogie *b2 = (sonar_bogie *) calloc (1, sizeof(*b2));
   b2->name = strdup (b->name);
@@ -519,7 +562,7 @@ copy_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
 
 /* called from sonar-icmp.c */
 void
-free_bogie (sonar_sensor_data *ssd, sonar_bogie *b)
+sonar_free_bogie (sonar_sensor_data *ssd, sonar_bogie *b)
 {
   if (b->closure)
     ssd->free_bogie_cb (ssd, b->closure);
@@ -542,7 +585,7 @@ delete_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
           prev->next = b->next;
         else
           (*from_list) = b->next;
-        free_bogie (ssd, b);
+        sonar_free_bogie (ssd, b);
         break;
       }
 }
@@ -569,7 +612,7 @@ copy_and_insert_bogie (sonar_sensor_data *ssd, sonar_bogie *b,
         }
     }
 
-  b = copy_bogie (ssd, b);
+  b = sonar_copy_bogie (ssd, b);
   b->next = *to_list;
   *to_list = b;
 }
@@ -715,22 +758,22 @@ static void
 draw_startup_blurb (ModeInfo *mi)
 {
   sonar_configuration *sp = &sps[MI_SCREEN(mi)];
-  const char *msg = (sp->error ? sp->error : "Resolving hosts...");
-  static const GLfloat color[4] = {0, 1, 0, 1};
 
-  if (!sp->error && ping_arg && !strcmp (ping_arg, "simulation"))
-    return;  /* don't bother */
+  if (sp->error)
+    {
+      const char *msg = sp->error;
+      static const GLfloat color[4] = {0, 1, 0, 1};
 
-  glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
-  glTranslatef (0, 0, 0.3);
-  draw_text (mi, msg, 0, 0, 0, 30.0);
+      glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
+      glTranslatef (0, 0, 0.3);
+      draw_text (mi, msg, 0, 0, 0, 30.0);
 
-  /* only leave error message up for N seconds */
-  if (sp->error &&
-      sp->start_time + 4 < double_time())
-    {
-      free (sp->error);
-      sp->error = 0;
+      /* only leave error message up for N seconds */
+      if (sp->start_time + 6 < double_time())
+        {
+          free (sp->error);
+          sp->error = 0;
+        }
     }
 }
 
@@ -766,64 +809,29 @@ sonar_handle_event (ModeInfo *mi, XEvent *event)
 {
   sonar_configuration *sp = &sps[MI_SCREEN(mi)];
 
-  if (event->xany.type == ButtonPress &&
-      event->xbutton.button == Button1)
-    {
-      sp->button_down_p = True;
-      gltrackball_start (sp->trackball,
-                         event->xbutton.x, event->xbutton.y,
-                         MI_WIDTH (mi), MI_HEIGHT (mi));
-      return True;
-    }
-  else if (event->xany.type == ButtonRelease &&
-           event->xbutton.button == Button1)
-    {
-      sp->button_down_p = False;
-      return True;
-    }
-  else if (event->xany.type == ButtonPress &&
-           (event->xbutton.button == Button4 ||
-            event->xbutton.button == Button5 ||
-            event->xbutton.button == Button6 ||
-            event->xbutton.button == Button7))
-    {
-      gltrackball_mousewheel (sp->trackball, event->xbutton.button, 10,
-                              !!event->xbutton.state);
-      return True;
-    }
-  else if (event->xany.type == MotionNotify &&
-           sp->button_down_p)
-    {
-      gltrackball_track (sp->trackball,
-                         event->xmotion.x, event->xmotion.y,
-                         MI_WIDTH (mi), MI_HEIGHT (mi));
-      return True;
-    }
+  if (gltrackball_event_handler (event, sp->trackball,
+                                 MI_WIDTH (mi), MI_HEIGHT (mi),
+                                 &sp->button_down_p))
+    return True;
 
   return False;
 }
 
+static void free_sonar (ModeInfo *mi);
 
 ENTRYPOINT void 
 init_sonar (ModeInfo *mi)
 {
   sonar_configuration *sp;
 
-  if (!sps) {
-    sps = (sonar_configuration *)
-      calloc (MI_NUM_SCREENS(mi), sizeof (sonar_configuration));
-    if (!sps) {
-      fprintf(stderr, "%s: out of memory\n", progname);
-      exit(1);
-    }
-  }
+  MI_INIT (mi, sps, free_sonar);
   sp = &sps[MI_SCREEN(mi)];
   sp->glx_context = init_GL(mi);
 
   reshape_sonar (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
   clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
 
-  sp->trackball = gltrackball_init ();
+  sp->trackball = gltrackball_init (False);
   sp->rot = make_rotator (0, 0, 0, 0, speed * 0.003, True);
 
   sp->texfont = load_texture_font (MI_DISPLAY(mi), "font");
@@ -852,8 +860,56 @@ init_sonar (ModeInfo *mi)
   sp->start_time = double_time ();
   sp->sweep_offset = random() % 60;
   sp->sweep_th = -1;
+  sp->state = MSG;
+}
+
+
+# ifdef TEST_ASYNC_NETDB
+
+#   include <arpa/inet.h>
+
+static void _print_sockaddr (void *addr, socklen_t addrlen, FILE *stream)
+{
+  sa_family_t family = ((struct sockaddr *)addr)->sa_family;
+  char buf[256];
+  switch (family)
+    {
+    case AF_INET:
+      fputs (inet_ntoa(((struct sockaddr_in *)addr)->sin_addr), stream);
+      break;
+    case AF_INET6:
+      inet_ntop(family, &((struct sockaddr_in6 *)addr)->sin6_addr,
+                buf, sizeof (buf));
+      fputs (buf, stream);
+      break;
+    default:
+      abort();
+      break;
+    }
+}
+
+static void _print_error (int gai_error, int errno_error, FILE *stream)
+{
+  fputs (gai_error == EAI_SYSTEM ? strerror(errno_error) : gai_strerror(gai_error), stream);
 }
 
+#   if ASYNC_NETDB_USE_GAI
+
+static void _print_thread (pthread_t thread, FILE *stream)
+{
+#     ifdef __linux__
+    fprintf (stream, "%#lx", thread);
+#     elif defined __APPLE__ && defined __MACH__
+    fprintf (stream, "%p", thread);
+#     else
+    putc ('?', stream);
+#     endif
+}
+
+#   endif /* ASYNC_NETDB_USE_GAI */
+
+# endif /* TEST_ASYNC_NETDB */
+
 
 static void
 init_sensor (ModeInfo *mi)
@@ -865,20 +921,106 @@ init_sensor (ModeInfo *mi)
   if (!ping_arg || !*ping_arg ||
       !strcmp(ping_arg, "default") ||
       !!strcmp (ping_arg, "simulation"))
-    sp->ssd = init_ping (MI_DISPLAY (mi), &sp->error, ping_arg,
-                         ping_timeout, resolve_p, times_p, debug_p);
+    /* sonar_init_ping() always disavows privs, even on failure. */
+    sp->ssd = sonar_init_ping (MI_DISPLAY (mi), &sp->error, &sp->desc,
+                               ping_arg, ping_timeout, resolve_p, times_p,
+                               debug_p);
+  else
+    setuid(getuid()); /* Disavow privs if not pinging. */
 
-  /* Disavow privs.  This was already done in init_ping(), but
-     we might not have called that at all, so do it again. */
-  setuid(getuid());
+  sp->start_time = double_time ();  /* for error message timing */
 
   if (!sp->ssd)
-    sp->ssd = init_simulation (MI_DISPLAY (mi), &sp->error,
-                               team_a_name, team_b_name,
-                               team_a_count, team_b_count,
-                               debug_p);
+    sp->ssd = sonar_init_simulation (MI_DISPLAY (mi), &sp->error, &sp->desc,
+                                     team_a_name, team_b_name,
+                                     team_a_count, team_b_count,
+                                     debug_p);
   if (!sp->ssd)
     abort();
+
+# if TEST_ASYNC_NETDB
+  /*
+     For extremely mysterious reasons, setuid apparently causes
+     pthread_join(3) to deadlock.
+     A rough guess at the sequence of events:
+     1. Worker thread is created.
+     2. Worker thread exits.
+     3. setuid(getuid()) is called.
+     4. pthread_join is called slightly later.
+
+     This may have something to do with glibc's use of SIGSETXID.
+   */
+
+  putc ('\n', stderr);
+
+#   if !ASYNC_NETDB_USE_GAI
+  fputs ("Warning: getaddrinfo() was not available at compile time.\n", stderr);
+#   endif
+
+  {
+    static const unsigned long addresses[] =
+      {
+        INADDR_LOOPBACK,
+        0x00010203,
+        0x08080808
+      };
+    struct sockaddr_in addr;
+    addr.sin_family = AF_INET;
+    addr.sin_port = 0;
+    addr.sin_addr.s_addr = htonl (addresses[random () % 3]);
+
+    sp->query0 = async_name_from_addr_start (MI_DISPLAY (mi), (void *)&addr,
+                                             sizeof(addr));
+    assert (sp->query0);
+    if (sp->query0)
+      {
+        fputs ("Looking up hostname from address: ", stderr);
+        _print_sockaddr (&addr, sizeof(addr), stderr);
+#   if ASYNC_NETDB_USE_GAI
+        fputs (" @ ", stderr);
+        _print_thread (sp->query0->io.thread, stderr);
+#   endif
+        putc ('\n', stderr);
+      }
+
+    if (!(random () & 3))
+      {
+        fputs ("Aborted hostname lookup (early)\n", stderr);
+        async_name_from_addr_cancel (sp->query0);
+        sp->query0 = NULL;
+      }
+  }
+
+  {
+    static const char *const hosts[] =
+      {
+        "example.com",
+        "invalid",
+        "ip6-localhost"
+      };
+    const char *host = hosts[random () % 3];
+
+    sp->query1 = async_addr_from_name_start (MI_DISPLAY(mi), host);
+
+    assert (sp->query1);
+
+    fprintf (stderr, "Looking up address from hostname: %s", host);
+#   if ASYNC_NETDB_USE_GAI
+    fputs (" @ ", stderr);
+    _print_thread (sp->query1->io.thread, stderr);
+#   endif
+    putc ('\n', stderr);
+
+    if (!(random () & 3))
+      {
+        fputs ("Aborted address lookup (early)\n", stderr);
+        async_addr_from_name_cancel (sp->query1);
+        sp->query1 = NULL;
+      }
+  }
+
+  fflush (stderr);
+# endif
 }
 
 
@@ -923,6 +1065,8 @@ draw_sonar (ModeInfo *mi)
     }
 
   glPushMatrix ();
+  glRotatef(current_device_rotation(), 0, 0, 1);
+
   {
     GLfloat s = 7;
     if (MI_WIDTH(mi) < MI_HEIGHT(mi))
@@ -931,7 +1075,6 @@ draw_sonar (ModeInfo *mi)
   }
 
   gltrackball_rotate (sp->trackball);
-  glRotatef(current_device_rotation(), 0, 0, 1);
 
   if (wobble_p)
     {
@@ -969,8 +1112,37 @@ draw_sonar (ModeInfo *mi)
   glCallList (sp->grid_list);
   mi->polygon_count += sp->screen_polys;
 
-  if (! sp->ssd || sp->error)
+  glPushMatrix();
+  mi->polygon_count += draw_angles (mi);               /* angles */
+  glPopMatrix();
+
+  if (sp->desc)                                                /* local subnet */
+    {
+      glPushMatrix();
+      glTranslatef (0, 0, 0.00002);
+      mi->polygon_count += draw_text (mi, sp->desc, 1.35, M_PI * 0.75, 0, 10);
+      /* glRotatef (45, 0, 0, 1); */
+      /* mi->polygon_count += draw_text (mi, sp->desc, 1.2, M_PI/2, 0, 10); */
+      glPopMatrix();
+    }
+
+  if (sp->error)
+    sp->state = MSG;
+
+  switch (sp->state) {
+  case MSG:                    /* Frame 1: get "Resolving Hosts" on screen. */
     draw_startup_blurb(mi);
+    sp->state++;
+    break;
+  case RESOLVE:                        /* Frame 2: gethostbyaddr may take a while. */
+    if (! sp->ssd)
+      init_sensor (mi);
+    sp->state++;
+    break;
+  case RUN:                    /* Frame N: ping away */
+    sweep (sp);
+    break;
+  }
 
   glPopMatrix ();
 
@@ -979,15 +1151,75 @@ draw_sonar (ModeInfo *mi)
 
   glXSwapBuffers(dpy, window);
 
-  if (! sp->ssd)
-    /* Just starting up.  "Resolving hosts" text printed.  Go stall. */
-    init_sensor (mi);
-  else
-    sweep (sp);
+# if TEST_ASYNC_NETDB
+  if(sp->query0 && async_name_from_addr_is_done (sp->query0))
+    {
+      if (!(random () & 3))
+        {
+          fputs ("Aborted hostname lookup (late)\n", stderr);
+          async_name_from_addr_cancel (sp->query0);
+        }
+      else
+        {
+          char *hostname = NULL;
+          int errno_error;
+          int gai_error = async_name_from_addr_finish (sp->query0, &hostname,
+                                                       &errno_error);
+
+          if(gai_error)
+            {
+              fputs ("Couldn't get hostname: ", stderr);
+              _print_error (gai_error, errno_error, stderr);
+              putc ('\n', stderr);
+            }
+          else
+            {
+              fprintf (stderr, "Got a hostname: %s\n", hostname);
+              free (hostname);
+            }
+        }
+
+      sp->query0 = NULL;
+    }
+
+  if(sp->query1 && async_addr_from_name_is_done (sp->query1))
+    {
+      if (!(random () & 3))
+        {
+          fputs ("Aborted address lookup (late)\n", stderr);
+          async_addr_from_name_cancel (sp->query1);
+        }
+      else
+        {
+          async_netdb_sockaddr_storage_t addr;
+          socklen_t addrlen;
+          int errno_error;
+          int gai_error = async_addr_from_name_finish (sp->query1, &addr,
+                                                       &addrlen, &errno_error);
+
+          if (gai_error)
+            {
+              fputs ("Couldn't get address: ", stderr);
+              _print_error (gai_error, errno_error, stderr);
+              putc ('\n', stderr);
+            }
+          else
+            {
+              fputs ("Got an address: ", stderr);
+              _print_sockaddr (&addr, addrlen, stderr);
+              putc ('\n', stderr);
+            }
+        }
+
+      sp->query1 = NULL;
+    }
+
+  fflush (stderr);
+# endif /* TEST_ASYNC_NETDB */
 }
 
-ENTRYPOINT void
-release_sonar (ModeInfo *mi)
+static void
+free_sonar (ModeInfo *mi)
 {
 #if 0
   sonar_configuration *sp = &sps[MI_SCREEN(mi)];