1 /* razzledazzle, Copyright (c) 2018 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
12 #define DEFAULTS "*delay: 30000 \n" \
13 "*showFPS: False \n" \
14 "*wireframe: False \n" \
16 "*suppressRotationAnimation: True\n" \
18 # define release_dazzle 0
20 #define countof(x) (sizeof((x))/sizeof((*x)))
22 #include "xlockmore.h"
28 #ifdef USE_GL /* whole file */
30 #define DEF_SPEED "1.0"
31 #define DEF_DENSITY "5.0"
32 #define DEF_THICKNESS "0.1"
33 #define DEF_MODE "Random"
35 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
37 #define RANDSIGN() ((random() & 1) ? 1 : -1)
39 extern const struct gllist
40 *ships_ship1, *ships_ship2, *ships_ship3, *ships_ship4,
41 *ships_ship5, *ships_ship6, *ships_ship7, *ships_ship8;
43 static const struct gllist * const *all_ships[] = {
44 &ships_ship1, &ships_ship2, &ships_ship3, &ships_ship4,
45 &ships_ship5, &ships_ship6, &ships_ship7, &ships_ship8,
49 typedef enum { LEFT, RIGHT, UP, DOWN } direction;
51 typedef struct node node;
60 GLfloat color1[4], color2[4];
64 GLXContext *glx_context;
66 GLfloat xoff, yoff, dx, dy;
73 enum { SHIPS, FLAT, RANDOM } mode;
76 } dazzle_configuration;
78 static dazzle_configuration *bps = NULL;
80 static GLfloat speed, thickness, density;
81 static char *mode_arg;
83 static XrmOptionDescRec opts[] = {
84 { "-speed", ".speed", XrmoptionSepArg, 0 },
85 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
86 { "-density", ".density", XrmoptionSepArg, 0 },
87 { "-mode", ".mode", XrmoptionSepArg, 0 },
88 { "-ships", ".mode", XrmoptionNoArg, "ships" },
89 { "-flat", ".mode", XrmoptionNoArg, "flat" },
92 static argtype vars[] = {
93 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
94 {&thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float},
95 {&density, "density", "Density", DEF_DENSITY, t_Float},
96 {&mode_arg, "mode", "Mode", DEF_MODE, t_String},
99 ENTRYPOINT ModeSpecOpt dazzle_opts = {countof(opts), opts, countof(vars), vars, NULL};
104 draw_grid (ModeInfo *mi, int gx, int gy)
106 dazzle_configuration *bp = &bps[MI_SCREEN(mi)];
107 Bool wire = MI_IS_WIREFRAME(mi);
109 long wh = density * 2;
117 for (y = 0; y < wh; y++)
118 for (x = 0; x < wh; x++)
120 node *n0 = &bp->nodes[(y % wh) * wh + (x % wh)];
121 node *n1 = &bp->nodes[(y % wh) * wh + ((x+1) % wh)];
122 node *n2 = &bp->nodes[((y+1) % wh) * wh + ((x+1) % wh)];
123 node *n3 = &bp->nodes[((y+1) % wh) * wh + (x % wh)];
125 Bool horiz_p, visible;
126 GLfloat x0, y0, x1, y1, x2, y2, x3, y3;
127 GLfloat xoff = (x < wh-1 ? 0 : wh);
128 GLfloat yoff = (y < wh-1 ? 0 : wh);
129 GLfloat bx = fmod ((double) bp->xoff, 2.0);
130 GLfloat by = fmod ((double) bp->yoff, 2.0);
139 glBegin (GL_LINE_LOOP);
140 for (a = 0; a < 360; a += 10)
141 glVertex3f ((n0->x / density-1) + 0.05 * cos(a * M_PI/180) + bx,
142 (n0->y / density-1) + 0.05 * sin(a * M_PI/180) + by,
147 x0 = n0->x / density - 1 + bx;
148 y0 = n0->y / density - 1 + by;
150 x1 = (n1->x+xoff) / density - 1 + bx;
151 y1 = n1->y / density - 1 + by;
152 x2 = (n2->x+xoff) / density - 1 + bx;
153 y2 = (n2->y+yoff) / density - 1 + by;
154 x3 = n3->x / density - 1 + bx;
155 y3 = (n3->y+yoff) / density - 1 + by;
159 if (gx == 0 && gy == 0)
165 glColor3f(0.5, 0, 0.5);
166 if (wire) glBegin (GL_LINE_LOOP);
167 glVertex3f (x0, y0, 0);
168 glVertex3f (x1, y1, 0);
169 glVertex3f (x2, y2, 0);
170 glVertex3f (x3, y3, 0);
176 /* This isn't quite right: just because all corners are off screen
177 doesn't mean the quad isn't visible. We need to intersect the
178 edges with the screen rectangle.
182 visible = ((x0 >= -max && y0 >= -max && x0 <= max && y0 <= max) ||
183 (x1 >= -max && y1 >= -max && x1 <= max && y1 <= max) ||
184 (x2 >= -max && y2 >= -max && x2 <= max && y2 <= max) ||
185 (x3 >= -max && y3 >= -max && x3 <= max && y3 <= max));
188 if (!visible) continue;
193 nstripes = n0->nstripes;
194 horiz_p = n0->horiz_p;
196 for (i = 0; i < nstripes; i++)
198 GLfloat ss = (GLfloat) i / nstripes;
199 GLfloat ss1 = (GLfloat) (i+1) / nstripes;
201 glColor4fv (n0->color1);
205 glColor4fv (n0->color2);
209 x0 = n0->x + (n3->x - n0->x) * ss;
210 y0 = n0->y + ((n3->y+yoff) - n0->y) * ss;
211 x1 = (n1->x+xoff) + ((n2->x+xoff) - (n1->x+xoff)) * ss;
212 y1 = n1->y + ((n2->y+yoff) - n1->y) * ss;
214 x2 = (n1->x+xoff) + ((n2->x+xoff) - (n1->x+xoff)) * ss1;
215 y2 = n1->y + ((n2->y+yoff) - n1->y) * ss1;
216 x3 = n0->x + (n3->x - n0->x) * ss1;
217 y3 = n0->y + ((n3->y+yoff) - n0->y) * ss1;
221 x0 = n0->x + ((n1->x+xoff) - n0->x) * ss;
222 y0 = n0->y + (n1->y - n0->y) * ss;
223 x1 = n3->x + ((n2->x+xoff) - n3->x) * ss;
224 y1 = (n3->y+yoff) + ((n2->y+yoff) - (n3->y+yoff)) * ss;
226 x2 = n3->x + ((n2->x+xoff) - n3->x) * ss1;
227 y2 = (n3->y+yoff) + ((n2->y+yoff) - (n3->y+yoff)) * ss1;
228 x3 = n0->x + ((n1->x+xoff) - n0->x) * ss1;
229 y3 = n0->y + (n1->y - n0->y) * ss1;
232 if (wire) glBegin (GL_LINES);
233 glVertex3f (x0 / density - 1 + bx, y0 / density - 1 + by, 0);
234 glVertex3f (x1 / density - 1 + bx, y1 / density - 1 + by, 0);
235 glVertex3f (x2 / density - 1 + bx, y2 / density - 1 + by, 0);
236 glVertex3f (x3 / density - 1 + bx, y3 / density - 1 + by, 0);
248 move_grid (ModeInfo *mi)
250 dazzle_configuration *bp = &bps[MI_SCREEN(mi)];
252 long wh = density * 2;
253 Bool wire = MI_IS_WIREFRAME(mi);
254 GLfloat max = 1.0 / density * 3;
256 if (bp->button_down_p)
262 if (! (random() % 50))
264 bp->dx += frand(0.0002) * RANDSIGN() * speed;
265 bp->dy += frand(0.0002) * RANDSIGN() * speed;
268 if (bp->dx > 0.003 * speed) bp->dx = 0.003 * speed;
269 if (bp->dy > 0.003 * speed) bp->dy = 0.003 * speed;
271 for (y = 0; y < wh; y++)
272 for (x = 0; x < wh; x++)
274 node *n = &bp->nodes[y * wh + x];
275 GLfloat x2 = n->x + n->dx;
276 GLfloat y2 = n->y + n->dy;
278 if (x2 < n->gx + max && x2 >= n->gx - max &&
279 y2 < n->gy + max && y2 >= n->gy - max)
285 if (! (random() % 50))
287 n->dx += frand(0.0005) * RANDSIGN() * speed;
288 n->dy += frand(0.0005) * RANDSIGN() * speed;
291 /* If this quad was not drawn, it's ok to re-randomize stripes, */
294 int i = random() % bp->ncolors;
295 int j = (i + bp->ncolors / 2) % bp->ncolors;
296 GLfloat cscale = 0.3;
298 n->color1[0] = bp->colors[i].red / 65536.0;
299 n->color1[1] = bp->colors[i].green / 65536.0;
300 n->color1[2] = bp->colors[i].blue / 65536.0;
303 n->color2[0] = bp->colors[j].red / 65536.0;
304 n->color2[1] = bp->colors[j].green / 65536.0;
305 n->color2[2] = bp->colors[j].blue / 65536.0;
310 n->color1[0] = cscale * n->color1[0] + 1 - cscale;
311 n->color1[1] = cscale * n->color1[1] + 1 - cscale;
312 n->color1[2] = cscale * n->color1[2] + 1 - cscale;
313 n->color2[0] = cscale * n->color2[0];
314 n->color2[1] = cscale * n->color2[1];
315 n->color2[2] = cscale * n->color2[2];
318 n->horiz_p = random() & 1;
319 n->nstripes = 2 + (int) (BELLRAND(1.0 / thickness));
326 /* Window management, etc
329 reshape_dazzle (ModeInfo *mi, int width, int height)
331 glViewport (0, 0, (GLint) width, (GLint) height);
333 glMatrixMode(GL_PROJECTION);
335 glOrtho (0, 1, 1, 0, -1, 1);
337 if (width > height * 5) { /* tiny window: show middle */
338 GLfloat s = (GLfloat)height/width;
339 glOrtho (0, 1, 0.5-s, 0.5+s, -1, 1);
342 # ifdef USE_IPHONE /* So much WTF */
344 int rot = current_device_rotation();
346 glTranslatef (0.5, 0.5, 0);
348 if (rot == 180 || rot == -180) {
349 glTranslatef (1, 1, 0);
350 } else if (rot == 90 || rot == -270) {
351 glRotatef (180, 0, 0, 1);
352 glTranslatef (0, 1, 0);
353 } else if (rot == -90 || rot == 270) {
354 glRotatef (180, 0, 0, 1);
355 glTranslatef (1, 0, 0);
358 glTranslatef(-0.5, -0.5, 0);
362 glMatrixMode(GL_MODELVIEW);
365 glClear(GL_COLOR_BUFFER_BIT);
370 dazzle_randomize (ModeInfo *mi)
372 dazzle_configuration *bp = &bps[MI_SCREEN(mi)];
374 long wh = density * 2;
376 bp->ncolors = MI_NCOLORS(mi) - 1;
377 if (bp->ncolors < 1) bp->ncolors = 1;
378 if (bp->colors) free (bp->colors);
379 bp->colors = (XColor *) calloc (bp->ncolors, sizeof(XColor));
381 make_random_colormap (0, 0, 0, bp->colors, &bp->ncolors,
382 True, False, 0, False);
384 make_smooth_colormap (0, 0, 0,
385 bp->colors, &bp->ncolors,
386 False, False, False);
387 if (bp->ncolors < 1) abort();
390 if (bp->nodes) free (bp->nodes);
392 bp->nodes = (node *) calloc (wh * wh, sizeof (node));
393 for (y = 0; y < wh; y++)
394 for (x = 0; x < wh; x++)
396 node *n = &bp->nodes[wh * y + x];
402 bp->xoff = bp->yoff = 0;
403 for (x = 0; x < 1000; x++)
406 bp->dx = frand(0.0005) * RANDSIGN() * speed;
407 bp->dy = frand(0.0005) * RANDSIGN() * speed;
409 if (bp->mode == SHIPS || bp->mode == RANDOM)
411 bp->which_ship = random() % countof(all_ships);
412 if (bp->mode == RANDOM && !(random() % 3))
416 if (bp->which_ship != -1)
425 dazzle_handle_event (ModeInfo *mi, XEvent *event)
427 dazzle_configuration *bp = &bps[MI_SCREEN(mi)];
428 Bool wire = MI_IS_WIREFRAME(mi);
429 GLfloat bx = fmod ((double) bp->xoff, 2.0);
430 GLfloat by = fmod ((double) bp->yoff, 2.0);
431 long wh = density * 2;
433 if (event->xany.type == ButtonPress)
435 GLfloat x = (GLfloat) event->xbutton.x / MI_WIDTH (mi) - 0.5;
436 GLfloat y = (GLfloat) event->xbutton.y / MI_HEIGHT (mi) - 0.5;
438 int xoff = 0, yoff = 0;
442 if (wire) x /= 0.2, y /= 0.2;
444 for (y0 = -1; y0 <= 1; y0++)
445 for (x0 = -1; x0 <= 1; x0++)
446 for (y1 = 0; y1 < wh; y1++)
447 for (x1 = 0; x1 < wh; x1++)
449 node *n0 = &bp->nodes[(y1 % wh) * wh + (x1 % wh)];
451 GLfloat x2 = n0->x / density - 1 + bx + x0*2;
452 GLfloat y2 = n0->y / density - 1 + by + y0*2;
454 dist2 = (x - x2) * (x - x2) + (y - y2) * (y - y2);
464 bp->button_down_p = True;
470 else if (event->xany.type == ButtonRelease)
473 bp->button_down_p = False;
476 else if (event->xany.type == MotionNotify && bp->dragging)
478 GLfloat x = (GLfloat) event->xmotion.x / MI_WIDTH (mi) - 0.5;
479 GLfloat y = (GLfloat) event->xmotion.y / MI_HEIGHT (mi) - 0.5;
480 if (wire) x /= 0.2, y /= 0.2;
485 bp->dragging->x = x * density + density;
486 bp->dragging->y = y * density + density;
487 bp->dragging->dx = bp->dragging->dy = 0;
490 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
492 dazzle_randomize (mi);
501 init_dazzle (ModeInfo *mi)
503 dazzle_configuration *bp;
507 bp = &bps[MI_SCREEN(mi)];
509 bp->glx_context = init_GL(mi);
511 if (!mode_arg || !*mode_arg || !strcasecmp(mode_arg, "random"))
513 else if (!strcasecmp(mode_arg, "ship") || !strcasecmp(mode_arg, "ships"))
515 else if (!strcasecmp(mode_arg, "flat"))
519 fprintf (stderr, "%s: mode must be ship, flat or random, not %s\n",
525 if (bp->mode == SHIPS || bp->mode == RANDOM)
528 bp->dlists = (GLuint *) calloc (countof(all_ships)+1, sizeof(GLuint));
529 for (i = 0; i < countof(all_ships); i++)
531 const struct gllist *gll = *all_ships[i];
533 bp->dlists[i] = glGenLists (1);
534 glNewList (bp->dlists[i], GL_COMPILE);
536 glMatrixMode(GL_MODELVIEW);
538 glMatrixMode(GL_TEXTURE);
540 glMatrixMode(GL_MODELVIEW);
545 glTranslatef (-1, 0, 0);
547 renderList (gll, MI_IS_WIREFRAME(mi));
549 glMatrixMode(GL_TEXTURE);
551 glMatrixMode(GL_MODELVIEW);
557 dazzle_randomize (mi);
558 reshape_dazzle (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
563 draw_dazzle (ModeInfo *mi)
565 dazzle_configuration *bp = &bps[MI_SCREEN(mi)];
566 Bool wire = MI_IS_WIREFRAME(mi);
567 Display *dpy = MI_DISPLAY(mi);
568 Window window = MI_WINDOW(mi);
571 if (!bp->glx_context)
574 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
576 glShadeModel(GL_SMOOTH);
577 glDisable(GL_DEPTH_TEST);
578 glEnable(GL_NORMALIZE);
579 glDisable(GL_CULL_FACE);
581 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
584 mi->polygon_count = 0;
586 glTranslatef (0.5, 0.5, 0);
589 glScalef (0.2, 0.2, 1);
593 for (y = -1; y <= 1; y++)
594 for (x = -1; x <= 1; x++)
595 draw_grid (mi, x, y);
597 if (bp->which_ship != -1)
600 int rot = current_device_rotation();
609 /* Draw into the depth buffer but not the frame buffer */
610 glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
611 glClear (GL_DEPTH_BUFFER_BIT);
612 glEnable (GL_DEPTH_TEST);
616 glRotatef (90, 0, 0, 1);
617 if (rot == 90 || rot == -270)
618 glRotatef (180, 0, 0, 1);
622 glRotatef (90, 1, 0, 0);
623 glScalef (0.9, 0.9, 0.9);
624 glTranslatef (-0.5, 0, -0.2);
627 if (rot == 0 || rot == 180 || rot == -180)
628 glScalef (1, 1, (GLfloat) MI_HEIGHT(mi) / MI_WIDTH(mi));
631 glScalef (1, 1, (GLfloat) MI_WIDTH(mi) / MI_HEIGHT(mi));
633 /* Wave boat horizontally and vertically */
634 glTranslatef (cos ((double) bp->frames / 80 * M_PI * speed) / 200,
636 cos ((double) bp->frames / 60 * M_PI * speed) / 300);
638 glCallList (bp->dlists[bp->which_ship]);
639 mi->polygon_count += (*all_ships[bp->which_ship])->points / 3;
642 /* Wave horizon vertically */
644 cos ((double) bp->frames / 120 * M_PI * speed) / 200,
649 glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
651 /* Black out everything that isn't a ship. */
654 glVertex3f (-1, -1, 0);
655 glVertex3f (-1, 1, 0);
656 glVertex3f ( 1, 1, 0);
657 glVertex3f ( 1, -1, 0);
661 GLfloat horizon = 0.15;
663 glColor3f (0.7, 0.7, 1.0);
665 glVertex3f (-1, -1, 0);
666 glVertex3f (-1, horizon, 0);
667 glVertex3f ( 1, horizon, 0);
668 glVertex3f ( 1, -1, 0);
671 glColor3f (0.0, 0.05, 0.2);
673 glVertex3f (-1, horizon, 0);
674 glVertex3f (-1, 1, 0);
675 glVertex3f ( 1, 1, 0);
676 glVertex3f ( 1, horizon, 0);
681 glDisable (GL_DEPTH_TEST);
689 glBegin(GL_LINE_LOOP);
690 glVertex3f(-0.5, -0.5, 0);
691 glVertex3f(-0.5, 0.5, 0);
692 glVertex3f( 0.5, 0.5, 0);
693 glVertex3f( 0.5, -0.5, 0);
701 if (mi->fps_p) do_fps (mi);
704 glXSwapBuffers(dpy, window);
709 free_dazzle (ModeInfo *mi)
711 dazzle_configuration *bp = &bps[MI_SCREEN(mi)];
712 if (bp->nodes) free (bp->nodes);
713 if (bp->colors) free (bp->colors);
717 for (i = 0; i < countof(all_ships); i++)
718 glDeleteLists (bp->dlists[i], 1);
723 XSCREENSAVER_MODULE_2 ("RazzleDazzle", razzledazzle, dazzle)