1 /* unknownpleasures, Copyright (c) 2013-2018, 2019
2  * -Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or
10  * implied warranty.
11  *
12  * A very particular waterfall chart.
13  *
14  * Interestingly, the original image is copyright-free:
15  * http://adamcap.com/2011/05/19/history-of-joy-division-unknown-pleasures-album-art/
16  * https://blogs.scientificamerican.com/sa-visual/pop-culture-pulsar-origin-story-of-joy-division-s-unknown-pleasures-album-cover-video/
17  * https://en.wikipedia.org/wiki/Unknown_Pleasures
18  *
19  *   "Eighty successive periods of the first pulsar observed, CP1919
20  *   (Cambridge pulsar at 19 hours 19 minutes right ascension), are stacked
21  *   on top of one another using the average period of 1.33730 seconds in
22  *   this computer-generated illustration produced at the Arecibo Radio
23  *   Observatory in Puerto Rico. Athough the leading edges of the radio
24  *   pulses occur within a few thousandths of a second of the predicted
25  *   times, the shape of the pulses is quite irregular. Some of this
26  *   irregularity in radio reception is caused by the effects of
27  *   transmission through the interstellar medium. The average pulse width
28  *   is less than 50 thousandths of a second."
29  *
30  * TODO:
31  *
32  * - Take a function generator program as a command line argument:
33  *   read lines of N float values from it, interpolate to full width.
34  */
35 
36 #define DEF_ORTHO      "True"
37 #define DEF_SPEED      "1.0"
38 #define DEF_RESOLUTION "100"
39 #define DEF_AMPLITUDE  "0.13"
40 #define DEF_NOISE      "1.0"
41 #define DEF_ASPECT     "1.9"
42 #define DEF_BUZZ       "False"
43 #define DEF_MASK       "(none)"
44 
45 #define DEFAULTS	"*delay:	30000       \n" \
46 			"*count:        80          \n" \
47 			"*showFPS:      False       \n" \
48 			"*wireframe:    False       \n" \
49 			"*geometry:    =800x800"   "\n" \
50 
51 # define release_unk 0
52 #undef countof
53 #define countof(x) (sizeof((x))/sizeof((*x)))
54 
55 #include "xlockmore.h"
56 #include "colors.h"
57 #include "gltrackball.h"
58 #include "ximage-loader.h"
59 #include "grab-ximage.h"
60 #include <ctype.h>
61 
62 #ifdef USE_GL /* whole file */
63 
64 #undef DEBUG
65 
66 Bool ortho_arg;
67 GLfloat speed_arg;
68 int resolution_arg;
69 GLfloat amplitude_arg;
70 GLfloat noise_arg;
71 GLfloat aspect_arg;
72 Bool buzz_arg;
73 char *mask_arg;
74 
75 
76 typedef struct {
77   GLXContext *glx_context;
78   trackball_state *trackball;
79   Bool button_down_p;
80   Bool orthop;
81   double speed, tick;
82   int resolution;	/* X points */
83   int count;		/* Y lines */
84   GLfloat amplitude;	/* Z height */
85   int frames;		/* Number of renders of each line, for buzzing */
86   GLfloat noise;	/* Number of peaks in each line */
87   GLfloat aspect;	/* Shape of the plot */
88   GLuint base;		/* Display list for back */
89   GLuint *lines;	/* Display lists for each edge * face * frame */
90   GLfloat *heights;	/* Animated elevation / alpha of each line */
91   GLfloat fg[4], bg[4];	/* Colors */
92   XImage *mask;
93   double mask_scale;
94   int frame_count;
95 } unk_configuration;
96 
97 static unk_configuration *bps = NULL;
98 
99 static XrmOptionDescRec opts[] = {
100   { "-speed",        ".speed",      XrmoptionSepArg, 0 },
101   { "-resolution",   ".resolution", XrmoptionSepArg, 0 },
102   { "-amplitude",    ".amplitude",  XrmoptionSepArg, 0 },
103   { "-noise",        ".noise",      XrmoptionSepArg, 0 },
104   { "-aspect",       ".aspect",     XrmoptionSepArg, 0 },
105   { "-ortho",        ".ortho",      XrmoptionNoArg,  "True"  },
106   { "-no-ortho",     ".ortho",      XrmoptionNoArg,  "False" },
107   { "-buzz",         ".buzz",       XrmoptionNoArg,  "True"  },
108   { "-no-buzz",      ".buzz",       XrmoptionNoArg,  "False" },
109   { "-mask",         ".mask",       XrmoptionSepArg, 0 },
110 };
111 
112 static argtype vars[] = {
113   {&ortho_arg,      "ortho",       "Ortho",       DEF_ORTHO,      t_Bool},
114   {&speed_arg,      "speed",       "Speed",       DEF_SPEED,      t_Float},
115   {&resolution_arg, "resolution",  "Resolution",  DEF_RESOLUTION, t_Int},
116   {&amplitude_arg,  "amplitude",   "Amplitude",   DEF_AMPLITUDE,  t_Float},
117   {&noise_arg,      "noise",       "Noise",       DEF_NOISE,      t_Float},
118   {&aspect_arg,     "aspect",      "Aspect",      DEF_ASPECT,     t_Float},
119   {&buzz_arg,       "buzz",        "Buzz",        DEF_BUZZ,       t_Bool},
120   {&mask_arg,       "mask",        "Image",       DEF_MASK,       t_String},
121 };
122 
123 ENTRYPOINT ModeSpecOpt unk_opts = {countof(opts), opts, countof(vars), vars, NULL};
124 
125 
126 
127 /* Returns the current time in seconds as a double.
128  */
129 static double
double_time(void)130 double_time (void)
131 {
132   struct timeval now;
133 # ifdef GETTIMEOFDAY_TWO_ARGS
134   struct timezone tzp;
135   gettimeofday(&now, &tzp);
136 # else
137   gettimeofday(&now);
138 # endif
139 
140   return (now.tv_sec + ((double) now.tv_usec * 0.000001));
141 }
142 
143 
144 static void
parse_color(ModeInfo * mi,char * res,char * class,GLfloat * a)145 parse_color (ModeInfo *mi, char *res, char *class, GLfloat *a)
146 {
147   XColor c;
148   char *s = get_string_resource(mi->dpy, res, class);
149   if (! XParseColor (MI_DISPLAY(mi), MI_COLORMAP(mi), s, &c))
150     {
151       fprintf (stderr, "%s: can't parse %s color %s", progname, res, s);
152       exit (1);
153     }
154   if (s) free (s);
155   a[0] = c.red   / 65536.0;
156   a[1] = c.green / 65536.0;
157   a[2] = c.blue  / 65536.0;
158   a[3] = 1.0;
159 }
160 
161 
162 ENTRYPOINT void
reshape_unk(ModeInfo * mi,int width,int height)163 reshape_unk (ModeInfo *mi, int width, int height)
164 {
165   unk_configuration *bp = &bps[MI_SCREEN(mi)];
166   int wire = MI_IS_WIREFRAME(mi);
167   GLfloat h = (GLfloat) height / (GLfloat) width;
168   int y = 0;
169   int i;
170   int new_count;
171 
172   if (width > height * 5) {   /* tiny window: show middle */
173     height = width*1.5;
174     y = -height/2;
175     h = height / (GLfloat) width;
176   }
177 
178   glViewport (0, y, (GLint) width, (GLint) height);
179 
180   if (bp->orthop)
181     {
182       int magic = 700;
183       int range = 30; /* Must be small or glPolygonOffset doesn't work. */
184       glMatrixMode(GL_PROJECTION);
185       glLoadIdentity();
186       gluPerspective (1.0, 1/h, magic-range/2, magic+range/2);
187 
188       glMatrixMode(GL_MODELVIEW);
189       glLoadIdentity();
190       gluLookAt( 0, 0, magic,
191                  0, 0, 0,
192                  0, 1, 0);
193       if (width < height)
194         glScalef (1/h, 1/h, 1);
195       glTranslatef (0, -0.5, 0);
196     }
197   else
198     {
199       glMatrixMode(GL_PROJECTION);
200       glLoadIdentity();
201       gluPerspective (30.0, 1/h, 1.0, 100.0);
202 
203       glMatrixMode(GL_MODELVIEW);
204       glLoadIdentity();
205       gluLookAt( 0.0, 0.0, 30.0,
206                  0.0, 0.0, 0.0,
207                  0.0, 1.0, 0.0);
208       if (width < height)
209         glScalef (1/h, 1/h, 1);
210     }
211 
212   new_count = MI_COUNT(mi);
213 
214 # if 0
215   /* Lower the resolution to get a decent frame rate on iPhone 4s */
216   if (mi->xgwa.width <= 640 || mi->xgwa.height <= 640)
217     new_count /= 3;
218 # endif
219 
220   /* Lower it even further for iPhone 3 */
221   if (mi->xgwa.width <= 480 || mi->xgwa.height <= 480)
222     new_count /= 2;
223 
224   if (wire) new_count /= 2;
225 
226   if (new_count < 1) new_count = 1;
227 
228   if (bp->count != new_count || !bp->lines)
229     {
230       if (bp->lines)
231         {
232           for (i = 0; i < bp->count * bp->frames * 2; i++)
233             glDeleteLists (bp->lines[i], 1);
234           free (bp->lines);
235           free (bp->heights);
236         }
237 
238       bp->count = new_count;
239       bp->lines = (GLuint *)
240         malloc (bp->count * bp->frames * 2 * sizeof(*bp->lines));
241       for (i = 0; i < bp->count * bp->frames * 2; i++)
242         bp->lines[i] = glGenLists (1);
243       bp->heights = (GLfloat *) calloc (bp->count, sizeof(*bp->heights));
244     }
245 
246   {
247     GLfloat lw = 1;
248     GLfloat s = 1;
249 
250 # ifdef HAVE_MOBILE
251     lw = 4;
252     s = 1.4;
253 
254 # else  /* !HAVE_MOBILE */
255 
256     if (MI_WIDTH(mi) > 2560) lw = 4;  /* Retina displays */
257 #  ifdef HAVE_COCOA
258     else if (MI_WIDTH(mi) > 1280) lw = 3;  /* WTF */
259 #  endif
260     else if (MI_WIDTH(mi) > 1920) lw = 3;
261     else if (mi->xgwa.width > 640 && mi->xgwa.height > 640) lw = 2;
262 
263     /* Make the image fill the screen a little more fully */
264     if (mi->xgwa.width <= 640 || mi->xgwa.height <= 640)
265       s = 1.2;
266 
267 # endif /* !HAVE_MOBILE */
268 
269     s /= 1.9 / bp->aspect;
270 
271     glScalef (s, s, s);
272     glLineWidth (lw);
273   }
274 
275   glClear(GL_COLOR_BUFFER_BIT);
276 }
277 
278 static void
load_image(ModeInfo * mi)279 load_image (ModeInfo *mi)
280 {
281   unk_configuration *bp = &bps[MI_SCREEN(mi)];
282   XImage *image0;
283   int x, y;
284   double xs, ys;
285   unsigned long max = 0;
286 
287   if (!mask_arg || !*mask_arg || !strcasecmp(mask_arg, "(none)"))
288     return;
289 
290   image0 = file_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi), mask_arg);
291   if (!image0) return;
292 
293   bp->mask = XCreateImage (MI_DISPLAY(mi), MI_VISUAL(mi), 32, ZPixmap, 0, 0,
294                            bp->resolution,
295                            bp->count * image0->height / image0->width *
296                            (1.9 / bp->aspect) * 0.75,
297                            32, 0);
298   if (!bp->mask) abort();
299   bp->mask->data = (char *)
300     malloc (bp->mask->height * bp->mask->bytes_per_line);
301   if (!bp->mask->data) abort();
302 
303   xs = image0->width  / (double) bp->mask->width;
304   ys = image0->height / (double) bp->mask->height;
305 
306   /* Scale the image down to a 1-bit mask. */
307   for (y = 0; y < bp->mask->height; y++)
308     for (x = 0; x < bp->mask->width; x++)
309       {
310         int x2, y2, n = 0;
311         double total = 0;
312         unsigned long p;
313         for (y2 = y * ys; y2 < (y+1) * ys; y2++)
314           for (x2 = x * xs; x2 < (x+1) * xs; x2++)
315             {
316               unsigned long agbr = XGetPixel (image0, x2, y2);
317               unsigned long a    = (agbr >> 24) & 0xFF;
318               unsigned long gray = (a == 0
319                                     ? 0
320                                     : ((((agbr >> 16) & 0xFF) +
321                                         ((agbr >>  8) & 0xFF) +
322                                         ((agbr >>  0) & 0xFF))
323                                        / 3));
324 # if 0
325               if (gray < 96) gray /= 2;  /* a little more contrast */
326 # endif
327               total += gray / 255.0;
328               n++;
329             }
330         p = 255 * total / n;
331         if (p > max) max = p;
332         p = (0xFF << 24) | (p << 16) | (p << 8) | p;
333         XPutPixel (bp->mask, x, bp->mask->height-y-1, p);
334       }
335 
336   bp->mask_scale = 255.0 / max;
337   XDestroyImage (image0);
338 }
339 
340 
341 # ifdef DEBUG
342 static GLfloat poly1 = 0, poly2 = 0;
343 # endif
344 
345 ENTRYPOINT Bool
unk_handle_event(ModeInfo * mi,XEvent * event)346 unk_handle_event (ModeInfo *mi, XEvent *event)
347 {
348   unk_configuration *bp = &bps[MI_SCREEN(mi)];
349 
350   if (event->xany.type == ButtonPress &&
351       (event->xbutton.button == Button4 ||
352        event->xbutton.button == Button5 ||
353        event->xbutton.button == Button6 ||
354        event->xbutton.button == Button7))
355     {
356       int b = event->xbutton.button;
357       int speed = 1;
358       if (b == Button6 || b == Button7)
359         speed *= 3;
360       if (bp->orthop)
361         switch (b) {				/* swap axes */
362         case Button4: b = Button6; break;
363         case Button5: b = Button7; break;
364         case Button6: b = Button4; break;
365         case Button7: b = Button5; break;
366         }
367       gltrackball_mousewheel (bp->trackball, b, speed, !!event->xbutton.state);
368       return True;
369     }
370 
371 # ifdef DEBUG
372   else if (event->type == KeyPress)
373     {
374       KeySym keysym;
375       char c = 0;
376       GLfloat i = 0.1;
377       XLookupString (&event->xkey, &c, 1, &keysym, 0);
378       switch (keysym) {
379       case XK_Up:    poly1 += i; break;
380       case XK_Down:  poly1 -= i; break;
381       case XK_Left:  poly2 += i; break;
382       case XK_Right: poly2 -= i; break;
383       default: break;
384       }
385       fprintf (stderr,"%.2f %.2f\n", poly1, poly2);
386       return True;
387     }
388 # endif /* DEBUG */
389 
390   else if (gltrackball_event_handler (event, bp->trackball,
391                                       MI_WIDTH (mi), MI_HEIGHT (mi),
392                                       &bp->button_down_p))
393     return True;
394   else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
395     {
396       bp->orthop = !bp->orthop;
397       reshape_unk (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
398       return True;
399     }
400 
401   return False;
402 }
403 
404 
405 /* Like cos but constrained to [-pi, +pi] */
406 static GLfloat
cos1(GLfloat th)407 cos1 (GLfloat th)
408 {
409   if (th < -M_PI) th = -M_PI;
410   if (th >  M_PI) th =  M_PI;
411   return cos (th);
412 }
413 
414 
415 /* Returns an array of floats for one new row, range [0, 1.0] */
416 static double *
generate_signal(ModeInfo * mi)417 generate_signal (ModeInfo *mi)
418 {
419   unk_configuration *bp = &bps[MI_SCREEN(mi)];
420   double *points = (double *) calloc (sizeof(*points), bp->resolution);
421   double *p;
422   int i, j;
423   int nspikes = (6 + frand(15)) * bp->noise;
424   double step = 1.0 / bp->resolution;
425   double r, max;
426 
427   for (j = 0; j < nspikes; j++)
428     {
429       double off = (bp->mask
430                     ? frand (1.0) - 0.5    /* all the way to the edge */
431                     : frand (0.8) - 0.4);  /* leave a margin */
432       double amp = (0.1 + frand (0.9)) * nspikes;
433       double freq = (7 + frand (11)) * bp->noise;
434       for (i = 0, r = -0.5, p = points;
435            i < bp->resolution;
436            i++, r += step, p++)
437         *p += amp/2 + amp/2 * cos1 ((r + off) * M_PI * 2 * freq);
438     }
439 
440   /* Avoid clipping */
441   max = nspikes;
442   if (max <= 0) max = 1;
443   for (i = 0, p = points; i < bp->resolution; i++, p++)
444     if (max < *p) max = *p;
445 
446   /* Multiply by baseline clipping curve, add static. */
447   for (i = 0, r = -0.5, p = points; i < bp->resolution; i++, r += step, p++)
448     *p = ((*p / max)
449           * (0.5 +
450              0.5
451              * (bp->mask ? 1 : cos1 (r * r * M_PI * 14))
452              * (1 - frand(0.2))));
453 
454   return points;
455 }
456 
457 
458 static void
tick_unk(ModeInfo * mi)459 tick_unk (ModeInfo *mi)
460 {
461   unk_configuration *bp = &bps[MI_SCREEN(mi)];
462   int wire = MI_IS_WIREFRAME(mi);
463   double *points = generate_signal (mi);
464   int linesp, frame;
465 
466   /* Roll forward by one line (2 dlists per frame) */
467   GLuint *cur = (GLuint *) malloc (bp->frames * 2 * sizeof(*cur));
468   memcpy (cur, bp->lines, bp->frames * 2 * sizeof(*cur));
469   memmove (bp->lines, bp->lines + 2 * bp->frames,
470            sizeof(*bp->lines) * (bp->count-1) * bp->frames * 2);
471   memcpy (bp->lines + (bp->count-1) * bp->frames * 2, cur,
472           bp->frames * 2 * sizeof(*cur));
473 
474   memmove (bp->heights, bp->heights + 1,
475            sizeof(*bp->heights) * (bp->count-1));
476   bp->heights[bp->count-1] = 0;
477 
478   /* Regenerate the pair at the bottom. */
479 
480   for (frame = 0; frame < bp->frames; frame++)
481     {
482       mi->polygon_count = 0;
483       for (linesp = 0; linesp <= 1; linesp++)
484         {
485           int i;
486 
487           glNewList (cur[frame * 2 + !linesp], GL_COMPILE);
488           glBegin (linesp
489                    ? GL_LINE_STRIP
490                    : wire ? GL_LINES : GL_QUAD_STRIP);
491           for (i = 0; i < bp->resolution; i++)
492             {
493               GLfloat x = i / (GLfloat) bp->resolution;
494               GLfloat z = (points[i] + frand (0.05)) * bp->amplitude;
495 
496               if (bp->mask)
497                 {
498                   int h = bp->mask->height;  /* leave a 10% gutter */
499                   int y = bp->frame_count % (int) (h * 1.1);
500                   unsigned long p = (y < h ? XGetPixel (bp->mask, i, y) : 0);
501                   unsigned long gray = ((p >> 8) & 0xFF);
502                   z *= gray * bp->mask_scale / 255.0;
503                 }
504 
505               if (z < 0) z = 0;
506               if (z > bp->amplitude) z = bp->amplitude;
507               glVertex3f (x, 0, z);
508               if (! linesp)
509                 glVertex3f (x, 0, 0);
510               mi->polygon_count++;
511             }
512           glEnd ();
513           glEndList ();
514         }
515     }
516 
517   bp->frame_count++;
518   mi->polygon_count *= bp->count;
519   mi->polygon_count += 5;  /* base */
520 
521   free (cur);
522   free (points);
523 }
524 
525 
526 ENTRYPOINT void
init_unk(ModeInfo * mi)527 init_unk (ModeInfo *mi)
528 {
529   unk_configuration *bp;
530   int wire = MI_IS_WIREFRAME(mi);
531   int i;
532 
533   MI_INIT (mi, bps);
534 
535   bp = &bps[MI_SCREEN(mi)];
536 
537   bp->glx_context = init_GL(mi);
538 
539   bp->orthop = ortho_arg;
540   bp->resolution = resolution_arg;
541   bp->amplitude = amplitude_arg;
542   bp->noise = noise_arg;
543   bp->aspect = aspect_arg;
544   bp->speed = speed_arg;
545   bp->frames = buzz_arg ? 15 : 1;
546   if (bp->resolution < 1) bp->resolution = 1;
547   if (bp->resolution > 300) bp->resolution = 300;
548   if (bp->amplitude < 0.01) bp->amplitude = 0.01;
549   if (bp->amplitude > 1) bp->amplitude = 1;
550   if (bp->noise < 0.01) bp->noise = 0.01;
551   if (bp->speed <= 0.001) bp->speed = 0.001;
552 
553   parse_color (mi, "foreground", "Foreground", bp->fg);
554   parse_color (mi, "background", "Background", bp->bg);
555 
556   reshape_unk (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
557 
558   bp->trackball = gltrackball_init (False);
559 
560   if (MI_COUNT(mi) < 1) MI_COUNT(mi) = 1;
561   /* bp->count is set in reshape */
562 
563   load_image (mi);
564 
565   bp->base = glGenLists (1);
566   glNewList (bp->base, GL_COMPILE);
567   {
568     GLfloat h1 = 0.01;
569     GLfloat h2 = 0.02;
570     GLfloat h3 = (h1 + h2) / 2;
571     GLfloat s = 0.505;
572     glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
573     glVertex3f (-s, -s, -h1);
574     glVertex3f ( s, -s, -h1);
575     glVertex3f ( s,  s, -h1);
576     glVertex3f (-s,  s, -h1);
577     glEnd();
578     glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
579     glVertex3f (-s, -s, 0);
580     glVertex3f (-s, -s, -h2);
581     glVertex3f ( s, -s, -h2);
582     glVertex3f ( s, -s, 0);
583     glEnd();
584     glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
585     glVertex3f (-s, -s, 0);
586     glVertex3f (-s, -s, -h2);
587     glVertex3f (-s,  s, -h2);
588     glVertex3f (-s,  s, 0);
589     glEnd();
590     glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
591     glVertex3f ( s, -s, 0);
592     glVertex3f ( s, -s, -h2);
593     glVertex3f ( s,  s, -h2);
594     glVertex3f ( s,  s, 0);
595     glEnd();
596     glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
597     glVertex3f (-s,  s, 0);
598     glVertex3f (-s,  s, -h2);
599     glVertex3f ( s,  s, -h2);
600     glVertex3f ( s,  s, 0);
601     glEnd();
602 
603     glColor3fv (bp->fg);
604     glBegin (GL_LINE_LOOP);
605     s -= 0.01;
606     glVertex3f (-s, -s, -h3);
607     glVertex3f ( s, -s, -h3);
608     glVertex3f ( s,  s, -h3);
609     glVertex3f (-s,  s, -h3);
610     glEnd();
611   }
612   glEndList ();
613 
614   if (! wire)
615     {
616       glEnable (GL_LINE_SMOOTH);
617       glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
618       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
619       glEnable (GL_BLEND);
620     }
621 
622   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
623 
624   for (i = 0; i < bp->count; i++)
625     tick_unk (mi);
626 }
627 
628 
629 static double
ease_fn(double r)630 ease_fn (double r)
631 {
632   return cos ((r/2 + 1) * M_PI) + 1; /* Smooth curve up, end at slope 1. */
633 }
634 
635 
636 static double
ease_ratio(double r)637 ease_ratio (double r)
638 {
639   double ease = 0.5;
640   if      (r <= 0)     return 0;
641   else if (r >= 1)     return 1;
642   else if (r <= ease)  return     ease * ease_fn (r / ease);
643   else if (r > 1-ease) return 1 - ease * ease_fn ((1 - r) / ease);
644   else                 return r;
645 }
646 
647 
648 ENTRYPOINT void
draw_unk(ModeInfo * mi)649 draw_unk (ModeInfo *mi)
650 {
651   unk_configuration *bp = &bps[MI_SCREEN(mi)];
652   int wire = MI_IS_WIREFRAME(mi);
653   Display *dpy = MI_DISPLAY(mi);
654   Window window = MI_WINDOW(mi);
655   GLfloat step = 1.0 / bp->count;
656   double speed = (0.6 / bp->speed) * (80.0 / bp->count);
657   double now = double_time();
658   double ratio = (now - bp->tick) / speed;
659   int frame;
660   int i;
661 
662   ratio = (ratio < 0 ? 0 : ratio > 1 ? 1 : ratio);
663 
664   if (!bp->glx_context)
665     return;
666 
667   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
668 
669   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
670 
671   glShadeModel (GL_FLAT);
672   glEnable (GL_DEPTH_TEST);
673   glDisable (GL_CULL_FACE);
674 
675   glPushMatrix ();
676 
677   glRotatef(current_device_rotation(), 0, 0, 1);
678 
679   gltrackball_rotate (bp->trackball);
680 
681   glRotatef (-45, 1, 0, 0);
682 
683   i = bp->orthop ? 15 : 17;
684   glScalef (i / bp->aspect, i, i / bp->aspect);
685 
686   glDisable (GL_POLYGON_OFFSET_FILL);
687   if (wire)
688     glColor3f (0.5 * bp->fg[0], 0.5 * bp->fg[1], 0.5 * bp->fg[2]);
689   else
690     glColor4fv (bp->bg);
691   glCallList (bp->base);
692 
693   /* So the masking quads don't interfere with the lines.
694      These numbers are empirical black magic. */
695   glEnable (GL_POLYGON_OFFSET_FILL);
696 
697 # ifdef DEBUG
698   glPolygonOffset (poly1, poly2);
699 # else
700   glPolygonOffset (0.5, 0.5);
701 # endif
702 
703   glTranslatef (-0.5, 0.55 + step*ratio, 0);
704 
705   frame = random() % bp->frames;
706   for (i = 0; i < bp->count; i++)
707     {
708       int j = i * bp->frames * 2 + frame * 2;
709       GLfloat s  = ease_ratio (bp->heights[i]);
710       GLfloat s2 = ease_ratio (bp->heights[i] * 1.5);
711 
712       glPushMatrix();
713       glScalef (1, 1, s);
714       glColor4f (bp->fg[0], bp->fg[1], bp->fg[2], s2);
715       glCallList (bp->lines[j]);	/* curve */
716       s = 1;
717       if (wire)
718         glColor4f (0.5 * bp->fg[0], 0.5 * bp->fg[1], 0.5 * bp->fg[2], s);
719       else
720         glColor4f (bp->bg[0], bp->bg[1], bp->bg[2], s);
721       glCallList (bp->lines[j+1]);	/* shield */
722       glPopMatrix();
723       glTranslatef (0, -step, 0);
724     }
725 
726   glPopMatrix ();
727 
728   if (mi->fps_p) do_fps (mi);
729   glFinish();
730 
731   if (!bp->button_down_p)
732     {
733       /* Set height/fade based on distance from either edge. */
734       GLfloat dist = bp->count * 0.05;
735       int i;
736       for (i = 0; i < bp->count; i++)
737         {
738           GLfloat i2 = i - ratio;
739           GLfloat h = ((i2 < bp->count/2 ? i2 : (bp->count - 1 - i2))
740                        / dist);
741           bp->heights[i] = (h > 1 ? 1 : h < 0 ? 0 : h);
742         }
743 
744       if (bp->tick + speed <= now)     /* Add a new row. */
745         {
746           tick_unk (mi);
747           bp->tick = now;
748         }
749     }
750 
751   glXSwapBuffers(dpy, window);
752 }
753 
754 
755 ENTRYPOINT void
free_unk(ModeInfo * mi)756 free_unk (ModeInfo *mi)
757 {
758   unk_configuration *bp = &bps[MI_SCREEN(mi)];
759   int i;
760   if (!bp->glx_context) return;
761   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
762   if (bp->trackball) gltrackball_free (bp->trackball);
763   for (i = 0; i < bp->count * bp->frames * 2; i++)
764     glDeleteLists (bp->lines[i], 1);
765   free (bp->lines);
766   free (bp->heights);
767   if (bp->mask) XDestroyImage (bp->mask);
768 }
769 
770 XSCREENSAVER_MODULE_2 ("UnknownPleasures", unknownpleasures, unk)
771 
772 #endif /* USE_GL */
773