X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fglx%2Fglmatrix.c;h=4782c1cc7f65ce912822f00a26e8d9caa8ae1a4f;hb=4361b69d3178d7fc98d0388f9a223af6c2651aba;hp=1fa1123cf394dc4e30ff3048034a50aa5e3603df;hpb=96bdd7cf6ea60c418a76921acaf0e34d6f5be930;p=xscreensaver diff --git a/hacks/glx/glmatrix.c b/hacks/glx/glmatrix.c index 1fa1123c..4782c1cc 100644 --- a/hacks/glx/glmatrix.c +++ b/hacks/glx/glmatrix.c @@ -1,4 +1,4 @@ -/* glmatrix, Copyright (c) 2003 Jamie Zawinski +/* glmatrix, Copyright (c) 2003, 2004 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 @@ -16,37 +16,12 @@ * movie did. */ -#include - -extern XtAppContext app; - -#define PROGCLASS "GLMatrix" -#define HACK_INIT init_matrix -#define HACK_DRAW draw_matrix -#define HACK_RESHAPE reshape_matrix -#define HACK_HANDLE_EVENT matrix_handle_event -#define EVENT_MASK PointerMotionMask -#define matrix_opts xlockmore_opts - -#define DEF_SPEED "1.0" -#define DEF_DENSITY "20" -#define DEF_FOG "True" -#define DEF_WAVES "True" -#define DEF_ROTATE "True" -#define DEF_TEXTURE "True" -#define DEF_MODE "Matrix" - #define DEFAULTS "*delay: 30000 \n" \ "*showFPS: False \n" \ "*wireframe: False \n" \ - "*mode: " DEF_MODE " \n" \ - "*speed: " DEF_SPEED " \n" \ - "*density: " DEF_DENSITY " \n" \ - "*fog: " DEF_FOG " \n" \ - "*waves: " DEF_WAVES " \n" \ - "*texture: " DEF_TEXTURE " \n" \ - "*rotate: " DEF_ROTATE " \n" \ +# define refresh_matrix 0 +# define release_matrix 0 #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) @@ -55,50 +30,71 @@ extern XtAppContext app; #include "xlockmore.h" #include "xpm-ximage.h" -#include +#ifdef __GNUC__ + __extension__ /* don't warn about "string length is greater than the length + ISO C89 compilers are required to support" when including + the following XPM file... */ +#endif #include "../images/matrix3.xpm" #ifdef USE_GL /* whole file */ -#include - -#include "gllist.h" +#define DEF_SPEED "1.0" +#define DEF_DENSITY "20" +#define DEF_CLOCK "False" +#define DEF_FOG "True" +#define DEF_WAVES "True" +#define DEF_ROTATE "True" +#define DEF_TEXTURE "True" +#define DEF_MODE "Matrix" +#define DEF_TIMEFMT " %l%M%p " #define CHAR_COLS 16 #define CHAR_ROWS 13 -static int real_char_rows; - -static int matrix_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, - 192, 193, 194, 195, 196, 197, 198, 199, - 200, 201, 202, 203, 204, 205, 206, 207 }; -static int decimal_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }; -static int hex_encoding[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, - 33, 34, 35, 36, 37, 38 }; -static int binary_encoding[] = { 16, 17 }; -static int dna_encoding[] = { 33, 35, 39, 52 }; -#if 0 -static unsigned char char_map[256] = { - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0 */ - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 16 */ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 32 */ + +static const int matrix_encoding[] = { + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, +# if 0 + 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207 +# else + 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175 +# endif + }; +static const int decimal_encoding[] = { + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }; +static const int hex_encoding[] = { + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38 }; +static const int binary_encoding[] = { 16, 17 }; +static const int dna_encoding[] = { 33, 35, 39, 52 }; + +static const unsigned char char_map[256] = { + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 0 */ + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 16 */ + 0, 1, 2, 96, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 32 */ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 48 */ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 64 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 80 */ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 96 */ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 112 */ - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 128 */ - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 144 */ + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 128 */ + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 144 */ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 160 */ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 176 */ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 192 */ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 208 */ +#if 0 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, /* 224 */ 176,177,178,195,180,181,182,183,184,185,186,187,188,189,190,191 /* 240 */ +#else /* see spank_image() */ + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 224 */ + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, /* 240 */ +#endif }; -#endif /* 0 */ #define CURSOR_GLYPH 97 @@ -109,7 +105,7 @@ static unsigned char char_map[256] = { #define WAVE_SIZE 22 /* periodicity of color (brightness) waves */ #define SPLASH_RATIO 0.7 /* ratio of GRID_DEPTH where chars hit the screen */ -static struct { GLfloat x, y; } nice_views[] = { +static const struct { GLfloat x, y; } nice_views[] = { { 0, 0 }, { 0, -20 }, /* this is a list of viewer rotations that look nice. */ { 0, 20 }, /* every now and then we switch to a new one. */ @@ -145,6 +141,9 @@ typedef struct { 0 means no glyph; negative means "spinner". If non-zero, real value is abs(G)-1. */ + Bool highlight[GRID_SIZE]; + /* some glyphs may be highlighted */ + int spin_speed; /* Rotate all spinners every this-many frames */ int spin_tick; /* frame counter */ @@ -161,7 +160,7 @@ typedef struct { GLuint texture; int nstrips; strip *strips; - int *glyph_map; + const int *glyph_map; int nglyphs; GLfloat tex_char_width, tex_char_height; @@ -170,15 +169,19 @@ typedef struct { GLfloat view_x, view_y; int view_steps, view_tick; Bool auto_tracking_p; + int track_tick; + + int real_char_rows; + GLfloat brightness_ramp[WAVE_SIZE]; } matrix_configuration; static matrix_configuration *mps = NULL; -static GLfloat brightness_ramp[WAVE_SIZE]; - static GLfloat speed; static GLfloat density; +static Bool do_clock; +static char *timefmt; static Bool do_fog; static Bool do_waves; static Bool do_rotate; @@ -193,6 +196,9 @@ static XrmOptionDescRec opts[] = { { "-hexadecimal", ".mode", XrmoptionNoArg, "hexadecimal" }, { "-decimal", ".mode", XrmoptionNoArg, "decimal" }, { "-dna", ".mode", XrmoptionNoArg, "dna" }, + { "-clock", ".clock", XrmoptionNoArg, "True" }, + { "+clock", ".clock", XrmoptionNoArg, "False" }, + { "-timefmt", ".timefmt", XrmoptionSepArg, 0 }, { "-fog", ".fog", XrmoptionNoArg, "True" }, { "+fog", ".fog", XrmoptionNoArg, "False" }, { "-waves", ".waves", XrmoptionNoArg, "True" }, @@ -204,16 +210,18 @@ static XrmOptionDescRec opts[] = { }; static argtype vars[] = { - {(caddr_t *) &mode_str, "mode", "Mode", DEF_MODE, t_String}, - {(caddr_t *) &speed, "speed", "Speed", DEF_SPEED, t_Float}, - {(caddr_t *) &density, "density", "Density", DEF_DENSITY, t_Float}, - {(caddr_t *) &do_fog, "fog", "Fog", DEF_FOG, t_Bool}, - {(caddr_t *) &do_waves, "waves", "Waves", DEF_WAVES, t_Bool}, - {(caddr_t *) &do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool}, - {(caddr_t *) &do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool}, + {&mode_str, "mode", "Mode", DEF_MODE, t_String}, + {&speed, "speed", "Speed", DEF_SPEED, t_Float}, + {&density, "density", "Density", DEF_DENSITY, t_Float}, + {&do_clock, "clock", "Clock", DEF_CLOCK, t_Bool}, + {&timefmt, "timefmt", "Timefmt", DEF_TIMEFMT, t_String}, + {&do_fog, "fog", "Fog", DEF_FOG, t_Bool}, + {&do_waves, "waves", "Waves", DEF_WAVES, t_Bool}, + {&do_rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool}, + {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool}, }; -ModeSpecOpt matrix_opts = {countof(opts), opts, countof(vars), vars, NULL}; +ENTRYPOINT ModeSpecOpt matrix_opts = {countof(opts), opts, countof(vars), vars, NULL}; /* Re-randomize the state of one strip. @@ -223,6 +231,7 @@ reset_strip (ModeInfo *mi, strip *s) { matrix_configuration *mp = &mps[MI_SCREEN(mi)]; int i; + Bool time_displayed_p = False; /* never display time twice in one strip */ memset (s, 0, sizeof(*s)); s->x = (GLfloat) (frand(GRID_SIZE) - (GRID_SIZE/2)); @@ -245,15 +254,38 @@ reset_strip (ModeInfo *mi, strip *s) s->wave_tick = 0; for (i = 0; i < GRID_SIZE; i++) - { - int draw_p = (random() % 7); - int spin_p = (draw_p && !(random() % 20)); - int g = (draw_p - ? mp->glyph_map[(random() % mp->nglyphs)] + 1 - : 0); - if (spin_p) g = -g; - s->glyphs[i] = g; - } + if (do_clock && + !time_displayed_p && + (i < GRID_SIZE-5) && /* display approx. once per 5 strips */ + !(random() % (GRID_SIZE-5)*5)) + { + int j; + char text[80]; + time_t now = time ((time_t *) 0); + struct tm *tm = localtime (&now); + strftime (text, sizeof(text)-1, timefmt, tm); + + /* render time into the strip */ + for (j = 0; j < strlen(text) && i < GRID_SIZE; j++, i++) + { + s->glyphs[i] = char_map [((unsigned char *) text)[j]] + 1; + s->highlight[i] = True; + } + + time_displayed_p = True; + } + else + { + int draw_p = (random() % 7); + int spin_p = (draw_p && !(random() % 20)); + int g = (draw_p + ? mp->glyph_map[(random() % mp->nglyphs)] + 1 + : 0); + if (spin_p) g = -g; + s->glyphs[i] = g; + s->highlight[i] = False; + } + s->spinner_glyph = - (mp->glyph_map[(random() % mp->nglyphs)] + 1); } @@ -325,7 +357,7 @@ tick_strip (ModeInfo *mi, strip *s) /* Draw a single character at the given position and brightness. */ static void -draw_glyph (ModeInfo *mi, int glyph, +draw_glyph (ModeInfo *mi, int glyph, Bool highlight, GLfloat x, GLfloat y, GLfloat z, GLfloat brightness) { @@ -333,7 +365,7 @@ draw_glyph (ModeInfo *mi, int glyph, int wire = MI_IS_WIREFRAME(mi); GLfloat w = mp->tex_char_width; GLfloat h = mp->tex_char_height; - GLfloat cx, cy; + GLfloat cx = 0, cy = 0; GLfloat S = 1; Bool spinner_p = (glyph < 0); @@ -355,7 +387,7 @@ draw_glyph (ModeInfo *mi, int glyph, int ccy = ((glyph - 1) / CHAR_COLS); cx = ccx * w; - cy = (real_char_rows - ccy - 1) * h; + cy = (mp->real_char_rows - ccy - 1) * h; if (do_fog) { @@ -367,11 +399,17 @@ draw_glyph (ModeInfo *mi, int glyph, } { - GLfloat r, g, b, a = 1; + GLfloat r, g, b, a; + + if (highlight) + brightness *= 2; + if (!do_texture && !spinner_p) - r = b = 0, g = brightness; + r = b = 0, g = 1; else - r = g = b = brightness; + r = g = b = 1; + + a = brightness; /* If the glyph is very close to the screen (meaning it is very large, and is about to splash into the screen and vanish) then start fading @@ -386,23 +424,7 @@ draw_glyph (ModeInfo *mi, int glyph, if (i < 0) i = 0; else if (i >= WAVE_SIZE) i = WAVE_SIZE-1; - a = brightness_ramp[i]; -#if 1 - /* I don't understand this -- if I change the alpha on the color of - the quad, I'd expect that to make the quad more transparent. - But instead, it seems to be making the transparent parts of the - texture on the quad be *less* transparent! So as we fade out, - we fade towards a completely solid rectangle. WTF? - - So, for now, instead of changing the alpha, just make the colors - be darker. This isn't quite right (it causes a large dark glyph - to occlude the brighter glyphs behind it) but it's close... - */ - r *= a; - g *= a; - b *= a; - a = 1; -#endif + a *= mp->brightness_ramp[i]; } glColor4f (r,g,b,a); @@ -435,6 +457,7 @@ draw_glyph (ModeInfo *mi, int glyph, static void draw_strip (ModeInfo *mi, strip *s) { + matrix_configuration *mp = &mps[MI_SCREEN(mi)]; int i; for (i = 0; i < GRID_SIZE; i++) { @@ -453,15 +476,17 @@ draw_strip (ModeInfo *mi, strip *s) { int j = WAVE_SIZE - ((i + (GRID_SIZE - s->wave_position)) % WAVE_SIZE); - brightness = brightness_ramp[j]; + brightness = mp->brightness_ramp[j]; } - draw_glyph (mi, g, s->x, s->y - i, s->z, brightness); + draw_glyph (mi, g, s->highlight[i], + s->x, s->y - i, s->z, brightness); } } if (!s->erasing_p) - draw_glyph (mi, s->spinner_glyph, s->x, s->y - s->spinner_y, s->z, 1.0); + draw_glyph (mi, s->spinner_glyph, False, + s->x, s->y - s->spinner_y, s->z, 1.0); } @@ -506,9 +531,8 @@ auto_track (ModeInfo *mi) /* if we're not moving, maybe start moving. Otherwise, do nothing. */ if (! mp->auto_tracking_p) { - static int tick = 0; - if (++tick < 20/speed) return; - tick = 0; + if (++mp->track_tick < 20/speed) return; + mp->track_tick = 0; if (! (random() % 20)) mp->auto_tracking_p = True; else @@ -544,7 +568,7 @@ auto_track (ModeInfo *mi) /* Window management, etc */ -void +ENTRYPOINT void reshape_matrix (ModeInfo *mi, int width, int height) { GLfloat h = (GLfloat) height / (GLfloat) width; @@ -565,19 +589,19 @@ reshape_matrix (ModeInfo *mi, int width, int height) } -Bool +ENTRYPOINT Bool matrix_handle_event (ModeInfo *mi, XEvent *event) { matrix_configuration *mp = &mps[MI_SCREEN(mi)]; if (event->xany.type == ButtonPress && - event->xbutton.button & Button1) + event->xbutton.button == Button1) { mp->button_down_p = True; return True; } else if (event->xany.type == ButtonRelease && - event->xbutton.button & Button1) + event->xbutton.button == Button1) { mp->button_down_p = False; return True; @@ -587,6 +611,7 @@ matrix_handle_event (ModeInfo *mi, XEvent *event) } +#if 0 static Bool bigendian (void) { @@ -594,6 +619,7 @@ bigendian (void) u.i = 1; return !u.c[0]; } +#endif /* The image with the characters in it is 512x598, meaning that it needs to @@ -606,16 +632,20 @@ bigendian (void) If this hack ever grows into something that displays full Latin1 text, well then, Something Else Will Need To Be Done. + + Since currently GLMatrix does not run textclient / xscreensaver-text, + it's not an issue. (XMatrix does that.) + */ static void -spank_image (XImage *xi) +spank_image (matrix_configuration *mp, XImage *xi) { int ch = xi->height / CHAR_ROWS; int cut = 2; unsigned char *bits = (unsigned char *) xi->data; unsigned char *from, *to, *s, *end; int L = xi->bytes_per_line * ch; - int i; +/* int i;*/ /* Copy row 12 into 10 (which really means, copy 2 into 0, since texture data is upside down.). @@ -645,13 +675,15 @@ spank_image (XImage *xi) *s++ = 0; xi->height -= (cut * ch); - real_char_rows -= cut; + mp->real_char_rows -= cut; +# if 0 /* Finally, pull the map indexes back to match the new bits. */ for (i = 0; i < countof(matrix_encoding); i++) if (matrix_encoding[i] > (CHAR_COLS * (CHAR_ROWS - cut))) matrix_encoding[i] -= (cut * CHAR_COLS); +# endif } @@ -671,8 +703,8 @@ load_textures (ModeInfo *mi, Bool flip_p) matrix3_xpm); orig_w = xi->width; orig_h = xi->height; - real_char_rows = CHAR_ROWS; - spank_image (xi); + mp->real_char_rows = CHAR_ROWS; + spank_image (mp, xi); if (xi->height != 512 && xi->height != 1024) { @@ -719,9 +751,15 @@ load_textures (ModeInfo *mi, Bool flip_p) */ { int rpos, gpos, bpos, apos; /* bitfield positions */ +#if 0 + /* #### Cherub says that the little-endian case must be taken on MacOSX, + or else the colors/alpha are the wrong way around. How can + that be the case? + */ if (bigendian()) rpos = 24, gpos = 16, bpos = 8, apos = 0; else +#endif rpos = 0, gpos = 8, bpos = 16, apos = 24; for (y = 0; y < xi->height; y++) @@ -731,7 +769,7 @@ load_textures (ModeInfo *mi, Bool flip_p) unsigned char r = (p >> rpos) & 0xFF; unsigned char g = (p >> gpos) & 0xFF; unsigned char b = (p >> bpos) & 0xFF; - unsigned char a = ~g; + unsigned char a = g; g = 0xFF; p = (r << rpos) | (g << gpos) | (b << bpos) | (a << apos); XPutPixel (xi, x, y, p); @@ -744,7 +782,8 @@ load_textures (ModeInfo *mi, Bool flip_p) glGenTextures (1, &mp->texture); glPixelStorei (GL_UNPACK_ALIGNMENT, 4); - glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width); + /* messes up -fps */ + /* glPixelStorei (GL_UNPACK_ROW_LENGTH, xi->width);*/ glBindTexture (GL_TEXTURE_2D, mp->texture); check_gl_error ("texture init"); glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, xi->width, xi->height, 0, GL_RGBA, @@ -768,12 +807,11 @@ load_textures (ModeInfo *mi, Bool flip_p) glPixelStorei (GL_UNPACK_ALIGNMENT, 1); check_gl_error ("texture param"); - xi->data = 0; /* don't free the texture data */ XDestroyImage (xi); } -void +ENTRYPOINT void init_matrix (ModeInfo *mi) { matrix_configuration *mp; @@ -784,14 +822,7 @@ init_matrix (ModeInfo *mi) if (wire) do_texture = False; - if (!mps) { - mps = (matrix_configuration *) - calloc (MI_NUM_SCREENS(mi), sizeof (matrix_configuration)); - if (!mps) { - fprintf(stderr, "%s: out of memory\n", progname); - exit(1); - } - } + MI_INIT (mi, mps, NULL); mp = &mps[MI_SCREEN(mi)]; mp->glx_context = init_GL(mi); @@ -846,12 +877,7 @@ init_matrix (ModeInfo *mi) glEnable(GL_NORMALIZE); if (do_texture) - { - load_textures (mi, flip_p); - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); - } + load_textures (mi, flip_p); /* to scale coverage-percent to strips, this number looks about right... */ mp->nstrips = (int) (density * 2.2); @@ -883,7 +909,7 @@ init_matrix (ModeInfo *mi) j *= (M_PI / 2); /* j ranges from 0.0 - PI/2 */ j = sin (j); /* j ranges from 0.0 - 1.0 */ j = 0.2 + (j * 0.8); /* j ranges from 0.2 - 1.0 */ - brightness_ramp[i] = j; + mp->brightness_ramp[i] = j; /* printf("%2d %8.2f\n", i, j); */ } @@ -903,6 +929,7 @@ draw_grid (ModeInfo *mi) glDisable(GL_BLEND); } glPushMatrix(); + glColor3f(1, 1, 1); glBegin(GL_LINES); glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0); @@ -946,7 +973,7 @@ draw_grid (ModeInfo *mi) #endif /* DEBUG */ -void +ENTRYPOINT void draw_matrix (ModeInfo *mi) { matrix_configuration *mp = &mps[MI_SCREEN(mi)]; @@ -957,9 +984,25 @@ draw_matrix (ModeInfo *mi) if (!mp->glx_context) return; + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context)); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix (); + glRotatef(current_device_rotation(), 0, 0, 1); + + if (do_texture) + { + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + + /* Jeff Epler points out: + By using GL_ONE instead of GL_SRC_ONE_MINUS_ALPHA, glyphs are + added to each other, so that a bright glyph with a darker one + in front is a little brighter than the bright glyph alone. + */ + glBlendFunc (GL_SRC_ALPHA, GL_ONE); + } if (do_rotate) { @@ -1018,4 +1061,6 @@ draw_matrix (ModeInfo *mi) glXSwapBuffers(dpy, window); } +XSCREENSAVER_MODULE_2 ("GLMatrix", glmatrix, matrix) + #endif /* USE_GL */