1 /* xscreensaver, Copyright (c) 1999 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * Matrix -- simulate the text scrolls from the movie "The Matrix".
13 * The movie people distribute their own Windows/Mac screensaver that does
14 * a similar thing, so I wrote one for Unix. However, that version (the
15 * Windows/Mac version at http://www.whatisthematrix.com/) doesn't match my
16 * memory of what the screens in the movie looked like, so my `xmatrix'
17 * does things differently.
20 #include "screenhack.h"
22 #include <X11/Xutil.h>
26 # include "images/matrix.xpm"
27 # include "images/matrix2.xpm"
30 #include "images/matrix.xbm"
31 #include "images/matrix2.xbm"
40 unsigned int glyph : 8;
42 unsigned int changed : 1;
43 unsigned int spinner : 1;
55 XWindowAttributes xgwa;
57 int grid_width, grid_height;
58 int char_width, char_height;
62 Bool insert_top_p, insert_bottom_p;
66 int image_width, image_height;
73 load_images (m_state *state)
76 if (!get_boolean_resource ("mono", "Boolean") &&
77 state->xgwa.depth > 1)
79 XpmAttributes xpmattrs;
81 xpmattrs.valuemask = 0;
84 xpmattrs.valuemask |= XpmCloseness;
85 xpmattrs.closeness = 40000;
88 xpmattrs.valuemask |= XpmVisual;
89 xpmattrs.visual = state->xgwa.visual;
92 xpmattrs.valuemask |= XpmDepth;
93 xpmattrs.depth = state->xgwa.depth;
96 xpmattrs.valuemask |= XpmColormap;
97 xpmattrs.colormap = state->xgwa.colormap;
100 result = XpmCreatePixmapFromData (state->dpy, state->window,
104 &state->images, 0 /* mask */,
106 if (!state->images || (result != XpmSuccess && result != XpmColorError))
109 state->image_width = xpmattrs.width;
110 state->image_height = xpmattrs.height;
111 state->nglyphs = CHAR_ROWS;
114 #endif /* !HAVE_XPM */
116 unsigned long fg, bg;
117 state->image_width = (state->small_p ? matrix2_width : matrix_width);
118 state->image_height = (state->small_p ? matrix2_height : matrix_height);
119 state->nglyphs = CHAR_ROWS;
121 fg = get_pixel_resource("foreground", "Foreground",
122 state->dpy, state->xgwa.colormap);
123 bg = get_pixel_resource("background", "Background",
124 state->dpy, state->xgwa.colormap);
126 XCreatePixmapFromBitmapData (state->dpy, state->window,
128 ? (char *) matrix2_bits
129 : (char *) matrix_bits),
130 state->image_width, state->image_height,
131 bg, fg, state->xgwa.depth);
137 init_spinners (m_state *state)
139 int i = get_integer_resource ("spinners", "Integer");
143 for (y = 0; y < state->grid_height; y++)
144 for (x = 0; x < state->grid_width; x++)
146 cell = &state->cells[state->grid_width * y + x];
152 x = random() % state->grid_width;
153 y = random() % state->grid_height;
154 cell = &state->cells[state->grid_width * y + x];
161 init_matrix (Display *dpy, Window window)
165 m_state *state = (m_state *) calloc (sizeof(*state), 1);
167 state->window = window;
169 XGetWindowAttributes (dpy, window, &state->xgwa);
171 state->small_p = get_boolean_resource ("small", "Boolean");
174 gcv.foreground = get_pixel_resource("foreground", "Foreground",
175 state->dpy, state->xgwa.colormap);
176 gcv.background = get_pixel_resource("background", "Background",
177 state->dpy, state->xgwa.colormap);
178 state->draw_gc = XCreateGC (state->dpy, state->window,
179 GCForeground|GCBackground, &gcv);
180 gcv.foreground = gcv.background;
181 state->erase_gc = XCreateGC (state->dpy, state->window,
182 GCForeground|GCBackground, &gcv);
184 state->char_width = state->image_width / CHAR_COLS;
185 state->char_height = state->image_height / CHAR_ROWS;
187 state->grid_width = state->xgwa.width / state->char_width;
188 state->grid_height = state->xgwa.height / state->char_height;
190 state->grid_height++;
192 state->cells = (m_cell *)
193 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
194 state->feeders = (m_feeder *) calloc (sizeof(m_feeder), state->grid_width);
196 state->density = get_integer_resource ("density", "Integer");
198 insert = get_string_resource("insert", "Insert");
199 if (insert && !strcmp(insert, "top"))
201 state->insert_top_p = True;
202 state->insert_bottom_p = False;
204 else if (insert && !strcmp(insert, "bottom"))
206 state->insert_top_p = False;
207 state->insert_bottom_p = True;
209 else if (insert && !strcmp(insert, "both"))
211 state->insert_top_p = True;
212 state->insert_bottom_p = True;
216 if (insert && *insert)
218 "%s: `insert' must be `top', `bottom', or `both', not `%s'\n",
220 state->insert_top_p = False;
221 state->insert_bottom_p = True;
227 init_spinners (state);
234 insert_glyph (m_state *state, int glyph, int x, int y)
236 Bool bottom_feeder_p = (y >= 0);
239 if (y >= state->grid_height)
244 to = &state->cells[state->grid_width * y + x];
248 for (y = state->grid_height-1; y > 0; y--)
250 from = &state->cells[state->grid_width * (y-1) + x];
251 to = &state->cells[state->grid_width * y + x];
252 to->glyph = from->glyph;
253 to->glow = from->glow;
256 to = &state->cells[x];
264 else if (bottom_feeder_p)
265 to->glow = 1 + (random() % 2);
272 feed_matrix (m_state *state)
276 /* Update according to current feeders. */
277 for (x = 0; x < state->grid_width; x++)
279 m_feeder *f = &state->feeders[x];
281 if (f->throttle) /* this is a delay tick, synced to frame. */
285 else if (f->remaining > 0) /* how many items are in the pipe */
287 int g = (random() % state->nglyphs) + 1;
288 insert_glyph (state, g, x, f->y);
290 if (f->y >= 0) /* bottom_feeder_p */
293 else /* if pipe is empty, insert spaces */
295 insert_glyph (state, 0, x, f->y);
296 if (f->y >= 0) /* bottom_feeder_p */
300 if ((random() % 10) == 0) /* randomly change throttle speed */
302 f->throttle = ((random() % 5) + (random() % 5));
308 densitizer (m_state *state)
310 /* Horrid kludge that converts percentages (density of screen coverage)
311 to the parameter that actually controls this. I got this mapping
312 empirically, on a 1024x768 screen. Sue me. */
313 if (state->density < 10) return 85;
314 else if (state->density < 15) return 60;
315 else if (state->density < 20) return 45;
316 else if (state->density < 25) return 25;
317 else if (state->density < 30) return 20;
318 else if (state->density < 35) return 15;
319 else if (state->density < 45) return 10;
320 else if (state->density < 50) return 8;
321 else if (state->density < 55) return 7;
322 else if (state->density < 65) return 5;
323 else if (state->density < 80) return 3;
324 else if (state->density < 90) return 2;
330 hack_matrix (m_state *state)
334 /* Glow some characters. */
335 if (!state->insert_bottom_p)
337 int i = random() % (state->grid_width / 2);
340 int x = random() % state->grid_width;
341 int y = random() % state->grid_height;
342 m_cell *cell = &state->cells[state->grid_width * y + x];
343 if (cell->glyph && cell->glow == 0)
345 cell->glow = random() % 10;
351 /* Change some of the feeders. */
352 for (x = 0; x < state->grid_width; x++)
354 m_feeder *f = &state->feeders[x];
355 Bool bottom_feeder_p;
357 if (f->remaining > 0) /* never change if pipe isn't empty */
360 if ((random() % densitizer(state)) != 0) /* then change N% of the time */
363 f->remaining = 3 + (random() % state->grid_height);
364 f->throttle = ((random() % 5) + (random() % 5));
366 if ((random() % 4) != 0)
369 if (state->insert_top_p && state->insert_bottom_p)
370 bottom_feeder_p = (random() & 1);
372 bottom_feeder_p = state->insert_bottom_p;
375 f->y = random() % (state->grid_height / 2);
380 if (! (random() % 500))
381 init_spinners (state);
386 draw_matrix (m_state *state)
394 for (y = 0; y < state->grid_height; y++)
395 for (x = 0; x < state->grid_width; x++)
397 m_cell *cell = &state->cells[state->grid_width * y + x];
405 if (cell->glyph == 0)
406 XFillRectangle (state->dpy, state->window, state->erase_gc,
407 x * state->char_width,
408 y * state->char_height,
412 XCopyArea (state->dpy, state->images, state->window, state->draw_gc,
413 ((cell->glow > 0 || cell->spinner)
414 ? (state->char_width * GLOW_COL)
416 ? (state->char_width * PLAIN_COL)
417 : (state->char_width * FADE_COL))),
418 (cell->glyph - 1) * state->char_height,
419 state->char_width, state->char_height,
420 x * state->char_width,
421 y * state->char_height);
430 else if (cell->glow < 0)
440 cell->glyph = random() % CHAR_ROWS;
448 static int ndens = 0;
449 static int tdens = 0;
455 ((double) (state->grid_width * state->grid_height))));
458 printf ("density: %d%% (%d%%)\n", dens, (tdens / ndens));
467 char *progclass = "XMatrix";
469 char *defaults [] = {
470 ".background: black",
471 ".foreground: green",
480 XrmOptionDescRec options [] = {
481 { "-small", ".small", XrmoptionNoArg, "True" },
482 { "-large", ".small", XrmoptionNoArg, "False" },
483 { "-delay", ".delay", XrmoptionSepArg, 0 },
484 { "-top", ".insert", XrmoptionNoArg, "top" },
485 { "-bottom", ".insert", XrmoptionNoArg, "bottom" },
486 { "-both", ".insert", XrmoptionNoArg, "both" },
487 { "-density", ".density", XrmoptionSepArg, 0 },
493 screenhack (Display *dpy, Window window)
495 m_state *state = init_matrix (dpy, window);
496 int delay = get_integer_resource ("delay", "Integer");
501 screenhack_handle_events (dpy);
502 if (delay) usleep (delay);