http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.tar.gz
[xscreensaver] / hacks / glx / gleidescope.c
diff --git a/hacks/glx/gleidescope.c b/hacks/glx/gleidescope.c
new file mode 100644 (file)
index 0000000..7470d3f
--- /dev/null
@@ -0,0 +1,1088 @@
+/* -*- Mode: C; tab-width: 4 -*- */
+
+#if !defined( lint ) && !defined( SABER )
+static const char sccsid[] = "@(#)gleidescope.c        1.0 03/06/27 xlockmore";
+#endif
+
+/* enable -grab switch */
+/*#define      GRAB*/
+
+/*-
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind.  The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof.  In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ *
+ *     Revision History:
+ *
+ *     20030627        1.0             acd             First Release.
+ *                                                             Texture loading code from 'glplanet'
+ *                                                                     by Jamie Zawinski <jwz@jwz.org>
+ *     20030810        1.1             acd             Added size flag.
+ *                                                             Now grabs screen / video / picture
+ *                                                                     (uses code from 'glslideshow' by
+ *                                                                     Mike Oliphant, Ben Buxton, Jamie Zawinski).
+ *                                                             Added -duration.
+ *                                                             Added mouse code.
+ *                                                             Added fade code (also from glslideshow).
+ *     20031013        1.2             acd             Migrated to compile without warnings under
+ *                                                                     xscreensaver 4.13.
+ *     20031023        1.3             acd             Better code to limit twisting speeds.
+ *                                                             Tweaked initial rotation values.
+ *                                                             Move, Rotate, Zoom now chosen at random if
+ *                                                                     no preference is given.
+ *                                                             Made grid slightly bigger so you can't see
+ *                                                                     the edge when zooming and moving.
+ */
+
+#include <X11/Intrinsic.h>
+#include "colors.h"
+
+#include "xpm-ximage.h"
+
+/*
+**----------------------------------------------------------------------------
+** Defines
+**----------------------------------------------------------------------------
+*/
+
+#ifdef STANDALONE
+# define PROGCLASS                             "gleidescope"
+# define HACK_INIT                             init_gleidescope
+# define HACK_DRAW                             draw_gleidescope
+# define HACK_RESHAPE                  reshape_gleidescope
+# define HACK_HANDLE_EVENT             gleidescope_handle_event
+# define EVENT_MASK                            PointerMotionMask
+# define gleidescope_opts              xlockmore_opts
+# define DEFAULTS \
+               "*delay:                20000           \n"     \
+               "*showFPS:              False           \n"     \
+               "*move:                 False           \n"     \
+               "*rotate:               False           \n"     \
+               "*zoom:                 False           \n"     \
+               "*image:                DEFAULT         \n"     \
+               "*size:                 -1                      \n"     \
+               "*duration:             30                      \n" \
+
+# include "xlockmore.h"                                /* from the xscreensaver distribution */
+#else  /* !STANDALONE */
+# include "xlock.h"                                    /* from the xlockmore distribution */
+#endif /* !STANDALONE */
+
+#ifdef USE_GL
+
+#include <GL/glu.h>
+
+/* acd TODO should all these be in gleidestruct? */
+#ifdef GRAB
+static Bool            grab;                   /* grab images */
+#endif
+static Bool            move;                   /* moving camera */
+static Bool            nomove;                 /* no moving camera */
+static Bool            rotate;                 /* rotate in place */
+static Bool            norotate;               /* no rotate in place */
+static int             size = -1;              /* size */
+static Bool            zoom;                   /* zooming camera */
+static Bool            nozoom;                 /* no zooming camera */
+static char            *image;                 /* name of texture to load */
+static int             duration;               /* length of time to display grabbed image */
+
+#define        MAX_TANGLE_VEL  2.0
+
+static float   tangle = 0;                     /* texture angle */
+static float   tangle_vel = 0.0;       /* texture velocity */
+static float   tangle_acc = 0.0;       /* texture acceleration */
+
+#define        MAX_RANGLE_VEL  1.5
+
+static float   rangle = 0;                     /* rotate angle */
+static float   rangle_vel = 0.0;       /* rotate velocity */
+static float   rangle_acc = 0.0;       /* rotate acceleration */
+
+static XrmOptionDescRec opts[] =
+{
+#ifdef GRAB
+       {"-grab",               (char *) ".gleidescope.grab",           XrmoptionNoArg,         "true"},
+#endif
+       {"-move",               (char *) ".gleidescope.move",           XrmoptionNoArg,         "true"},
+       {"-no-move",    (char *) ".gleidescope.nomove",         XrmoptionNoArg,         "true"},
+       {"-rotate",             (char *) ".gleidescope.rotate",         XrmoptionNoArg,         "true"},
+       {"-no-rotate",  (char *) ".gleidescope.norotate",       XrmoptionNoArg,         "true"},
+       /*{"-size",             (char *) ".gleidescope.size",           XrmoptionNoArg,         "-1"},*/
+       {"-zoom",               (char *) ".gleidescope.zoom",           XrmoptionNoArg,         "true"},
+       {"-no-zoom",    (char *) ".gleidescope.nozoom",         XrmoptionNoArg,         "true"},
+       {"-image",              (char *) ".gleidescope.image",          XrmoptionSepArg,        "DEFAULT"},
+       {"-duration",   (char *) ".gleidescope.duration",       XrmoptionSepArg,        "30"},
+};
+
+
+static argtype vars[] = {
+#ifdef GRAB
+       {(caddr_t *) &grab,                     "grab",         "Grab",         "False",        t_Bool},
+#endif
+       {(caddr_t *) &move,                     "move",         "Move",         "False",        t_Bool},
+       {(caddr_t *) &nomove,           "nomove",       "noMove",       "False",        t_Bool},
+       {(caddr_t *) &rotate,           "rotate",       "Rotate",       "False",        t_Bool},
+       {(caddr_t *) &norotate,         "norotate",     "noRotate",     "False",        t_Bool},
+       /*{(caddr_t *) &size,                   "size",         "Size",         "-1",           t_Int},*/
+       {(caddr_t *) &zoom,                     "zoom",         "Zoom",         "False",        t_Bool},
+       {(caddr_t *) &nozoom,           "nozoom",       "noZoom",       "False",        t_Bool},
+       {(caddr_t *) &image,            "image",        "Image",        "DEFAULT",      t_String},
+       {(caddr_t *) &duration,         "duration",     "Duration",     "30",           t_Int},
+};
+
+static OptionStruct desc[] = {
+#ifdef GRAB
+       {"-grab",               "grab images to create animation"},
+#endif
+       {"-move",               "camera will move"},
+       {"-no-move",    "camera won't move"},
+       {"-rotate",             "camera will rotate"},
+       {"-no-rotate",  "camera won't rotate"},
+       /*{"-size",             "size of the hexagons (1-10)"},*/
+       {"-zoom",               "camera will zoom"},
+       {"-no-zoom",    "camera won't zoom"},
+       {"-image",              "xpm / xbm image file to use for texture"},
+       {"-duration",   "length of time texture will be used"},
+};
+
+ModeSpecOpt gleidescope_opts = {
+       sizeof opts / sizeof opts[0], opts,
+       sizeof vars / sizeof vars[0], vars,
+       desc
+};
+
+#ifdef USE_MODULES
+ModStruct   gleidescope_description = { 
+     "gleidescope", "init_gleidescope", "draw_gleidescope", "release_gleidescope",
+     "draw_gleidescope", "init_gleidescope", NULL, &gleidescope_opts,
+     1000, 1, 2, 1, 4, 1.0, "",
+     "GL Kaleidescope", 0, NULL};
+#endif
+
+/*
+**-----------------------------------------------------------------------------
+**     Typedefs
+**-----------------------------------------------------------------------------
+*/
+
+typedef struct hex_s {
+       GLfloat x, y, z;                /* position */
+} hex_t;
+
+typedef struct {
+       GLfloat x;
+       GLfloat y;
+       GLfloat z;
+} vectorf;
+
+#define        MAX_FADE        500     /* number of fade cycles */
+
+typedef struct {
+       float                   cam_x_speed, cam_z_speed, cam_y_speed;
+       int                             cam_x_phase, cam_z_phase, cam_y_phase;
+       float                   tic;
+       GLXContext              *glx_context;
+       Window                  window;
+       GLfloat                 max_tx, max_ty; /* maximum texture sizes */
+       GLuint                  textures[2];    /* texture handles */
+       GLuint                  visible;                /* texture handle for new texture */
+       GLint                   fade;
+       time_t                  start_time;
+       Bool                    button_down_p;
+} gleidestruct;
+
+#define        XOFFSET (0.8660254f)    /* sin 60' */
+#define        YOFFSET (1.5000000f)    /* cos 60' + 1 */
+
+#if 0
+
+#define        SIZE    3
+
+/* generates a grid with edges of given size */
+/* acd TODO - replace hex[] with this and allow size and distance as parameters */
+
+int
+generate_grid(int size)
+
+       int     i, x, y;
+
+       size--;
+
+       i = size;
+       for (y = -size ; y <= size ; y++) {
+               for (x = -i ; x <= i ; x += 2) {
+                       printf("{XOFFSET * %d, YOFFSET * %d, 0},\n", x, y);
+               }
+               printf("\n");
+               if (y < 0) {
+                       i++;
+               } else {
+                       i--;
+               }
+       }
+       return 0;
+}
+#endif
+
+hex_t hex[] = {
+       /* edges of size 7 */
+       /* number of hexagons required to cover screen depends on camera distance */
+       /* at a distance of 10 this is just about enough. */
+       {XOFFSET * -6, YOFFSET * -6, 0},
+       {XOFFSET * -4, YOFFSET * -6, 0},
+       {XOFFSET * -2, YOFFSET * -6, 0},
+       {XOFFSET * 0, YOFFSET * -6, 0},
+       {XOFFSET * 2, YOFFSET * -6, 0},
+       {XOFFSET * 4, YOFFSET * -6, 0},
+       {XOFFSET * 6, YOFFSET * -6, 0},
+
+       {XOFFSET * -7, YOFFSET * -5, 0},
+       {XOFFSET * -5, YOFFSET * -5, 0},
+       {XOFFSET * -3, YOFFSET * -5, 0},
+       {XOFFSET * -1, YOFFSET * -5, 0},
+       {XOFFSET * 1, YOFFSET * -5, 0},
+       {XOFFSET * 3, YOFFSET * -5, 0},
+       {XOFFSET * 5, YOFFSET * -5, 0},
+       {XOFFSET * 7, YOFFSET * -5, 0},
+
+       {XOFFSET * -8, YOFFSET * -4, 0},
+       {XOFFSET * -6, YOFFSET * -4, 0},
+       {XOFFSET * -4, YOFFSET * -4, 0},
+       {XOFFSET * -2, YOFFSET * -4, 0},
+       {XOFFSET * 0, YOFFSET * -4, 0},
+       {XOFFSET * 2, YOFFSET * -4, 0},
+       {XOFFSET * 4, YOFFSET * -4, 0},
+       {XOFFSET * 6, YOFFSET * -4, 0},
+       {XOFFSET * 8, YOFFSET * -4, 0},
+
+       {XOFFSET * -9, YOFFSET * -3, 0},
+       {XOFFSET * -7, YOFFSET * -3, 0},
+       {XOFFSET * -5, YOFFSET * -3, 0},
+       {XOFFSET * -3, YOFFSET * -3, 0},
+       {XOFFSET * -1, YOFFSET * -3, 0},
+       {XOFFSET * 1, YOFFSET * -3, 0},
+       {XOFFSET * 3, YOFFSET * -3, 0},
+       {XOFFSET * 5, YOFFSET * -3, 0},
+       {XOFFSET * 7, YOFFSET * -3, 0},
+       {XOFFSET * 9, YOFFSET * -3, 0},
+
+       {XOFFSET * -10, YOFFSET * -2, 0},
+       {XOFFSET * -8, YOFFSET * -2, 0},
+       {XOFFSET * -6, YOFFSET * -2, 0},
+       {XOFFSET * -4, YOFFSET * -2, 0},
+       {XOFFSET * -2, YOFFSET * -2, 0},
+       {XOFFSET * 0, YOFFSET * -2, 0},
+       {XOFFSET * 2, YOFFSET * -2, 0},
+       {XOFFSET * 4, YOFFSET * -2, 0},
+       {XOFFSET * 6, YOFFSET * -2, 0},
+       {XOFFSET * 8, YOFFSET * -2, 0},
+       {XOFFSET * 10, YOFFSET * -2, 0},
+
+       {XOFFSET * -11, YOFFSET * -1, 0},
+       {XOFFSET * -9, YOFFSET * -1, 0},
+       {XOFFSET * -7, YOFFSET * -1, 0},
+       {XOFFSET * -5, YOFFSET * -1, 0},
+       {XOFFSET * -3, YOFFSET * -1, 0},
+       {XOFFSET * -1, YOFFSET * -1, 0},
+       {XOFFSET * 1, YOFFSET * -1, 0},
+       {XOFFSET * 3, YOFFSET * -1, 0},
+       {XOFFSET * 5, YOFFSET * -1, 0},
+       {XOFFSET * 7, YOFFSET * -1, 0},
+       {XOFFSET * 9, YOFFSET * -1, 0},
+       {XOFFSET * 11, YOFFSET * -1, 0},
+
+       {XOFFSET * -12, YOFFSET * 0, 0},
+       {XOFFSET * -10, YOFFSET * 0, 0},
+       {XOFFSET * -8, YOFFSET * 0, 0},
+       {XOFFSET * -6, YOFFSET * 0, 0},
+       {XOFFSET * -4, YOFFSET * 0, 0},
+       {XOFFSET * -2, YOFFSET * 0, 0},
+       {XOFFSET * 0, YOFFSET * 0, 0},
+       {XOFFSET * 2, YOFFSET * 0, 0},
+       {XOFFSET * 4, YOFFSET * 0, 0},
+       {XOFFSET * 6, YOFFSET * 0, 0},
+       {XOFFSET * 8, YOFFSET * 0, 0},
+       {XOFFSET * 10, YOFFSET * 0, 0},
+       {XOFFSET * 12, YOFFSET * 0, 0},
+
+       {XOFFSET * -11, YOFFSET * 1, 0},
+       {XOFFSET * -9, YOFFSET * 1, 0},
+       {XOFFSET * -7, YOFFSET * 1, 0},
+       {XOFFSET * -5, YOFFSET * 1, 0},
+       {XOFFSET * -3, YOFFSET * 1, 0},
+       {XOFFSET * -1, YOFFSET * 1, 0},
+       {XOFFSET * 1, YOFFSET * 1, 0},
+       {XOFFSET * 3, YOFFSET * 1, 0},
+       {XOFFSET * 5, YOFFSET * 1, 0},
+       {XOFFSET * 7, YOFFSET * 1, 0},
+       {XOFFSET * 9, YOFFSET * 1, 0},
+       {XOFFSET * 11, YOFFSET * 1, 0},
+
+       {XOFFSET * -10, YOFFSET * 2, 0},
+       {XOFFSET * -8, YOFFSET * 2, 0},
+       {XOFFSET * -6, YOFFSET * 2, 0},
+       {XOFFSET * -4, YOFFSET * 2, 0},
+       {XOFFSET * -2, YOFFSET * 2, 0},
+       {XOFFSET * 0, YOFFSET * 2, 0},
+       {XOFFSET * 2, YOFFSET * 2, 0},
+       {XOFFSET * 4, YOFFSET * 2, 0},
+       {XOFFSET * 6, YOFFSET * 2, 0},
+       {XOFFSET * 8, YOFFSET * 2, 0},
+       {XOFFSET * 10, YOFFSET * 2, 0},
+
+       {XOFFSET * -9, YOFFSET * 3, 0},
+       {XOFFSET * -7, YOFFSET * 3, 0},
+       {XOFFSET * -5, YOFFSET * 3, 0},
+       {XOFFSET * -3, YOFFSET * 3, 0},
+       {XOFFSET * -1, YOFFSET * 3, 0},
+       {XOFFSET * 1, YOFFSET * 3, 0},
+       {XOFFSET * 3, YOFFSET * 3, 0},
+       {XOFFSET * 5, YOFFSET * 3, 0},
+       {XOFFSET * 7, YOFFSET * 3, 0},
+       {XOFFSET * 9, YOFFSET * 3, 0},
+
+       {XOFFSET * -8, YOFFSET * 4, 0},
+       {XOFFSET * -6, YOFFSET * 4, 0},
+       {XOFFSET * -4, YOFFSET * 4, 0},
+       {XOFFSET * -2, YOFFSET * 4, 0},
+       {XOFFSET * 0, YOFFSET * 4, 0},
+       {XOFFSET * 2, YOFFSET * 4, 0},
+       {XOFFSET * 4, YOFFSET * 4, 0},
+       {XOFFSET * 6, YOFFSET * 4, 0},
+       {XOFFSET * 8, YOFFSET * 4, 0},
+
+       {XOFFSET * -7, YOFFSET * 5, 0},
+       {XOFFSET * -5, YOFFSET * 5, 0},
+       {XOFFSET * -3, YOFFSET * 5, 0},
+       {XOFFSET * -1, YOFFSET * 5, 0},
+       {XOFFSET * 1, YOFFSET * 5, 0},
+       {XOFFSET * 3, YOFFSET * 5, 0},
+       {XOFFSET * 5, YOFFSET * 5, 0},
+       {XOFFSET * 7, YOFFSET * 5, 0},
+
+       {XOFFSET * -6, YOFFSET * 6, 0},
+       {XOFFSET * -4, YOFFSET * 6, 0},
+       {XOFFSET * -2, YOFFSET * 6, 0},
+       {XOFFSET * 0, YOFFSET * 6, 0},
+       {XOFFSET * 2, YOFFSET * 6, 0},
+       {XOFFSET * 4, YOFFSET * 6, 0},
+       {XOFFSET * 6, YOFFSET * 6, 0},
+};
+
+/*
+**----------------------------------------------------------------------------
+** Local Variables
+**----------------------------------------------------------------------------
+*/
+
+static gleidestruct *gleidescope = NULL;
+
+/*
+ *load defaults in config structure
+ */
+void setdefaultconfig(void)
+{
+#ifdef GRAB
+       grab = False;
+#endif
+       move = False;
+       rotate = False;
+       zoom = False;
+       image = NULL;
+}
+
+static int xstart;
+static int ystart;
+static double xmouse = 0.0;
+static double ymouse = 0.0;
+
+Bool
+gleidescope_handle_event(ModeInfo *mi, XEvent *event)
+{
+       gleidestruct *gp = &gleidescope[MI_SCREEN(mi)];
+
+       /*
+       printf("event:%d\n", event->xany.type);
+       printf("button:%d\n", event->xbutton.button);
+       */
+       switch(event->xany.type)
+       {
+               case ButtonPress:
+
+                       if (event->xbutton.button == Button1 || event->xbutton.button == Button3)
+                       {
+                               /* store initial values of mouse */
+                               xstart = event->xbutton.x;
+                               ystart = event->xbutton.y;
+
+                               /* button is down */
+                               gp->button_down_p = True;
+                               return True;
+                       }
+#if 0  /* TODO */
+                       else if (event->xbutton.button == Button4)
+                       {
+                               /* zoom in */
+                               return True;
+                       }
+                       else if (event->xbutton.button == Button5)
+                       {
+                               /* zoom out */
+                               return True;
+                       }
+#endif
+                       break;
+
+               case ButtonRelease:
+
+                       if (event->xbutton.button == Button1 || event->xbutton.button == Button3)
+                       {
+                               /* button is up */
+                               gp->button_down_p = False;
+                               return True;
+                       }
+                       break;
+
+               case MotionNotify:
+
+                       if (gp->button_down_p)
+                       {
+                               /* update mouse position */
+                               xmouse += (double)(event->xmotion.x - xstart) / MI_WIDTH(mi);
+                               ymouse += (double)(event->xmotion.y - ystart) / MI_HEIGHT(mi);
+                               xstart = event->xmotion.x;
+                               ystart = event->xmotion.y;
+
+                               return True;
+                       }
+                       break;
+       }
+
+       return False;
+}
+
+#include "grab-ximage.h"
+
+static void
+getSnapshot(ModeInfo *mi, GLuint name)
+{
+       XImage  *ximage;
+       int     status;
+       int     tw, th;
+       gleidestruct *gp = &gleidescope[MI_SCREEN(mi)];
+
+       if (MI_IS_WIREFRAME(mi))
+               return;
+
+       ximage = screen_to_ximage(mi->xgwa.screen, mi->window, 0);
+
+       tw = mi->xgwa.width;
+       th = mi->xgwa.height;
+
+       gp->max_tx = (GLfloat) tw / (GLfloat) ximage->width;
+       gp->max_ty = (GLfloat) th / (GLfloat) ximage->height;
+
+       glBindTexture (GL_TEXTURE_2D, name);
+
+       glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+                       GL_LINEAR_MIPMAP_LINEAR);
+
+       clear_gl_error();
+       status = gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
+                       ximage->width, ximage->height,
+                       GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
+
+       if (!status && glGetError())
+               /* Some implementations of gluBuild2DMipmaps(), but set a GL error anyway.
+               **            We could just call check_gl_error(), but that would exit. */
+               status = -1;
+
+       if (status)
+       {
+               const GLubyte *s = gluErrorString (status);
+               if (s)
+               {
+                       fprintf (stderr, "%s: error mipmapping %dx%d texture: %s\n",
+                                       progname, ximage->width, ximage->height, s);
+               }
+               else
+               {
+                       fprintf (stderr, "%s: error mipmapping %dx%d texture: (unknown)\n",
+                                       progname, ximage->width, ximage->height);
+               }
+               clear_gl_error();
+       }
+       check_gl_error("mipmapping");  /* should get a return code instead of a
+                                                                         GL error, but just in case... */
+
+       free(ximage->data);
+       ximage->data = 0;
+       XDestroyImage (ximage);
+
+       /* remember time of last image change */
+       gp->start_time = time ((time_t *) 0);
+}
+
+static void
+setup_file_texture (ModeInfo *mi, char *filename, GLuint name)
+{
+       Display *dpy = mi->dpy;
+       Visual *visual = mi->xgwa.visual;
+       char buf[1024];
+
+       Colormap cmap = mi->xgwa.colormap;
+       XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
+
+       /* use this texture */
+       glBindTexture(GL_TEXTURE_2D, name);
+
+       clear_gl_error();
+       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+                       image->width, image->height, 0,
+                       GL_RGBA, GL_UNSIGNED_BYTE, image->data);
+       sprintf (buf, "texture: %.100s (%dx%d)",
+                       filename, image->width, image->height);
+       check_gl_error(buf);
+
+       /* setup parameters for texturing */
+       glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+       glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
+
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+}
+
+static void
+setup_texture(ModeInfo * mi, GLuint id)
+{
+       gleidestruct *gp = &gleidescope[MI_SCREEN(mi)];
+
+       if (!image || !*image || !strcmp(image, "DEFAULT")) {
+               /* no image specified - grab screen */
+               getSnapshot(mi, id);
+               /* max_tx and max_ty set in getSnapshot() */
+       } else {
+               /* use supplied image file */
+               setup_file_texture(mi, image, id);
+
+               /* set tx params to use whole image */
+               gp->max_tx = 1.0f;
+               gp->max_ty = 1.0f;
+       }
+
+       check_gl_error("texture initialization");
+
+       /* Need to flip the texture top for bottom for some reason. */
+       glMatrixMode (GL_TEXTURE);
+       glScalef (1, -1, 1);
+       glMatrixMode (GL_MODELVIEW);
+}
+
+#define        VERTEX0 glVertex3f( 0.0000f,  0.000f, 0.0f);
+#define        VERTEX1 glVertex3f( 0.0000f,  1.000f, 0.0f);
+#define        VERTEX2 glVertex3f( XOFFSET,  0.500f, 0.0f);
+#define        VERTEX3 glVertex3f( XOFFSET, -0.500f, 0.0f);
+#define        VERTEX4 glVertex3f( 0.0000f, -1.000f, 0.0f);
+#define        VERTEX5 glVertex3f(-XOFFSET, -0.500f, 0.0f);
+#define        VERTEX6 glVertex3f(-XOFFSET,  0.500f, 0.0f);
+
+static void
+draw_hexagons(ModeInfo *mi, int translucency, GLuint texture)
+{
+       int             i;
+       GLfloat col[4];
+       GLfloat t1x, t1y, t2x, t2y, t3x, t3y;
+       gleidestruct *gp = &gleidescope[MI_SCREEN(mi)];
+       GLfloat tangle2;
+
+       col[0] = 1.0;
+       col[1] = 1.0;
+       col[2] = 1.0;
+       col[3] = (float)translucency / MAX_FADE;
+
+       /* calculate vertices of equilateral triangle within image. */
+       /* t1 is always in centre */
+       t1x = gp->max_tx / 2;
+       t1y = gp->max_ty / 2;
+       /* t2 rotates */
+       t2x = (gp->max_tx / 2) * (1 + cos((ymouse * 2 * M_PI) + (tangle * M_PI / 180)));
+       t2y = (gp->max_ty / 2) * (1 + sin((ymouse * 2 * M_PI) + (tangle * M_PI / 180)));
+       /* t3 is always 60' further around than t2 */
+       tangle2 = (ymouse * 2 * M_PI) + (tangle * M_PI / 180) + (M_PI * 2 / 6);
+       t3x = (gp->max_tx / 2) * (1 + (cos(tangle2)));
+       t3y = (gp->max_ty / 2) * (1 + (sin(tangle2)));
+       /* NB image is flipped vertically hence: */
+       t1y = 1 - t1y;
+       t2y = 1 - t2y;
+       t3y = 1 - t3y;
+       /*printf("texcoords:[%f,%f]->[%f,%f](%f,%f)\n", t1x, t1y, t2x, t2y, gp->max_tx, gp->max_ty);*/
+
+       glColor4f(1.0, 1.0, 1.0, (float)translucency / MAX_FADE);
+       glEnable(GL_TEXTURE_2D);
+       glEnable(GL_BLEND);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       glDepthMask(GL_FALSE);
+       glBindTexture(GL_TEXTURE_2D, gp->textures[texture]);
+
+       for (i = 0 ; i < sizeof(hex) / sizeof(hex[0]) ; i++) {
+
+               glPushMatrix();
+
+               glTranslatef(hex[i].x, hex[i].y, 0.0);
+
+               glBegin(GL_TRIANGLES);
+
+               /*
+               ** six triangles to each hexagon
+               */
+
+               glTexCoord2f(t1x, t1y);
+               VERTEX0;
+               glTexCoord2f(t2x, t2y);
+               VERTEX1;
+               glTexCoord2f(t3x, t3y);
+               VERTEX6;
+
+               glTexCoord2f(t1x, t1y);
+               VERTEX0;
+               glTexCoord2f(t3x, t3y);
+               VERTEX6;
+               glTexCoord2f(t2x, t2y);
+               VERTEX5;
+
+               glTexCoord2f(t1x, t1y);
+               VERTEX0;
+               glTexCoord2f(t2x, t2y);
+               VERTEX5;
+               glTexCoord2f(t3x, t3y);
+               VERTEX4;
+
+               glTexCoord2f(t1x, t1y);
+               VERTEX0;
+               glTexCoord2f(t3x, t3y);
+               VERTEX4;
+               glTexCoord2f(t2x, t2y);
+               VERTEX3;
+
+               glTexCoord2f(t1x, t1y);
+               VERTEX0;
+               glTexCoord2f(t2x, t2y);
+               VERTEX3;
+               glTexCoord2f(t3x, t3y);
+               VERTEX2;
+
+               glTexCoord2f(t1x, t1y);
+               VERTEX0;
+               glTexCoord2f(t3x, t3y);
+               VERTEX2;
+               glTexCoord2f(t2x, t2y);
+               VERTEX1;
+
+               glEnd();
+
+               glPopMatrix();
+       }
+       
+#ifdef DISPLAY_TEXTURE
+       glPushMatrix();
+       /* acd debug - display (bigger, centred) texture */
+       glScalef(2.0, 2.0, 2.0);
+       glTranslatef(-0.5, -0.5, 0.0);
+       glBegin(GL_QUADS);
+       glTexCoord2f(0.0, 0.0);
+       glVertex3f(0.0, 0.0, -0.1);
+       glTexCoord2f(1.0, 0.0);
+       glVertex3f(1.0, 0.0, -0.1);
+       glTexCoord2f(1.0, 1.0);
+       glVertex3f(1.0, 1.0, -0.1);
+       glTexCoord2f(0.0, 1.0);
+       glVertex3f(0.0, 1.0, -0.1);
+       glEnd();
+       /* acd debug - display texture triangle */
+       glColor4f(1.0, 1.0, 1.0, 1.0);
+       glBegin(GL_LINE_LOOP);
+       glVertex3f(t1x, t1y, -0.11);
+       glVertex3f(t2x, t2y, -0.11);
+       glVertex3f(t3x, t3y, -0.11);
+       glEnd();
+       glPopMatrix();
+#endif
+
+       glDisable(GL_TEXTURE_2D);
+       glDepthMask(GL_TRUE);
+       glDisable(GL_BLEND);
+}
+
+/*
+ * main rendering loop
+ */
+static void
+draw(ModeInfo * mi)
+{
+       GLfloat x_angle, y_angle, z_angle;
+       gleidestruct *gp = &gleidescope[MI_SCREEN(mi)];
+       vectorf v1;
+       GLfloat pos[4];
+
+       glClearColor(0.5, 0.5, 0.5, 1.0);
+       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+       glLoadIdentity();
+
+       gp->tic += 0.005f;
+
+       x_angle = gp->cam_x_phase + gp->tic * gp->cam_x_speed;
+       y_angle = gp->cam_y_phase + gp->tic * gp->cam_y_speed;
+       z_angle = gp->cam_z_phase + gp->tic * gp->cam_z_speed;
+
+       if (move) {
+               v1.x = 2 * sin(x_angle);
+               v1.y = 2 * sin(y_angle);
+       } else {
+               v1.x = 0;
+               v1.y = 0;
+       }
+
+       /* size is changed in pinit() to be distance from plane */
+       size = MI_SIZE(mi);
+       if (size > 10) {
+               size = 10;
+       }
+       if (size < -1) {
+               size = -1;
+       }
+       if (size != -1) {
+               /* user defined size */
+               v1.z = size;
+       } else if (zoom) {
+               /* max distance given by adding the constant and the multiplier */
+               v1.z = 5.0 + 4.0 * sin(z_angle);
+       } else {
+               /* default */
+               v1.z = 7.0;
+       }
+
+       /* update rotation angle (but not if mouse button down) */
+       if (rotate && !gp->button_down_p)
+       {
+               float   new_rangle_vel = 0.0;
+
+               /* update camera rotation angle and velocity */
+               rangle += rangle_vel;
+               new_rangle_vel = rangle_vel + rangle_acc;
+               if (new_rangle_vel > -MAX_RANGLE_VEL && new_rangle_vel < MAX_RANGLE_VEL)
+               {
+                       /* new velocity is within limits */
+                       rangle_vel = new_rangle_vel;
+               }
+
+               /* randomly change twisting speed */
+               if ((random() % 1000) < 1)
+               {
+                       rangle_acc = frand(0.002) - 0.001;
+               }
+       }
+
+#ifdef WOBBLE
+       /* this makes the image wobble - requires -move and a larger grid */
+       gluLookAt(0, 0, v1.z, v1.x, v1.y, 0.0, 0.0, 1.0, 0.0);
+#else
+       /* no wobble - camera always perpendicular to grid */
+
+       /* rotating camera rather than entire space - smoother */
+       gluLookAt(
+                       v1.x, v1.y, v1.z,
+                       v1.x, v1.y, 0.0,
+                       sin((xmouse * M_PI * 2) + rangle * M_PI / 180),
+                       cos((xmouse * M_PI * 2) + rangle * M_PI / 180),
+                       0.0);
+#endif
+
+       /* light position same as camera */
+       pos[0] = v1.x;
+       pos[1] = v1.y;
+       pos[2] = v1.z;
+       pos[3] = 0;
+
+       if (gp->fade == 0)
+       {
+               /* not fading */
+               draw_hexagons(mi, MAX_FADE, gp->visible);
+       }
+       else
+       {
+               /* fading - show both textures with alpha */
+               draw_hexagons(mi, MAX_FADE - gp->fade, gp->visible);
+               draw_hexagons(mi, gp->fade, 1 - gp->visible);
+
+               /* fade some more */
+               gp->fade++;
+
+               /* have we faded enough? */
+               if (gp->fade > MAX_FADE)
+               {
+                       /* stop fading */
+                       gp->fade = 0;
+                       gp->visible = 1 - gp->visible;
+               }
+       }
+
+       /* increment texture angle based on time, velocity etc */
+       /* but only if button is not down */
+       if (!gp->button_down_p)
+       {
+               float           new_tangle_vel = 0.0;
+
+               tangle += tangle_vel;
+
+               /* work out new texture angle velocity */
+               new_tangle_vel = tangle_vel + tangle_acc;
+               if (new_tangle_vel > -MAX_TANGLE_VEL && new_tangle_vel < MAX_TANGLE_VEL)
+               {
+                       /* new velocity is inside limits */
+                       tangle_vel = new_tangle_vel;
+               }
+
+               /* randomly change texture angle acceleration */
+               if ((random() % 1000) < 1)
+               {
+                       tangle_acc = frand(0.002) - 0.001;
+               }
+       }
+
+       glFlush();
+}
+
+/* 
+ * new window size or exposure 
+ */
+void reshape_gleidescope(ModeInfo *mi, int width, int height)
+{
+       GLfloat         h = (GLfloat) height / (GLfloat) width;
+
+       glViewport(0, 0, (GLint) width, (GLint) height);
+       glMatrixMode(GL_PROJECTION);
+       glLoadIdentity();
+       gluPerspective(50.0, 1/h, 0.1, 2000.0);
+       glMatrixMode (GL_MODELVIEW);
+
+       glLineWidth(1);
+       glPointSize(1);   
+}
+
+static void
+pinit(ModeInfo * mi)
+{
+       gleidestruct    *gp = &gleidescope[MI_SCREEN(mi)];
+
+       /* set start time - star_time = 0 implies non-dynamic texture */
+       gp->start_time = (time_t)0;
+
+       /* set the texture size to default */
+       gp->max_tx = 1.0;
+       gp->max_ty = 1.0;
+
+       /* no fading */
+       gp->fade = 0;
+
+       glShadeModel(GL_SMOOTH);
+       glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+       glEnable(GL_DEPTH_TEST);
+       glEnable(GL_CULL_FACE);
+       glDisable(GL_LIGHTING);
+
+       /* space for textures */
+       glGenTextures(1, &gp->textures[0]);
+       glGenTextures(1, &gp->textures[1]);
+       gp->visible = 0;
+
+       setup_texture(mi, gp->textures[gp->visible]);
+
+       /*
+       **      want to choose a value for arg randomly if neither -arg nor -no-arg
+       **      is specified. xscreensaver libraries don't seem to let you do this -
+       **      if something isn't true then it is false (pesky two-state boolean values).
+       **      so, i've defined both -arg and -no-arg to arguments and added the 
+       **      following logic.
+       **      (btw if both -arg and -no-arg are defined then arg is set to False)
+       */
+       if (zoom == False && nozoom == False)
+       {
+               /* no zoom preference - randomise */
+               zoom = (((random() & 0x1) == 0x1) ? True : False);
+       }
+       else if (nozoom == True)
+       {
+               /* definately no zoom */
+               zoom = False;
+       }
+
+       if (move == False && nomove == False)
+       {
+               /* no move preference - randomise */
+               move = (((random() & 0x1) == 0x1) ? True : False);
+       }
+       else if (nomove == True)
+       {
+               /* definately no move */
+               move = False;
+       }
+
+       if (rotate == False && norotate == False)
+       {
+               /* no rotate preference - randomise */
+               rotate = (((random() & 0x1) == 0x1) ? True : False);
+       }
+       else if (norotate == True)
+       {
+               /* definately no rotate */
+               rotate = False;
+       }
+
+       /* define cam variables */
+       gp->cam_x_speed = frand(3.0) - 1.5;
+       gp->cam_x_phase = random() % 360;
+       gp->cam_y_speed = frand(3.0) - 1.5;
+       gp->cam_y_phase = random() % 360;
+       gp->cam_z_speed = frand(3.0) - 1.5;
+       gp->cam_z_phase = random() % 360;
+
+       /* initial angular speeds */
+       rangle_vel = frand(0.2) - 0.1;
+       tangle_vel = frand(0.2) - 0.1;
+       rangle_acc = frand(0.002) - 0.001;
+       tangle_acc = frand(0.002) - 0.001;
+
+    /* jwz */
+    {
+      GLfloat speed = 15;
+      rangle_vel *= speed;
+      tangle_vel *= speed;
+      rangle_acc *= speed;
+      tangle_acc *= speed;
+    }
+
+       /* distance is 11 - size */
+       if (size != -1) {
+               if (zoom) {
+                       fprintf(stderr, "-size given. ignoring -zoom.\n");
+                       zoom = False;
+               }
+               if (size < 1) {
+                       size = 1;
+               } else if (size >= 10) {
+                       size = 10;
+               }
+               size = 11 - size;
+       }
+
+#ifdef DEBUG
+printf("phases [%d, %d, %d]\n", gp->cam_x_phase, gp->cam_y_phase, gp->cam_z_phase);
+#endif
+}
+
+void
+init_gleidescope(ModeInfo * mi)
+{
+       gleidestruct *gp;
+       int screen = MI_SCREEN(mi);
+
+
+       if (gleidescope == NULL) {
+               gleidescope = (gleidestruct *) calloc(MI_NUM_SCREENS(mi), sizeof (gleidestruct));
+               if (gleidescope == NULL) {
+                       return;
+               }
+       }
+       gp = &gleidescope[screen];
+       gp->window = MI_WINDOW(mi);
+
+       if ((gp->glx_context = init_GL(mi)) != NULL) {
+
+               reshape_gleidescope(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+
+               glDrawBuffer(GL_BACK);
+
+               /* do initialisation */
+               pinit(mi);
+
+       } else {
+               MI_CLEARWINDOW(mi);
+       }
+}
+
+void
+draw_gleidescope(ModeInfo * mi)
+{
+       gleidestruct    *gp = &gleidescope[MI_SCREEN(mi)];
+       Display         *display = MI_DISPLAY(mi);
+       Window          window = MI_WINDOW(mi);
+
+
+       if (!gp->glx_context)
+               return;
+
+       glDrawBuffer(GL_BACK);
+
+       glXMakeCurrent(display, window, *(gp->glx_context));
+       draw(mi);
+
+       if (mi->fps_p) {
+               do_fps (mi);
+       }
+
+       glFinish();
+       glXSwapBuffers(display, window);
+
+#ifdef GRAB
+       if (grab) {
+               grab_frame(mi);
+       }
+#endif
+
+       /* need to change texture? */
+       if ((gp->start_time != 0) && (duration != -1) && gp->fade == 0) {
+               if (gp->start_time + duration <= time((time_t *)0)) {
+                       /* get new snapshot (into back buffer) and start fade count */
+                       getSnapshot(mi, gp->textures[1 - gp->visible]);
+                       gp->fade = 1;
+               }
+       }
+}
+
+void
+release_gleidescope(ModeInfo * mi)
+{
+       if (gleidescope != NULL) {
+               int screen;
+
+               for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
+                       gleidestruct *gp = &gleidescope[screen];
+
+                       /* acd -  is this needed? */
+                       if (gp->glx_context) {
+                               /* Display lists MUST be freed while their glXContext is current. */
+                               glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
+
+                               /* acd - was code here for freeing things that are no longer in struct */
+                       }
+               }
+               (void) free((void *) gleidescope);
+               gleidescope = NULL;
+       }
+       
+       FreeAllGL(mi);
+}
+
+#endif