-/* glmatrix, Copyright (c) 2003 Jamie Zawinski <jwz@jwz.org>
+/* glmatrix, Copyright (c) 2003, 2004 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* movie did.
*/
-#include <X11/Intrinsic.h>
-
-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)))
#include "xlockmore.h"
#include "xpm-ximage.h"
-#include <ctype.h>
+#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 <GL/glu.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 "
#include "gllist.h"
#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
#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. */
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 */
GLuint texture;
int nstrips;
strip *strips;
- int *glyph_map;
+ const int *glyph_map;
int nglyphs;
GLfloat tex_char_width, tex_char_height;
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;
{ "-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" },
};
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.
{
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));
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);
}
/* 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)
{
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);
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)
{
{
GLfloat r, g, b, a = 1;
+
+ if (highlight)
+ brightness *= 2;
+
if (!do_texture && !spinner_p)
r = b = 0, g = brightness;
else
if (i < 0) i = 0;
else if (i >= WAVE_SIZE) i = WAVE_SIZE-1;
- a = brightness_ramp[i];
+ a = mp->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.
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...
+ be darker. This works out ok so long as we use GL_ONE in
+ glBlendFunc, so that stacked glyph colors are added together.
*/
r *= a;
g *= a;
static void
draw_strip (ModeInfo *mi, strip *s)
{
+ matrix_configuration *mp = &mps[MI_SCREEN(mi)];
int i;
for (i = 0; i < GRID_SIZE; i++)
{
{
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);
}
/* 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
/* Window management, etc
*/
-void
+ENTRYPOINT void
reshape_matrix (ModeInfo *mi, int width, int height)
{
GLfloat h = (GLfloat) height / (GLfloat) width;
}
-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;
}
+#if 0
static Bool
bigendian (void)
{
u.i = 1;
return !u.c[0];
}
+#endif
/* The image with the characters in it is 512x598, meaning that it needs to
well then, Something Else Will Need To Be Done.
*/
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.).
*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
}
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)
{
*/
{
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++)
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;
load_textures (mi, flip_p);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
- glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
+
+ /* Jeff Epler points out:
+ By using GL_ONE instead of GL_SRC_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_ONE_MINUS_SRC_ALPHA, /* GL_SRC_ALPHA */ GL_ONE);
}
/* to scale coverage-percent to strips, this number looks about right... */
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); */
}
glDisable(GL_BLEND);
}
glPushMatrix();
+
glColor3f(1, 1, 1);
glBegin(GL_LINES);
glVertex3f(-GRID_SIZE, 0, 0); glVertex3f(GRID_SIZE, 0, 0);
#endif /* DEBUG */
-void
+ENTRYPOINT void
draw_matrix (ModeInfo *mi)
{
matrix_configuration *mp = &mps[MI_SCREEN(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 ();
glXSwapBuffers(dpy, window);
}
+XSCREENSAVER_MODULE_2 ("GLMatrix", glmatrix, matrix)
+
#endif /* USE_GL */