-/* glmatrix, Copyright (c) 2003 Jamie Zawinski <jwz@jwz.org>
+/* glmatrix, Copyright (c) 2003-2018 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 free_matrix 0
+# define release_matrix 0
#undef countof
#define countof(x) (sizeof((x))/sizeof((*x)))
#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
#include "xlockmore.h"
-#include "xpm-ximage.h"
-#include <ctype.h>
+#include "ximage-loader.h"
-#include "../images/matrix3.xpm"
+#include "images/gen/matrix3_png.h"
#ifdef USE_GL /* whole file */
-#include <GL/glu.h>
-
-#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
#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;
+ 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
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);
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;
+ int y = 0;
+
+ if (width > height * 5) { /* tiny window: show middle */
+ height = width * 9/16;
+ y = -height/2;
+ h = height / (GLfloat) width;
+ }
- glViewport (0, 0, (GLint) width, (GLint) height);
+ glViewport (0, y, (GLint) width, (GLint) height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
}
-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
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.).
*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
}
int cw, ch;
int orig_w, orig_h;
- /* The Matrix XPM is 512x598 -- but GL texture sizes must be powers of 2.
+ /* The Matrix image is 512x598 -- but GL texture sizes must be powers of 2.
So we waste some padding rows to round up.
*/
- xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
- matrix3_xpm);
+ xi = image_data_to_ximage (mi->dpy, mi->xgwa.visual,
+ matrix3_png, sizeof(matrix3_png));
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++)
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);
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,
- GL_UNSIGNED_INT_8_8_8_8_REV, xi->data);
+ GL_UNSIGNED_BYTE, xi->data);
{
char buf[255];
sprintf (buf, "creating %dx%d texture:", xi->width, xi->height);
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;
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);
mp = &mps[MI_SCREEN(mi)];
mp->glx_context = init_GL(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);
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 ();
+ 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)
{
glXSwapBuffers(dpy, window);
}
+XSCREENSAVER_MODULE_2 ("GLMatrix", glmatrix, matrix)
+
#endif /* USE_GL */