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 {&litude_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