-/* sonar, Copyright (c) 1998-2012 Jamie Zawinski and Stephen Martin
+/* sonar, Copyright (c) 1998-2018 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
* - 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;
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;
{ "+times", ".times", XrmoptionNoArg, "False" },
{ "-wobble", ".wobble", XrmoptionNoArg, "True" },
{ "+wobble", ".wobble", XrmoptionNoArg, "False" },
+ THREAD_OPTIONS
{ "-debug", ".debug", XrmoptionNoArg, "True" },
};
char *token = string2;
char *line;
+ if (MI_WIDTH(mi) > 2560) font_scale /= 2; /* Retina displays */
+
if (size <= 0) /* if size not specified, draw in yellow with alpha */
{
GLfloat color[4];
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;
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)
{
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;
}
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)
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 + 6 < 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;
+ }
}
}
{
sonar_configuration *sp = &sps[MI_SCREEN(mi)];
GLfloat h = (GLfloat) height / (GLfloat) width;
+ int y = 0;
+
+ if (width > height * 5) { /* tiny window: show middle */
+ height = width * 9/16;
+ y = -height/2;
+ h = height / (GLfloat) width;
+ }
- glViewport (0, 0, (GLint) width, (GLint) height);
+ glViewport (0, y, (GLint) width, (GLint) height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
{
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;
}
-
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);
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");
}
+# 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)
{
if (!ping_arg || !*ping_arg ||
!strcmp(ping_arg, "default") ||
!!strcmp (ping_arg, "simulation"))
+ /* 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. */
sp->start_time = double_time (); /* for error message timing */
- /* Disavow privs. This was already done in init_ping(), but
- we might not have called that at all, so do it again. */
- setuid(getuid());
-
if (!sp->ssd)
sp->ssd = sonar_init_simulation (MI_DISPLAY (mi), &sp->error, &sp->desc,
team_a_name, team_b_name,
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
}
glCallList (sp->grid_list);
mi->polygon_count += sp->screen_polys;
+ glPushMatrix();
+ mi->polygon_count += draw_angles (mi); /* angles */
+ glPopMatrix();
+
if (sp->desc) /* local subnet */
{
glPushMatrix();
glFinish();
glXSwapBuffers(dpy, window);
+
+# 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)
+free_sonar (ModeInfo *mi)
{
#if 0
sonar_configuration *sp = &sps[MI_SCREEN(mi)];