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.
21 #include "images/small.xpm"
22 #include "images/medium.xpm"
23 #include "images/large.xpm"
24 #include "images/matrix.xbm"
31 #include "resources.h"
36 extern Pixel back_pix
, fore_pix
;
37 extern int PixmapSize
;
40 static void load_images(m_state
* state
)
43 if (state
->xgwa
.depth
> 1) {
45 XpmAttributes xpmattrs
;
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) {
61 result
= XpmCreatePixmapFromData(state
->dpy
, state
->window
, small
, &state
->images
, 0 /* mask */ ,
63 } else if (PixmapSize
== 2) {
66 result
= XpmCreatePixmapFromData(state
->dpy
, state
->window
, medium
, &state
->images
, 0 /* mask */ ,
71 result
= XpmCreatePixmapFromData(state
->dpy
, state
->window
, large
, &state
->images
, 0 /* mask */ ,
75 if (!state
->images
|| (result
!= XpmSuccess
&& result
!= XpmColorError
))
78 state
->image_width
= xpmattrs
.width
;
79 state
->image_height
= xpmattrs
.height
;
80 state
->nglyphs
= state
->image_height
/ CHAR_HEIGHT
;
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
,
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);
101 state
->window
= window
;
103 XGetWindowAttributes(dpy
, window
, &state
->xgwa
);
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
;
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
);
122 state
->insert_top_p
= False
;
123 state
->insert_bottom_p
= True
;
129 static void insert_glyph(m_state
* state
, int glyph
, int x
, int y
)
132 Bool bottom_feeder_p
= (y
>= 0);
135 if (y
>= state
->grid_height
)
138 if (bottom_feeder_p
) {
140 to
= &state
->cells
[state
->grid_width
* y
+ x
];
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
];
152 to
= &state
->cells
[x
];
160 else if (bottom_feeder_p
)
161 to
->glow
= 1 + (random() % 2);
167 static void feed_matrix(m_state
* state
)
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. */
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
);
189 f
->y
++; /* bottom_feeder_p */
191 } else { /* if pipe is empty, insert spaces */
193 insert_glyph(state
, 0, x
, f
->y
);
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)
217 else if (state
->density
< 15)
219 else if (state
->density
< 20)
221 else if (state
->density
< 25)
223 else if (state
->density
< 30)
225 else if (state
->density
< 35)
227 else if (state
->density
< 45)
229 else if (state
->density
< 50)
231 else if (state
->density
< 55)
233 else if (state
->density
< 65)
235 else if (state
->density
< 80)
237 else if (state
->density
< 90)
244 static void hack_matrix(m_state
* state
)
249 /* Glow some characters. */
250 if (!state
->insert_bottom_p
) {
252 int i
= random() % (state
->grid_width
/ 2);
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 */
276 if ((random() % densitizer(state
)) != 0) /* then change N% of the time */
279 f
->remaining
= 3 + (random() % state
->grid_height
);
280 f
->throttle
= ((random() % 5) + (random() % 5));
282 if ((random() % 4) != 0)
285 if (state
->insert_top_p
&& state
->insert_bottom_p
)
286 bottom_feeder_p
= (random() & 1);
288 bottom_feeder_p
= state
->insert_bottom_p
;
291 f
->y
= random() % (state
->grid_height
/ 2);
297 void draw_matrix(m_state
* state
, int d
)
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
];
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
);
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) {
335 cell
->changed
= True
;
345 static int ndens
= 0;
346 static int tdens
= 0;
349 int dens
= (100.0 * (((double)count
) / ((double)(state
->grid_width
* state
->grid_height
))));
352 printf("density: %d%% (%d%%)\n", dens
, (tdens
/ ndens
));