-/* DNA Logo, Copyright (c) 2001-2015 Jamie Zawinski <jwz@jwz.org>
+/* DNA Logo, Copyright (c) 2001-2018 Jamie Zawinski <jwz@jwz.org>
*
* DNA Lounge
*
* San Francisco, CA
* 94103
*
- * http://www.dnalounge.com/
+ * https://www.dnalounge.com/
* http://www.dnapizza.com/
*
* Permission to use, copy, modify, distribute, and sell this software and its
"*wallFacets: 360 \n" \
"*barFacets: 90 \n" \
"*clockwise: False \n" \
- "*turns: 0.69 \n" \
- "*turnSpacing: 2.2 \n" \
- "*barSpacing: 0.24 \n" \
- "*wallHeight: 0.45 \n" \
+ "*turns: 0.72 \n" \
+ "*turnSpacing: 2.3 \n" \
+ "*barSpacing: 0.268 \n" \
+ "*wallHeight: 0.42 \n" \
"*wallThickness: 0.12 \n" \
"*barThickness: 0.058 \n" \
"*wallTaper: 0.95 \n" \
- "*gasketSize: 1.88 \n" \
+ "*gasketSize: 2.0 \n" \
"*gasketDepth: 0.15 \n" \
"*gasketThickness: 0.4 \n" \
- "*frameSize: 1.20 \n" \
+ "*frameSize: 1.28 \n" \
"*frameDepth: 0.01 \n" \
"*frameThickness: 0.03 \n" \
"*triangleSize: 0.045 \n" \
"*cwFont: " CWFONT "\n" \
"*geometry: =640x640\n" \
-# if defined(HAVE_COCOA)
+# if defined(HAVE_COCOA) || defined(HAVE_ANDROID)
# define CWFONT "Yearling 28, OCR A Std 24"
# else
# define CWFONT "-*-helvetica-medium-r-normal-*-*-240-*-*-*-*-*-*"
# endif
-# define refresh_logo 0
+# define free_logo 0
# define release_logo 0
#undef countof
#define countof(x) (sizeof((x))/sizeof((*x)))
-#undef LINEAR
#undef DXF_OUTPUT_HACK
#ifdef DXF_OUTPUT_HACK /* When this is defined, instead of rendering
HELIX_IN, HELIX, HELIX_OUT,
PIZZA_IN, PIZZA, PIZZA_OUT,
HELIX_AND_PIZZA,
+# ifdef CW
CODEWORD_IN, CODEWORD, CODEWORD_OUT, CODEWORD_BLANK
+# endif
} glyph_mode;
typedef struct {
Bool spinning_p;
- GLfloat position; /* 0.0 - 1.0 */
- GLfloat speed; /* how far along the path (may be negative) */
- GLfloat probability; /* relative likelyhood to start spinning */
+ GLfloat position; /* 0.0 - 1.0 */
+ GLfloat position_eased; /* 0.0 - 1.0, eased in and out */
+ GLfloat easement; /* portion of path that is eased. <= 0.5 */
+ GLfloat speed; /* how far along the path (may be negative) */
+ GLfloat probability; /* relative likelyhood to start spinning */
} spinner;
typedef struct {
GLfloat frame_thickness;
GLfloat triangle_size;
+# ifdef CW
int codeword_facets, codeword_disc_facets;
GLfloat codeword_spread, codeword_line_width, codeword_thickness;
GLfloat codeword_cap_size;
XYZ *codeword_guides;
GLfloat codeword_color[4], codeword_bg[4];
texture_font_data *font;
+# endif
+
+# ifdef DEBUG
+ GLfloat persp_off, pos_off;
+ texture_font_data *label_font;
+# endif
GLfloat speed;
glyph_mode mode;
spinner gasket_spinnerx, gasket_spinnery, gasket_spinnerz;
spinner scene_spinnerx, scene_spinnery; /* for DNA */
+# ifdef CW
rotator *scene_rot; /* for Codeword */
+# endif
spinner helix_spinnerz;
spinner pizza_spinnery, pizza_spinnerz;
spinner frame_spinner;
{ "-pizza", ".mode", XrmoptionNoArg, "pizza" },
{ "-helix", ".mode", XrmoptionNoArg, "helix" },
{ "-both", ".mode", XrmoptionNoArg, "both" },
+# ifdef CW
{ "-codeword", ".mode", XrmoptionNoArg, "codeword" },
{ "-cw", ".mode", XrmoptionNoArg, "codeword" },
{ "-text", ".text", XrmoptionSepArg, 0 },
+# endif
};
ENTRYPOINT ModeSpecOpt logo_opts = {countof(opts), opts, 0, NULL, NULL};
return (angle);
}
+
+# ifdef CW
+
static void
normalize (XYZ *p)
{
return (u.x * v.x) + (u.y * v.y) + (u.z * v.z);
}
+#endif /* CW */
+
\f
/* Make the helix
*/
th = 0;
x1 = 1;
y1 = 0;
- x1b = 1 - dc->wall_thickness;
+ x1b = 1;
y1b = 0;
z1 = -(dc->turn_spacing * dc->turns / 2);
while (th + th_inc <= max_th)
{
+ GLfloat thick = dc->wall_thickness;
+
th += th_inc;
x2 = cos (th);
y2 = sin (th);
z2 = z1 + z_inc;
- x2b = x2 * (1 - dc->wall_thickness);
- y2b = y2 * (1 - dc->wall_thickness);
h2 = h1;
h2off = h1off;
h2off = h2 - dc->wall_height/2;
else
h2off = dc->wall_height/2 - h2;
+
+ if (th + th_inc <= 0)
+ thick = 0;
+ else
+ thick *= cos (M_PI / 2 * (1 - (th / dc->wall_taper)));
}
else if (th >= max_th - dc->wall_taper)
{
h2off = dc->wall_height/2 - h2;
else
h2off = h2 - dc->wall_height/2;
+
+ if (th + th_inc > max_th)
+ thick = 0;
+ else
+ thick *= cos(M_PI / 2 * (1 - ((max_th - th)/dc->wall_taper)));
}
}
+ x2b = x2 * (1 - thick);
+ y2b = y2 * (1 - thick);
+
/* outer face
*/
glFrontFace(GL_CW);
th = (max_th * pad_ratio/2);
z = -(max_z / 2) + (max_z * pad_ratio/2);
+ /* ##### WHYYYYYY */
+ /* The image is not reflected across line y = -x and I don't know why. */
+ th += M_PI * -0.035;
+ z -= 0.08;
+
if (!dc->clockwise)
z = -z, z_inc = -z_inc;
GLfloat th, x, y, s;
int i, j, k;
int endpoints;
- int endedge1;
# ifdef HAVE_TESS
tess_out TO, *to = &TO;
points[j++] = edge[i*2];
points[j++] = 0;
}
- endedge1 = i;
}
s = 0.798; /* radius of end of slice, before crust gap */
# else /* !HAVE_TESS */
if (! wire)
{
+ glTranslatef(0, 0, thick2);
+ glNormal3f (0, 0, 1);
+ glFrontFace (GL_CW);
+
+ /* Sadly, jwzgl's glVertexPointer seems not to be recordable inside
+ display lists. */
+# if 0
glDisableClientState (GL_COLOR_ARRAY);
glDisableClientState (GL_NORMAL_ARRAY);
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
glEnableClientState (GL_VERTEX_ARRAY);
glVertexPointer (3, GL_FLOAT, 0, dnapizza_triangles);
-
- glTranslatef(0, 0, thick2);
- glNormal3f (0, 0, 1);
- glFrontFace (GL_CW);
glDrawArrays (GL_TRIANGLES, 0, countof (dnapizza_triangles) / 3);
+# else
+ glBegin (GL_TRIANGLES);
+ for (i = 0; i < countof (dnapizza_triangles); i += 3)
+ glVertex3fv (dnapizza_triangles + i);
+ glEnd();
+# endif
glTranslatef(0, 0, -thick2*2);
glNormal3f (0, 0, -1);
glFrontFace (GL_CCW);
+
+# if 0
glDrawArrays (GL_TRIANGLES, 0, countof (dnapizza_triangles) / 3);
+# else
+ int i;
+ glBegin (GL_TRIANGLES);
+ for (i = 0; i < countof (dnapizza_triangles); i += 3)
+ glVertex3fv (dnapizza_triangles + i);
+ glEnd();
+# endif
glTranslatef(0, 0, thick2);
}
}
+# ifdef CW
+
/* Upcase string, convert Unicrud to ASCII, remove any non-letters.
*/
static char *
int dial = 0;
int letter;
- GLfloat last_r;
+ GLfloat last_r = 0;
GLfloat inner_circum = M_PI * 2 * (iradius + rtick * 2);
GLfloat outer_circum = M_PI * 2 * (iradius + rtick * (letters + 1));
buf[1] = 0;
texture_string_metrics (dc->font, buf, &e, &ascent, &descent);
-# ifdef USE_IPHONE
+# ifdef HAVE_MOBILE
/* #### Magic magic magic WTF... */
glScalef (0.5, 0.5, 0.5);
# endif
glColor4fv (dc->codeword_color);
glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, dc->codeword_color);
-# ifdef USE_IPHONE /* Make the whole thing fit on the phone screen */
+# ifdef HAVE_MOBILE /* Make the whole thing fit on the phone screen */
{
GLfloat size = MI_WIDTH(mi) < MI_HEIGHT(mi) ? MI_WIDTH(mi) : MI_HEIGHT(mi);
glScalef (0.9, 0.9, 0.9);
/* Draw the start and end caps */
{
int i;
- GLfloat x, y, z, x2, y2, z2, X, Y, Z, L;
+ GLfloat x, y, z, x2, y2, z2, X, Y, Z;
GLfloat r = dc->codeword_spread * dc->codeword_cap_size;
i = 0;
X = (x2 - x);
Y = (y2 - y);
Z = (z2 - z);
- L = sqrt (X*X + Y*Y + Z*Z);
glPushMatrix();
glTranslatef (x, y, z);
return polys;
}
+#endif /* CW */
+
-\f
-/* Window management, etc
- */
ENTRYPOINT void
reshape_logo (ModeInfo *mi, int width, int height)
{
+# ifdef DEBUG
+ logo_configuration *dc = &dcs[MI_SCREEN(mi)];
+# endif
GLfloat h = (GLfloat) height / (GLfloat) width;
+ GLfloat persp = 64; /* 30 */
+ GLfloat pos = 13; /* 30 */
+
+# ifdef DEBUG
+ persp += dc->persp_off;
+ pos += dc->pos_off;
+# endif
glViewport (0, 0, (GLint) width, (GLint) height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
- gluPerspective (30.0, 1/h, 1.0, 100.0);
+ gluPerspective (persp, 1/h, 1, 100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
- gluLookAt( 0.0, 0.0, 30.0,
- 0.0, 0.0, 0.0,
- 0.0, 1.0, 0.0);
+ gluLookAt( 0, 0, pos,
+ 0, 0, 0,
+ 0, 1, 0);
+
+# ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
+ {
+ int o = (int) current_device_rotation();
+ if (o != 0 && o != 180 && o != -180)
+ glScalef (1/h, 1/h, 1/h); /* #### Why does this change the lighting? */
+ }
+# endif
glClear(GL_COLOR_BUFFER_BIT);
}
exit (1);
}
- if (!dcs) {
- dcs = (logo_configuration *)
- calloc (MI_NUM_SCREENS(mi), sizeof (logo_configuration));
- if (!dcs) {
- fprintf(stderr, "%s: out of memory\n", progname);
- exit(1);
- }
- }
+ MI_INIT (mi, dcs);
dc = &dcs[MI_SCREEN(mi)];
dc->triangle_size = get_float_resource(mi->dpy, "triangleSize", "Float");
dc->speed = get_float_resource(mi->dpy, "speed", "Float");
+# ifdef CW
dc->codeword_text = get_string_resource(mi->dpy, "text", "String");
dc->codeword_text = codeword_simplify_text (dc->codeword_text);
dc->codeword_text_out =
dc->codeword_line_width = get_float_resource(mi->dpy, "cwLineWidth", "Float");
dc->codeword_thickness = get_float_resource(mi->dpy, "cwThickness", "Float");
dc->codeword_cap_size = get_float_resource(mi->dpy, "cwCapSize", "Float");
+# endif
{
char *s = get_string_resource (MI_DISPLAY (mi), "mode", "String");
dc->mode = PIZZA;
else if (!strcasecmp (s, "both"))
dc->mode = HELIX_AND_PIZZA;
+# ifdef CW
else if (!strcasecmp (s, "codeword"))
dc->mode = CODEWORD_IN;
+# endif
else
{
fprintf (stderr,
- "%s: mode must be helix, pizza, both or codeword, not \"%s\"\n",
+ "%s: mode must be helix, pizza or both, not \"%s\"\n",
progname, s);
exit (1);
}
dc->anim_ratio = 0;
}
+# ifdef CW
if (dc->mode == CODEWORD_IN)
dc->font = load_texture_font (MI_DISPLAY(mi), "cwFont");
+# endif
+
+# ifdef DEBUG
+ dc->label_font = load_texture_font (MI_DISPLAY(mi), "fpsFont");
+# endif
{
XColor xcolor;
exit (1);
}
+# ifdef CW
dc->codeword_color[0] = xcolor.red / 65535.0;
dc->codeword_color[1] = xcolor.green / 65535.0;
dc->codeword_color[2] = xcolor.blue / 65535.0;
dc->codeword_bg[1] = xcolor.green / 65535.0;
dc->codeword_bg[2] = xcolor.blue / 65535.0;
dc->codeword_bg[3] = 1.0;
+# endif /* CW */
}
dc->trackball = gltrackball_init (False);
dc->gasket_spinnerx.probability = 0.1;
dc->gasket_spinnery.probability = 0.1;
dc->gasket_spinnerz.probability = 1.0;
+ dc->gasket_spinnerx.easement = 0.08;
+ dc->gasket_spinnery.easement = 0.08;
+ dc->gasket_spinnerz.easement = 0.08;
dc->helix_spinnerz.probability = 0.6;
+ dc->helix_spinnerz.easement = 0.2;
dc->pizza_spinnerz.probability = 0.6;
dc->pizza_spinnery.probability = 0.6;
+ dc->pizza_spinnerz.easement = 0.2;
+ dc->pizza_spinnery.easement = 0.2;
dc->frame_spinner.probability = 5.0;
+ dc->frame_spinner.easement = 0.2;
dc->scene_spinnerx.probability = 0.1;
dc->scene_spinnery.probability = 0.0;
+ dc->scene_spinnerx.easement = 0.1;
+ dc->scene_spinnery.easement = 0.1;
+# ifdef CW
if (dc->mode == CODEWORD_IN)
{
double tilt_speed = 0.003;
dc->scene_rot = make_rotator (0, 0, 0, 0, tilt_speed, True);
}
+# endif
/* start the frame off-screen */
dc->frame_spinner.spinning_p = True;
if (do_frame) dc->polys[6] += make_frame (dc, 1);
glEndList ();
+# ifdef CW
make_codeword_path (mi);
-
+# endif
/* When drawing both solid and wireframe objects,
make sure the wireframe actually shows up! */
KeySym keysym;
char c = 0;
XLookupString (&event->xkey, &c, 1, &keysym, 0);
+
+# ifdef DEBUG
+ {
+ GLfloat step = 0.1;
+ if (c == 'a') dc->persp_off += step;
+ else if (c == 'z') dc->persp_off -= step;
+ else if (c == 's') dc->pos_off += step;
+ else if (c == 'x') dc->pos_off -= step;
+ else return False;
+
+ /* dc->pos_off = -dc->persp_off; */
+ reshape_logo (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+ return True;
+ }
+# endif
+
if (c == ' ' || c == '\t')
{
switch (dc->anim_state) {
dc->anim_state = PIZZA_OUT;
dc->anim_ratio = 0.0;
return True;
+# ifdef CW
case CODEWORD:
dc->anim_state = CODEWORD_OUT;
dc->anim_ratio = 0.0;
return True;
+# endif
default:
break;
}
}
+static GLfloat
+spinner_ease (GLfloat x)
+{
+ /* Smooth curve up, ending at slope = 1. */
+ return cos ((x/2 + 1) * M_PI) + 1;
+}
+
+
static void
tick_spinner (ModeInfo *mi, spinner *s)
{
if (s->spinning_p)
{
s->position += s->speed;
- if (s->position >= 1.0 || s->position <= -1.0)
-
+ if (s->position >= 1.0 || s->position <= 0.0)
{
s->position = 0;
+ s->position_eased = 0;
s->spinning_p = False;
}
+ else if (s->easement > 0 && s->position <= s->easement)
+ s->position_eased = (s->easement *
+ spinner_ease (s->position / s->easement));
+ else if (s->easement > 0 && s->position >= 1-s->easement)
+ s->position_eased = (1 - s->easement *
+ spinner_ease ((1 - s->position) / s->easement));
+ else
+ s->position_eased = s->position;
}
else if (s->probability &&
(random() % (int) (PROBABILITY_SCALE / s->probability)) == 0)
s->speed = dc->speed * (frand(ss/3) + frand(ss/3) + frand(ss/3));
} while (s->speed <= 0);
if (random() & 1)
- s->speed = -s->speed;
+ {
+ s->speed = -s->speed;
+ s->position = 1.0;
+ }
}
}
GLfloat specular[] = {0.8, 0.8, 0.8, 1.0};
GLfloat shininess = 50.0;
Bool pizza_p;
+# ifdef CW
Bool codeword_p;
+# endif
if (!dc->glx_context)
return;
(random() % 200) +
(random() % 200));
+# ifndef DEBUG
tick_spinner (mi, &dc->gasket_spinnerx);
tick_spinner (mi, &dc->gasket_spinnery);
tick_spinner (mi, &dc->gasket_spinnerz);
tick_spinner (mi, &dc->scene_spinnery);
tick_spinner (mi, &dc->frame_spinner);
link_spinners (mi, &dc->scene_spinnerx, &dc->scene_spinnery);
-
-# ifdef LINEAR
- {
- static double i = 0.0;
- dc->anim_state = HELIX;
- dc->wire_overlay = 0;
- dc->gasket_spinnerx.spinning_p = 0;
- dc->gasket_spinnery.spinning_p = 0;
- dc->gasket_spinnerz.spinning_p = 0;
- dc->helix_spinnerz.spinning_p = 0;
- dc->pizza_spinnery.spinning_p = 0;
- dc->pizza_spinnerz.spinning_p = 0;
- dc->scene_spinnerx.spinning_p = 0;
- dc->scene_spinnery.spinning_p = 0;
- dc->frame_spinner.spinning_p = 0;
- dc->frame_spinner.position = 0.3;
- dc->gasket_spinnerz.position = i;
- dc->helix_spinnerz.position = i;
- i += 0.005;
- if (i > 1) i = 0;
- }
-# endif /* LINEAR */
+# endif /* DEBUG */
switch (dc->anim_state)
{
break;
+# ifdef CW
case CODEWORD_IN:
dc->scene_spinnerx.probability = 0.2;
dc->scene_spinnery.probability = 0.05;
break;
case CODEWORD:
- dc->scene_spinnerx.probability = 2.5;
+ dc->scene_spinnerx.probability = 0.5;
dc->scene_spinnery.probability = 0.2;
if (! dc->button_down_p)
dc->anim_ratio += (0.0005 + frand(0.002)) * dc->speed;
dc->anim_state = CODEWORD_IN;
}
break;
+# endif /* CW */
default:
abort();
break;
}
+# ifdef DEBUG
+ dc->anim_state = HELIX;
+ dc->wire_overlay = 0;
+# endif
+
pizza_p = (dc->anim_state == PIZZA ||
dc->anim_state == PIZZA_IN ||
dc->anim_state == PIZZA_OUT);
+# ifdef CW
codeword_p = (dc->anim_state == CODEWORD ||
dc->anim_state == CODEWORD_IN ||
dc->anim_state == CODEWORD_OUT ||
dc->anim_state == CODEWORD_BLANK);
+# endif
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glRotatef(current_device_rotation(), 0, 0, 1);
{
GLfloat scale = 1.8;
-# ifdef LINEAR
- scale = 3.85;
-# endif
glScalef(scale, scale, scale);
glColor3f(dc->color[0], dc->color[1], dc->color[2]);
/* Draw frame before trackball rotation */
+# ifdef CW
if (! codeword_p)
+# endif
{
- GLfloat p = (dc->frame_spinner.position >= 0
- ? dc->frame_spinner.position
- : -dc->frame_spinner.position);
+ GLfloat p = (dc->frame_spinner.position_eased >= 0
+ ? dc->frame_spinner.position_eased
+ : -dc->frame_spinner.position_eased);
GLfloat size = (p > 0.5 ? 1-p : p);
scale = 1 + (size * 10);
glPushMatrix();
glRotatef(90, 1, 0, 0);
glRotatef(90, 0, 0, 1);
-# ifdef LINEAR
-# define SINIFY(I) (I)
-# else
-# define SINIFY(I) sin (M_PI/2 * (I))
-# endif
-
+# ifdef CW
if (! codeword_p)
+# endif
{
- glRotatef (360 * SINIFY (dc->scene_spinnerx.position), 0, 1, 0);
- glRotatef (360 * SINIFY (dc->scene_spinnery.position), 0, 0, 1);
+ glRotatef (360 * dc->scene_spinnerx.position_eased, 0, 1, 0);
+ glRotatef (360 * dc->scene_spinnery.position_eased, 0, 0, 1);
glPushMatrix();
- glRotatef (360 * SINIFY (dc->gasket_spinnerx.position), 0, 1, 0);
- glRotatef (360 * SINIFY (dc->gasket_spinnery.position), 0, 0, 1);
- glRotatef (360 * SINIFY (dc->gasket_spinnerz.position), 1, 0, 0);
+ glRotatef (360 * dc->gasket_spinnerx.position_eased, 0, 1, 0);
+ glRotatef (360 * dc->gasket_spinnery.position_eased, 0, 0, 1);
+ glRotatef (360 * dc->gasket_spinnerz.position_eased, 1, 0, 0);
memcpy (gcolor, dc->color, sizeof (dc->color));
if (dc->wire_overlay != 0)
if (pizza_p)
{
- glRotatef (360 * SINIFY (dc->pizza_spinnery.position), 1, 0, 0);
- glRotatef (360 * SINIFY (dc->pizza_spinnerz.position), 0, 0, 1);
+ glRotatef (360 * dc->pizza_spinnery.position_eased, 1, 0, 0);
+ glRotatef (360 * dc->pizza_spinnerz.position_eased, 0, 0, 1);
}
else
{
- glRotatef (360 * SINIFY (dc->helix_spinnerz.position), 0, 0, 1);
+ glRotatef (360 * dc->helix_spinnerz.position_eased, 0, 0, 1);
}
scale = ((dc->anim_state == PIZZA_IN || dc->anim_state == HELIX_IN)
mi->polygon_count += dc->polys[0];
}
}
+# ifdef CW
else /* codeword_p */
{
-# if 0
+# if 0
double max = 70; /* face front */
double x, y, z;
get_position (dc->scene_rot, &x, &y, &z, !dc->button_down_p);
glRotatef (max/2 - x*max, 0, 0, 1);
glRotatef (max/2 - y*max, 0, 1, 0);
/* glRotatef (max/2 - z*max, 1, 0, 0); */
-# else
- glRotatef (360 * SINIFY (dc->scene_spinnerx.position), 0, 1, 0);
- glRotatef (360 * SINIFY (dc->scene_spinnery.position), 0, 0, 1);
-# endif
+# else
+ glRotatef (360 * dc->scene_spinnerx.position_eased, 0, 1, 0);
+ glRotatef (360 * dc->scene_spinnery.position_eased, 0, 0, 1);
+# endif
glClearColor (dc->codeword_bg[0],
dc->codeword_bg[1],
dc->codeword_bg[3]);
mi->polygon_count += draw_codeword_path (mi);
}
+# endif /* CW */
}
glPopMatrix();
if (dc->wire_overlay > 0)
dc->wire_overlay--;
+# ifdef DEBUG
+ {
+ char s[1024];
+ sprintf (s, "a/z, s/x; per = %0.2f pos = %0.2f",
+ dc->persp_off, dc->pos_off);
+ glColor3f (1,1,1);
+ print_texture_label (dpy, dc->label_font, MI_WIDTH(mi), MI_HEIGHT(mi),
+ 1, s);
+ }
+# endif
+
if (mi->fps_p) do_fps (mi);
glFinish();