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"
29 #include "images/matrix.xbm"
31 #define CHAR_HEIGHT 31
48 XWindowAttributes xgwa;
50 int grid_width, grid_height;
51 int char_width, char_height;
54 Bool insert_top_p, insert_bottom_p;
58 int image_width, image_height;
65 load_images (m_state *state)
68 if (!get_boolean_resource ("mono", "Boolean") &&
69 state->xgwa.depth > 1)
71 XpmAttributes xpmattrs;
73 xpmattrs.valuemask = 0;
76 xpmattrs.valuemask |= XpmCloseness;
77 xpmattrs.closeness = 40000;
80 xpmattrs.valuemask |= XpmVisual;
81 xpmattrs.visual = state->xgwa.visual;
84 xpmattrs.valuemask |= XpmDepth;
85 xpmattrs.depth = state->xgwa.depth;
88 xpmattrs.valuemask |= XpmColormap;
89 xpmattrs.colormap = state->xgwa.colormap;
92 result = XpmCreatePixmapFromData (state->dpy, state->window, matrix,
93 &state->images, 0 /* mask */,
95 if (!state->images || (result != XpmSuccess && result != XpmColorError))
98 state->image_width = xpmattrs.width;
99 state->image_height = xpmattrs.height;
100 state->nglyphs = state->image_height / CHAR_HEIGHT;
103 #endif /* !HAVE_XPM */
105 unsigned long fg, bg;
106 state->image_width = matrix_width;
107 state->image_height = matrix_height;
108 state->nglyphs = state->image_height / CHAR_HEIGHT;
110 fg = get_pixel_resource("foreground", "Foreground",
111 state->dpy, state->xgwa.colormap);
112 bg = get_pixel_resource("background", "Background",
113 state->dpy, state->xgwa.colormap);
115 XCreatePixmapFromBitmapData (state->dpy, state->window,
116 (char *) matrix_bits,
117 state->image_width, state->image_height,
118 bg, fg, state->xgwa.depth);
124 init_matrix (Display *dpy, Window window)
128 m_state *state = (m_state *) calloc (sizeof(*state), 1);
130 state->window = window;
132 XGetWindowAttributes (dpy, window, &state->xgwa);
135 gcv.foreground = get_pixel_resource("foreground", "Foreground",
136 state->dpy, state->xgwa.colormap);
137 gcv.background = get_pixel_resource("background", "Background",
138 state->dpy, state->xgwa.colormap);
139 state->draw_gc = XCreateGC (state->dpy, state->window,
140 GCForeground|GCBackground, &gcv);
141 gcv.foreground = gcv.background;
142 state->erase_gc = XCreateGC (state->dpy, state->window,
143 GCForeground|GCBackground, &gcv);
145 state->char_width = state->image_width / 2;
146 state->char_height = CHAR_HEIGHT;
148 state->grid_width = state->xgwa.width / state->char_width;
149 state->grid_height = state->xgwa.height / state->char_height;
151 state->grid_height++;
153 state->cells = (m_cell *)
154 calloc (sizeof(m_cell), state->grid_width * state->grid_height);
155 state->feeders = (m_feeder *) calloc (sizeof(m_feeder), state->grid_width);
157 state->density = get_integer_resource ("density", "Integer");
159 insert = get_string_resource("insert", "Insert");
160 if (insert && !strcmp(insert, "top"))
162 state->insert_top_p = True;
163 state->insert_bottom_p = False;
165 else if (insert && !strcmp(insert, "bottom"))
167 state->insert_top_p = False;
168 state->insert_bottom_p = True;
170 else if (insert && !strcmp(insert, "both"))
172 state->insert_top_p = True;
173 state->insert_bottom_p = True;
177 if (insert && *insert)
179 "%s: `insert' must be `top', `bottom', or `both', not `%s'\n",
181 state->insert_top_p = False;
182 state->insert_bottom_p = True;
193 insert_glyph (m_state *state, int glyph, int x, int y)
195 Bool bottom_feeder_p = (y >= 0);
198 if (y >= state->grid_height)
203 to = &state->cells[state->grid_width * y + x];
207 for (y = state->grid_height-1; y > 0; y--)
209 from = &state->cells[state->grid_width * (y-1) + x];
210 to = &state->cells[state->grid_width * y + x];
214 to = &state->cells[x];
222 else if (bottom_feeder_p)
223 to->glow = 1 + (random() % 2);
230 feed_matrix (m_state *state)
234 /* Update according to current feeders. */
235 for (x = 0; x < state->grid_width; x++)
237 m_feeder *f = &state->feeders[x];
239 if (f->throttle) /* this is a delay tick, synced to frame. */
243 else if (f->remaining > 0) /* how many items are in the pipe */
245 int g = (random() % state->nglyphs) + 1;
246 insert_glyph (state, g, x, f->y);
248 if (f->y >= 0) /* bottom_feeder_p */
251 else /* if pipe is empty, insert spaces */
253 insert_glyph (state, 0, x, f->y);
254 if (f->y >= 0) /* bottom_feeder_p */
258 if ((random() % 10) == 0) /* randomly change throttle speed */
260 f->throttle = ((random() % 5) + (random() % 5));
266 densitizer (m_state *state)
268 /* Horrid kludge that converts percentages (density of screen coverage)
269 to the parameter that actually controls this. I got this mapping
270 empirically, on a 1024x768 screen. Sue me. */
271 if (state->density < 10) return 85;
272 else if (state->density < 15) return 60;
273 else if (state->density < 20) return 45;
274 else if (state->density < 25) return 25;
275 else if (state->density < 30) return 20;
276 else if (state->density < 35) return 15;
277 else if (state->density < 45) return 10;
278 else if (state->density < 50) return 8;
279 else if (state->density < 55) return 7;
280 else if (state->density < 65) return 5;
281 else if (state->density < 80) return 3;
282 else if (state->density < 90) return 2;
288 hack_matrix (m_state *state)
292 /* Glow some characters. */
293 if (!state->insert_bottom_p)
295 int i = random() % (state->grid_width / 2);
298 int x = random() % state->grid_width;
299 int y = random() % state->grid_height;
300 m_cell *cell = &state->cells[state->grid_width * y + x];
301 if (cell->glyph && cell->glow == 0)
303 cell->glow = random() % 10;
304 cell->changed = True;
309 /* Change some of the feeders. */
310 for (x = 0; x < state->grid_width; x++)
312 m_feeder *f = &state->feeders[x];
313 Bool bottom_feeder_p;
315 if (f->remaining > 0) /* never change if pipe isn't empty */
318 if ((random() % densitizer(state)) != 0) /* then change N% of the time */
321 f->remaining = 3 + (random() % state->grid_height);
322 f->throttle = ((random() % 5) + (random() % 5));
324 if ((random() % 4) != 0)
327 if (state->insert_top_p && state->insert_bottom_p)
328 bottom_feeder_p = (random() & 1);
330 bottom_feeder_p = state->insert_bottom_p;
333 f->y = random() % (state->grid_height / 2);
341 draw_matrix (m_state *state)
349 for (y = 0; y < state->grid_height; y++)
350 for (x = 0; x < state->grid_width; x++)
352 m_cell *cell = &state->cells[state->grid_width * y + x];
360 if (cell->glyph == 0)
361 XFillRectangle (state->dpy, state->window, state->erase_gc,
362 x * state->char_width,
363 y * state->char_height,
367 XCopyArea (state->dpy, state->images, state->window, state->draw_gc,
368 (cell->glow ? state->char_width : 0),
369 (cell->glyph - 1) * state->char_height,
370 state->char_width, state->char_height,
371 x * state->char_width,
372 y * state->char_height);
374 cell->changed = False;
379 cell->changed = True;
386 static int ndens = 0;
387 static int tdens = 0;
393 ((double) (state->grid_width * state->grid_height))));
396 printf ("density: %d%% (%d%%)\n", dens, (tdens / ndens));
405 char *progclass = "XMatrix";
407 char *defaults [] = {
408 ".background: black",
409 ".foreground: green",
416 XrmOptionDescRec options [] = {
417 { "-delay", ".delay", XrmoptionSepArg, 0 },
418 { "-top", ".insert", XrmoptionNoArg, "top" },
419 { "-bottom", ".insert", XrmoptionNoArg, "bottom" },
420 { "-both", ".insert", XrmoptionNoArg, "both" },
421 { "-density", ".density", XrmoptionSepArg, 0 },
427 screenhack (Display *dpy, Window window)
429 m_state *state = init_matrix (dpy, window);
430 int delay = get_integer_resource ("delay", "Integer");
435 screenhack_handle_events (dpy);
436 if (delay) usleep (delay);