/* razzledazzle, Copyright (c) 2018 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #define DEFAULTS "*delay: 30000 \n" \ "*showFPS: False \n" \ "*wireframe: False \n" \ "*ncolors: 2 \n" \ "*suppressRotationAnimation: True\n" \ # define release_dazzle 0 #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) #include "xlockmore.h" #include "colors.h" #include "normals.h" #include "gllist.h" #include #ifdef USE_GL /* whole file */ #define DEF_SPEED "1.0" #define DEF_DENSITY "5.0" #define DEF_THICKNESS "0.1" #define DEF_MODE "Random" #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3) #undef RANDSIGN #define RANDSIGN() ((random() & 1) ? 1 : -1) extern const struct gllist *ships_ship1, *ships_ship2, *ships_ship3, *ships_ship4, *ships_ship5, *ships_ship6, *ships_ship7, *ships_ship8; static const struct gllist * const *all_ships[] = { &ships_ship1, &ships_ship2, &ships_ship3, &ships_ship4, &ships_ship5, &ships_ship6, &ships_ship7, &ships_ship8, }; typedef enum { LEFT, RIGHT, UP, DOWN } direction; typedef struct node node; struct node { long gx, gy; GLfloat x, y; GLfloat dx, dy; int nstripes; Bool horiz_p; Bool drawn_p; GLfloat color1[4], color2[4]; }; typedef struct { GLXContext *glx_context; Bool button_down_p; GLfloat xoff, yoff, dx, dy; node *nodes; node *dragging; int drag_x, drag_y; int ncolors; XColor *colors; GLuint *dlists; enum { SHIPS, FLAT, RANDOM } mode; int which_ship; long frames; } dazzle_configuration; static dazzle_configuration *bps = NULL; static GLfloat speed, thickness, density; static char *mode_arg; static XrmOptionDescRec opts[] = { { "-speed", ".speed", XrmoptionSepArg, 0 }, { "-thickness", ".thickness", XrmoptionSepArg, 0 }, { "-density", ".density", XrmoptionSepArg, 0 }, { "-mode", ".mode", XrmoptionSepArg, 0 }, { "-ships", ".mode", XrmoptionNoArg, "ships" }, { "-flat", ".mode", XrmoptionNoArg, "flat" }, }; static argtype vars[] = { {&speed, "speed", "Speed", DEF_SPEED, t_Float}, {&thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float}, {&density, "density", "Density", DEF_DENSITY, t_Float}, {&mode_arg, "mode", "Mode", DEF_MODE, t_String}, }; ENTRYPOINT ModeSpecOpt dazzle_opts = {countof(opts), opts, countof(vars), vars, NULL}; static void draw_grid (ModeInfo *mi, int gx, int gy) { dazzle_configuration *bp = &bps[MI_SCREEN(mi)]; Bool wire = MI_IS_WIREFRAME(mi); long x, y; long wh = density * 2; if (wire) glColor3f (1, 1, 1); if (!wire) glBegin (GL_QUADS); for (y = 0; y < wh; y++) for (x = 0; x < wh; x++) { node *n0 = &bp->nodes[(y % wh) * wh + (x % wh)]; node *n1 = &bp->nodes[(y % wh) * wh + ((x+1) % wh)]; node *n2 = &bp->nodes[((y+1) % wh) * wh + ((x+1) % wh)]; node *n3 = &bp->nodes[((y+1) % wh) * wh + (x % wh)]; int nstripes, i; Bool horiz_p, visible; GLfloat x0, y0, x1, y1, x2, y2, x3, y3; GLfloat xoff = (x < wh-1 ? 0 : wh); GLfloat yoff = (y < wh-1 ? 0 : wh); GLfloat bx = fmod ((double) bp->xoff, 2.0); GLfloat by = fmod ((double) bp->yoff, 2.0); bx += gx*2; by += gy*2; if (wire) { GLfloat a; glColor3f (0, 0, 1); glBegin (GL_LINE_LOOP); for (a = 0; a < 360; a += 10) glVertex3f ((n0->x / density-1) + 0.05 * cos(a * M_PI/180) + bx, (n0->y / density-1) + 0.05 * sin(a * M_PI/180) + by, 0); glEnd(); } x0 = n0->x / density - 1 + bx; y0 = n0->y / density - 1 + by; x1 = (n1->x+xoff) / density - 1 + bx; y1 = n1->y / density - 1 + by; x2 = (n2->x+xoff) / density - 1 + bx; y2 = (n2->y+yoff) / density - 1 + by; x3 = n3->x / density - 1 + bx; y3 = (n3->y+yoff) / density - 1 + by; if (wire) { if (gx == 0 && gy == 0) { glLineWidth (4); glColor3f(1, 0, 0); } else glColor3f(0.5, 0, 0.5); if (wire) glBegin (GL_LINE_LOOP); glVertex3f (x0, y0, 0); glVertex3f (x1, y1, 0); glVertex3f (x2, y2, 0); glVertex3f (x3, y3, 0); mi->polygon_count++; if (wire) glEnd(); glLineWidth (1); } /* This isn't quite right: just because all corners are off screen doesn't mean the quad isn't visible. We need to intersect the edges with the screen rectangle. */ { GLfloat max = 0.75; visible = ((x0 >= -max && y0 >= -max && x0 <= max && y0 <= max) || (x1 >= -max && y1 >= -max && x1 <= max && y1 <= max) || (x2 >= -max && y2 >= -max && x2 <= max && y2 <= max) || (x3 >= -max && y3 >= -max && x3 <= max && y3 <= max)); } if (!visible) continue; if (visible) n0->drawn_p = True; nstripes = n0->nstripes; horiz_p = n0->horiz_p; for (i = 0; i < nstripes; i++) { GLfloat ss = (GLfloat) i / nstripes; GLfloat ss1 = (GLfloat) (i+1) / nstripes; if (i & 1) glColor4fv (n0->color1); else if (wire) continue; else glColor4fv (n0->color2); if (horiz_p) { x0 = n0->x + (n3->x - n0->x) * ss; y0 = n0->y + ((n3->y+yoff) - n0->y) * ss; x1 = (n1->x+xoff) + ((n2->x+xoff) - (n1->x+xoff)) * ss; y1 = n1->y + ((n2->y+yoff) - n1->y) * ss; x2 = (n1->x+xoff) + ((n2->x+xoff) - (n1->x+xoff)) * ss1; y2 = n1->y + ((n2->y+yoff) - n1->y) * ss1; x3 = n0->x + (n3->x - n0->x) * ss1; y3 = n0->y + ((n3->y+yoff) - n0->y) * ss1; } else { x0 = n0->x + ((n1->x+xoff) - n0->x) * ss; y0 = n0->y + (n1->y - n0->y) * ss; x1 = n3->x + ((n2->x+xoff) - n3->x) * ss; y1 = (n3->y+yoff) + ((n2->y+yoff) - (n3->y+yoff)) * ss; x2 = n3->x + ((n2->x+xoff) - n3->x) * ss1; y2 = (n3->y+yoff) + ((n2->y+yoff) - (n3->y+yoff)) * ss1; x3 = n0->x + ((n1->x+xoff) - n0->x) * ss1; y3 = n0->y + (n1->y - n0->y) * ss1; } if (wire) glBegin (GL_LINES); glVertex3f (x0 / density - 1 + bx, y0 / density - 1 + by, 0); glVertex3f (x1 / density - 1 + bx, y1 / density - 1 + by, 0); glVertex3f (x2 / density - 1 + bx, y2 / density - 1 + by, 0); glVertex3f (x3 / density - 1 + bx, y3 / density - 1 + by, 0); mi->polygon_count++; if (wire) glEnd(); } } if (!wire) glEnd(); } static void move_grid (ModeInfo *mi) { dazzle_configuration *bp = &bps[MI_SCREEN(mi)]; long x, y; long wh = density * 2; Bool wire = MI_IS_WIREFRAME(mi); GLfloat max = 1.0 / density * 3; if (bp->button_down_p) return; bp->xoff += bp->dx; bp->yoff += bp->dy; if (! (random() % 50)) { bp->dx += frand(0.0002) * RANDSIGN() * speed; bp->dy += frand(0.0002) * RANDSIGN() * speed; } if (bp->dx > 0.003 * speed) bp->dx = 0.003 * speed; if (bp->dy > 0.003 * speed) bp->dy = 0.003 * speed; for (y = 0; y < wh; y++) for (x = 0; x < wh; x++) { node *n = &bp->nodes[y * wh + x]; GLfloat x2 = n->x + n->dx; GLfloat y2 = n->y + n->dy; if (x2 < n->gx + max && x2 >= n->gx - max && y2 < n->gy + max && y2 >= n->gy - max) { n->x = x2; n->y = y2; } if (! (random() % 50)) { n->dx += frand(0.0005) * RANDSIGN() * speed; n->dy += frand(0.0005) * RANDSIGN() * speed; } /* If this quad was not drawn, it's ok to re-randomize stripes, */ if (! n->drawn_p) { int i = random() % bp->ncolors; int j = (i + bp->ncolors / 2) % bp->ncolors; GLfloat cscale = 0.3; n->color1[0] = bp->colors[i].red / 65536.0; n->color1[1] = bp->colors[i].green / 65536.0; n->color1[2] = bp->colors[i].blue / 65536.0; n->color1[3] = 1.0; n->color2[0] = bp->colors[j].red / 65536.0; n->color2[1] = bp->colors[j].green / 65536.0; n->color2[2] = bp->colors[j].blue / 65536.0; n->color2[3] = 1.0; if (! wire) { n->color1[0] = cscale * n->color1[0] + 1 - cscale; n->color1[1] = cscale * n->color1[1] + 1 - cscale; n->color1[2] = cscale * n->color1[2] + 1 - cscale; n->color2[0] = cscale * n->color2[0]; n->color2[1] = cscale * n->color2[1]; n->color2[2] = cscale * n->color2[2]; } n->horiz_p = random() & 1; n->nstripes = 2 + (int) (BELLRAND(1.0 / thickness)); } n->drawn_p = False; } } /* Window management, etc */ ENTRYPOINT void reshape_dazzle (ModeInfo *mi, int width, int height) { glViewport (0, 0, (GLint) width, (GLint) height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho (0, 1, 1, 0, -1, 1); if (width > height * 5) { /* tiny window: show middle */ GLfloat s = (GLfloat)height/width; glOrtho (0, 1, 0.5-s, 0.5+s, -1, 1); } # ifdef USE_IPHONE /* So much WTF */ { int rot = current_device_rotation(); glTranslatef (0.5, 0.5, 0); if (rot == 180 || rot == -180) { glTranslatef (1, 1, 0); } else if (rot == 90 || rot == -270) { glRotatef (180, 0, 0, 1); glTranslatef (0, 1, 0); } else if (rot == -90 || rot == 270) { glRotatef (180, 0, 0, 1); glTranslatef (1, 0, 0); } glTranslatef(-0.5, -0.5, 0); } # endif glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClear(GL_COLOR_BUFFER_BIT); } static void dazzle_randomize (ModeInfo *mi) { dazzle_configuration *bp = &bps[MI_SCREEN(mi)]; long x, y; long wh = density * 2; bp->ncolors = MI_NCOLORS(mi) - 1; if (bp->ncolors < 1) bp->ncolors = 1; if (bp->colors) free (bp->colors); bp->colors = (XColor *) calloc (bp->ncolors, sizeof(XColor)); if (bp->ncolors < 3) make_random_colormap (0, 0, 0, bp->colors, &bp->ncolors, True, False, 0, False); else make_smooth_colormap (0, 0, 0, bp->colors, &bp->ncolors, False, False, False); if (bp->ncolors < 1) abort(); bp->dragging = 0; if (bp->nodes) free (bp->nodes); bp->nodes = (node *) calloc (wh * wh, sizeof (node)); for (y = 0; y < wh; y++) for (x = 0; x < wh; x++) { node *n = &bp->nodes[wh * y + x]; n->gx = n->x = x; n->gy = n->y = y; } bp->dx = bp->dy = 0; bp->xoff = bp->yoff = 0; for (x = 0; x < 1000; x++) move_grid (mi); bp->dx = frand(0.0005) * RANDSIGN() * speed; bp->dy = frand(0.0005) * RANDSIGN() * speed; if (bp->mode == SHIPS || bp->mode == RANDOM) { bp->which_ship = random() % countof(all_ships); if (bp->mode == RANDOM && !(random() % 3)) bp->which_ship = -1; } if (bp->which_ship != -1) { bp->dx /= 10; bp->dy /= 10; } } ENTRYPOINT Bool dazzle_handle_event (ModeInfo *mi, XEvent *event) { dazzle_configuration *bp = &bps[MI_SCREEN(mi)]; Bool wire = MI_IS_WIREFRAME(mi); GLfloat bx = fmod ((double) bp->xoff, 2.0); GLfloat by = fmod ((double) bp->yoff, 2.0); long wh = density * 2; if (event->xany.type == ButtonPress) { GLfloat x = (GLfloat) event->xbutton.x / MI_WIDTH (mi) - 0.5; GLfloat y = (GLfloat) event->xbutton.y / MI_HEIGHT (mi) - 0.5; node *nn = 0; int xoff = 0, yoff = 0; GLfloat d2 = 999999; long x0, y0, x1, y1; if (wire) x /= 0.2, y /= 0.2; for (y0 = -1; y0 <= 1; y0++) for (x0 = -1; x0 <= 1; x0++) for (y1 = 0; y1 < wh; y1++) for (x1 = 0; x1 < wh; x1++) { node *n0 = &bp->nodes[(y1 % wh) * wh + (x1 % wh)]; double dist2; GLfloat x2 = n0->x / density - 1 + bx + x0*2; GLfloat y2 = n0->y / density - 1 + by + y0*2; dist2 = (x - x2) * (x - x2) + (y - y2) * (y - y2); if (dist2 < d2) { d2 = dist2; nn = n0; xoff = x0; yoff = y0; } } bp->button_down_p = True; bp->dragging = nn; bp->drag_x = xoff; bp->drag_y = yoff; return True; } else if (event->xany.type == ButtonRelease) { bp->dragging = 0; bp->button_down_p = False; return True; } else if (event->xany.type == MotionNotify && bp->dragging) { GLfloat x = (GLfloat) event->xmotion.x / MI_WIDTH (mi) - 0.5; GLfloat y = (GLfloat) event->xmotion.y / MI_HEIGHT (mi) - 0.5; if (wire) x /= 0.2, y /= 0.2; x -= bx; y -= by; x -= bp->drag_x * 2; y -= bp->drag_y * 2; bp->dragging->x = x * density + density; bp->dragging->y = y * density + density; bp->dragging->dx = bp->dragging->dy = 0; return True; } else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event)) { dazzle_randomize (mi); return True; } return False; } ENTRYPOINT void init_dazzle (ModeInfo *mi) { dazzle_configuration *bp; MI_INIT (mi, bps); bp = &bps[MI_SCREEN(mi)]; bp->glx_context = init_GL(mi); if (!mode_arg || !*mode_arg || !strcasecmp(mode_arg, "random")) bp->mode = RANDOM; else if (!strcasecmp(mode_arg, "ship") || !strcasecmp(mode_arg, "ships")) bp->mode = SHIPS; else if (!strcasecmp(mode_arg, "flat")) bp->mode = FLAT; else { fprintf (stderr, "%s: mode must be ship, flat or random, not %s\n", progname, mode_arg); exit (1); } bp->which_ship = -1; if (bp->mode == SHIPS || bp->mode == RANDOM) { int i; bp->dlists = (GLuint *) calloc (countof(all_ships)+1, sizeof(GLuint)); for (i = 0; i < countof(all_ships); i++) { const struct gllist *gll = *all_ships[i]; bp->dlists[i] = glGenLists (1); glNewList (bp->dlists[i], GL_COMPILE); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMatrixMode(GL_TEXTURE); glPushMatrix(); glMatrixMode(GL_MODELVIEW); if (random() & 1) { glScalef (-1, 1, 1); glTranslatef (-1, 0, 0); } renderList (gll, MI_IS_WIREFRAME(mi)); glMatrixMode(GL_TEXTURE); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glEndList (); } } dazzle_randomize (mi); reshape_dazzle (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); } ENTRYPOINT void draw_dazzle (ModeInfo *mi) { dazzle_configuration *bp = &bps[MI_SCREEN(mi)]; Bool wire = MI_IS_WIREFRAME(mi); Display *dpy = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); int x, y; if (!bp->glx_context) return; glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context); glShadeModel(GL_SMOOTH); glDisable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); glDisable(GL_CULL_FACE); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix (); mi->polygon_count = 0; glTranslatef (0.5, 0.5, 0); if (wire) glScalef (0.2, 0.2, 1); move_grid (mi); for (y = -1; y <= 1; y++) for (x = -1; x <= 1; x++) draw_grid (mi, x, y); if (bp->which_ship != -1) { # ifdef USE_IPHONE int rot = current_device_rotation(); # endif if (wire) glColor3f (1, 0, 0); else { glColor3f (0, 0, 0); /* Draw into the depth buffer but not the frame buffer */ glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glClear (GL_DEPTH_BUFFER_BIT); glEnable (GL_DEPTH_TEST); } # ifdef USE_IPHONE glRotatef (90, 0, 0, 1); if (rot == 90 || rot == -270) glRotatef (180, 0, 0, 1); # endif glPushMatrix(); glRotatef (90, 1, 0, 0); glScalef (0.9, 0.9, 0.9); glTranslatef (-0.5, 0, -0.2); # ifdef USE_IPHONE if (rot == 0 || rot == 180 || rot == -180) glScalef (1, 1, (GLfloat) MI_HEIGHT(mi) / MI_WIDTH(mi)); else # endif glScalef (1, 1, (GLfloat) MI_WIDTH(mi) / MI_HEIGHT(mi)); /* Wave boat horizontally and vertically */ glTranslatef (cos ((double) bp->frames / 80 * M_PI * speed) / 200, 0, cos ((double) bp->frames / 60 * M_PI * speed) / 300); glCallList (bp->dlists[bp->which_ship]); mi->polygon_count += (*all_ships[bp->which_ship])->points / 3; glPopMatrix(); /* Wave horizon vertically */ glTranslatef (0, cos ((double) bp->frames / 120 * M_PI * speed) / 200, 0); if (! wire) { glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); /* Black out everything that isn't a ship. */ # if 0 glBegin (GL_QUADS); glVertex3f (-1, -1, 0); glVertex3f (-1, 1, 0); glVertex3f ( 1, 1, 0); glVertex3f ( 1, -1, 0); glEnd(); # else { GLfloat horizon = 0.15; glColor3f (0.7, 0.7, 1.0); glBegin (GL_QUADS); glVertex3f (-1, -1, 0); glVertex3f (-1, horizon, 0); glVertex3f ( 1, horizon, 0); glVertex3f ( 1, -1, 0); glEnd(); glColor3f (0.0, 0.05, 0.2); glBegin (GL_QUADS); glVertex3f (-1, horizon, 0); glVertex3f (-1, 1, 0); glVertex3f ( 1, 1, 0); glVertex3f ( 1, horizon, 0); glEnd(); } # endif glDisable (GL_DEPTH_TEST); } } if (wire) { glColor3f(0,1,1); glLineWidth(4); glBegin(GL_LINE_LOOP); glVertex3f(-0.5, -0.5, 0); glVertex3f(-0.5, 0.5, 0); glVertex3f( 0.5, 0.5, 0); glVertex3f( 0.5, -0.5, 0); glEnd(); glLineWidth(1); } glPopMatrix (); bp->frames++; if (mi->fps_p) do_fps (mi); glFinish(); glXSwapBuffers(dpy, window); } ENTRYPOINT void free_dazzle (ModeInfo *mi) { dazzle_configuration *bp = &bps[MI_SCREEN(mi)]; if (!bp->glx_context) return; glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context); if (bp->nodes) free (bp->nodes); if (bp->colors) free (bp->colors); if (bp->dlists) { int i; for (i = 0; i < countof(all_ships); i++) glDeleteLists (bp->dlists[i], 1); free (bp->dlists); } } XSCREENSAVER_MODULE_2 ("RazzleDazzle", razzledazzle, dazzle) #endif /* USE_GL */