wmcore: use pkg-config in Makefile.
[dockapps.git] / wmMatrix / matrix.c
blob82d82782611bce8f0190ec878018aeacc7d00479
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
9 * implied warranty.
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 <stdio.h>
21 #include "images/small.xpm"
22 #include "images/medium.xpm"
23 #include "images/large.xpm"
24 #include "images/matrix.xbm"
26 #define CHAR_HEIGHT 4
29 #include "matrix.h"
31 #include "resources.h"
34 extern GC NormalGC;
35 extern GC EraseGC;
36 extern Pixel back_pix, fore_pix;
37 extern int PixmapSize;
38 int CHAR_HEIGHT;
40 static void load_images(m_state * state)
43 if (state->xgwa.depth > 1) {
45 XpmAttributes xpmattrs;
46 int result;
48 xpmattrs.valuemask = 0;
49 xpmattrs.valuemask |= XpmCloseness;
50 xpmattrs.closeness = 40000;
51 xpmattrs.valuemask |= XpmVisual;
52 xpmattrs.visual = state->xgwa.visual;
53 xpmattrs.valuemask |= XpmDepth;
54 xpmattrs.depth = state->xgwa.depth;
55 xpmattrs.valuemask |= XpmColormap;
56 xpmattrs.colormap = state->xgwa.colormap;
58 if (PixmapSize == 1) {
60 CHAR_HEIGHT = 4;
61 result = XpmCreatePixmapFromData(state->dpy, state->window, small, &state->images, 0 /* mask */ ,
62 &xpmattrs);
63 } else if (PixmapSize == 2) {
65 CHAR_HEIGHT = 6;
66 result = XpmCreatePixmapFromData(state->dpy, state->window, medium, &state->images, 0 /* mask */ ,
67 &xpmattrs);
68 } else {
70 CHAR_HEIGHT = 8;
71 result = XpmCreatePixmapFromData(state->dpy, state->window, large, &state->images, 0 /* mask */ ,
72 &xpmattrs);
75 if (!state->images || (result != XpmSuccess && result != XpmColorError))
76 state->images = 0;
78 state->image_width = xpmattrs.width;
79 state->image_height = xpmattrs.height;
80 state->nglyphs = state->image_height / CHAR_HEIGHT;
82 } else {
84 state->image_width = matrix_width;
85 state->image_height = matrix_height;
86 state->nglyphs = state->image_height / CHAR_HEIGHT;
88 state->images = XCreatePixmapFromBitmapData(state->dpy, state->window,
89 (char *)matrix_bits,
90 state->image_width, state->image_height, back_pix, fore_pix, state->xgwa.depth);
95 m_state *init_matrix(Display * dpy, Window window)
98 m_state *state = (m_state *) calloc(sizeof(*state), 1);
100 state->dpy = dpy;
101 state->window = window;
103 XGetWindowAttributes(dpy, window, &state->xgwa);
104 load_images(state);
106 state->draw_gc = NormalGC;
107 state->erase_gc = EraseGC;
109 state->char_width = state->image_width / 2;
110 state->char_height = CHAR_HEIGHT;
112 state->grid_width = state->xgwa.width / state->char_width;
113 state->grid_height = state->xgwa.height / state->char_height;
114 state->grid_width++;
115 state->grid_height++;
117 state->cells = (m_cell *) calloc(sizeof(m_cell), state->grid_width * state->grid_height);
118 state->feeders = (m_feeder *) calloc(sizeof(m_feeder), state->grid_width);
120 state->density = 40;
122 state->insert_top_p = False;
123 state->insert_bottom_p = True;
125 return state;
129 static void insert_glyph(m_state * state, int glyph, int x, int y)
132 Bool bottom_feeder_p = (y >= 0);
133 m_cell *from, *to;
135 if (y >= state->grid_height)
136 return;
138 if (bottom_feeder_p) {
140 to = &state->cells[state->grid_width * y + x];
142 } else {
144 for (y = state->grid_height - 1; y > 0; y--) {
146 from = &state->cells[state->grid_width * (y - 1) + x];
147 to = &state->cells[state->grid_width * y + x];
148 *to = *from;
149 to->changed = True;
152 to = &state->cells[x];
156 to->glyph = glyph;
157 to->changed = True;
159 if (!to->glyph) ;
160 else if (bottom_feeder_p)
161 to->glow = 1 + (random() % 2);
162 else
163 to->glow = 0;
167 static void feed_matrix(m_state * state)
170 int x;
173 * Update according to current feeders.
175 for (x = 0; x < state->grid_width; x++) {
177 m_feeder *f = &state->feeders[x];
179 if (f->throttle) { /* this is a delay tick, synced to frame. */
181 f->throttle--;
183 } else if (f->remaining > 0) { /* how many items are in the pipe */
185 int g = (random() % state->nglyphs) + 1;
186 insert_glyph(state, g, x, f->y);
187 f->remaining--;
188 if (f->y >= 0)
189 f->y++; /* bottom_feeder_p */
191 } else { /* if pipe is empty, insert spaces */
193 insert_glyph(state, 0, x, f->y);
194 if (f->y >= 0)
195 f->y++; /* bottom_feeder_p */
199 if ((random() % 10) == 0) { /* randomly change throttle speed */
201 f->throttle = ((random() % 5) + (random() % 5));
209 static int densitizer(m_state * state)
212 /* Horrid kludge that converts percentages (density of screen coverage)
213 to the parameter that actually controls this. I got this mapping
214 empirically, on a 1024x768 screen. Sue me. */
215 if (state->density < 10)
216 return 85;
217 else if (state->density < 15)
218 return 60;
219 else if (state->density < 20)
220 return 45;
221 else if (state->density < 25)
222 return 25;
223 else if (state->density < 30)
224 return 20;
225 else if (state->density < 35)
226 return 15;
227 else if (state->density < 45)
228 return 10;
229 else if (state->density < 50)
230 return 8;
231 else if (state->density < 55)
232 return 7;
233 else if (state->density < 65)
234 return 5;
235 else if (state->density < 80)
236 return 3;
237 else if (state->density < 90)
238 return 2;
239 else
240 return 1;
244 static void hack_matrix(m_state * state)
247 int x;
249 /* Glow some characters. */
250 if (!state->insert_bottom_p) {
252 int i = random() % (state->grid_width / 2);
253 while (--i > 0) {
255 int x = random() % state->grid_width;
256 int y = random() % state->grid_height;
257 m_cell *cell = &state->cells[state->grid_width * y + x];
258 if (cell->glyph && cell->glow == 0) {
260 cell->glow = random() % 10;
261 cell->changed = True;
267 /* Change some of the feeders. */
268 for (x = 0; x < state->grid_width; x++) {
270 m_feeder *f = &state->feeders[x];
271 Bool bottom_feeder_p;
273 if (f->remaining > 0) /* never change if pipe isn't empty */
274 continue;
276 if ((random() % densitizer(state)) != 0) /* then change N% of the time */
277 continue;
279 f->remaining = 3 + (random() % state->grid_height);
280 f->throttle = ((random() % 5) + (random() % 5));
282 if ((random() % 4) != 0)
283 f->remaining = 0;
285 if (state->insert_top_p && state->insert_bottom_p)
286 bottom_feeder_p = (random() & 1);
287 else
288 bottom_feeder_p = state->insert_bottom_p;
290 if (bottom_feeder_p)
291 f->y = random() % (state->grid_height / 2);
292 else
293 f->y = -1;
297 void draw_matrix(m_state * state, int d)
300 int x, y;
301 int count = 0;
303 state->density = d;
304 feed_matrix(state);
305 hack_matrix(state);
307 for (y = 0; y < state->grid_height; y++) {
308 for (x = 0; x < state->grid_width; x++) {
310 m_cell *cell = &state->cells[state->grid_width * y + x];
312 if (cell->glyph)
313 count++;
315 if (!cell->changed)
316 continue;
318 if (cell->glyph == 0) {
320 XFillRectangle(state->dpy, state->window, state->erase_gc,
321 x * state->char_width, y * state->char_height, state->char_width, state->char_height);
322 } else {
324 XCopyArea(state->dpy, state->images, state->window, state->draw_gc,
325 (cell->glow ? state->char_width : 0), (cell->glyph - 1) * state->char_height,
326 state->char_width, state->char_height, x * state->char_width, y * state->char_height);
330 cell->changed = False;
332 if (cell->glow > 0) {
334 cell->glow--;
335 cell->changed = True;
342 #if 0
344 static int i = 0;
345 static int ndens = 0;
346 static int tdens = 0;
347 i++;
348 if (i > 50) {
349 int dens = (100.0 * (((double)count) / ((double)(state->grid_width * state->grid_height))));
350 tdens += dens;
351 ndens++;
352 printf("density: %d%% (%d%%)\n", dens, (tdens / ndens));
353 i = 0;
356 #endif