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
;
44 static void load_images (m_state
*state
) {
46 if (state
->xgwa
.depth
> 1) {
48 XpmAttributes xpmattrs
;
51 xpmattrs
.valuemask
= 0;
52 xpmattrs
.valuemask
|= XpmCloseness
;
53 xpmattrs
.closeness
= 40000;
54 xpmattrs
.valuemask
|= XpmVisual
;
55 xpmattrs
.visual
= state
->xgwa
.visual
;
56 xpmattrs
.valuemask
|= XpmDepth
;
57 xpmattrs
.depth
= state
->xgwa
.depth
;
58 xpmattrs
.valuemask
|= XpmColormap
;
59 xpmattrs
.colormap
= state
->xgwa
.colormap
;
64 result
= XpmCreatePixmapFromData (state
->dpy
, state
->window
, small
,
65 &state
->images
, 0 /* mask */,
67 } else if (PixmapSize
== 2){
70 result
= XpmCreatePixmapFromData (state
->dpy
, state
->window
, medium
,
71 &state
->images
, 0 /* mask */,
76 result
= XpmCreatePixmapFromData (state
->dpy
, state
->window
, large
,
77 &state
->images
, 0 /* mask */,
81 if (!state
->images
|| (result
!= XpmSuccess
&& result
!= XpmColorError
))
84 state
->image_width
= xpmattrs
.width
;
85 state
->image_height
= xpmattrs
.height
;
86 state
->nglyphs
= state
->image_height
/ CHAR_HEIGHT
;
90 state
->image_width
= matrix_width
;
91 state
->image_height
= matrix_height
;
92 state
->nglyphs
= state
->image_height
/ CHAR_HEIGHT
;
94 state
->images
= XCreatePixmapFromBitmapData (state
->dpy
, state
->window
,
96 state
->image_width
, state
->image_height
,
97 back_pix
, fore_pix
, state
->xgwa
.depth
);
103 m_state
*init_matrix( Display
*dpy
, Window window
) {
105 m_state
*state
= (m_state
*) calloc (sizeof(*state
), 1);
109 state
->window
= window
;
111 XGetWindowAttributes (dpy
, window
, &state
->xgwa
);
114 state
->draw_gc
= NormalGC
;
115 state
->erase_gc
= EraseGC
;
117 state
->char_width
= state
->image_width
/ 2;
118 state
->char_height
= CHAR_HEIGHT
;
120 state
->grid_width
= state
->xgwa
.width
/ state
->char_width
;
121 state
->grid_height
= state
->xgwa
.height
/ state
->char_height
;
123 state
->grid_height
++;
125 state
->cells
= (m_cell
*)calloc (sizeof(m_cell
), state
->grid_width
* state
->grid_height
);
126 state
->feeders
= (m_feeder
*)calloc (sizeof(m_feeder
), state
->grid_width
);
130 state
->insert_top_p
= False
;
131 state
->insert_bottom_p
= True
;
139 static void insert_glyph (m_state
*state
, int glyph
, int x
, int y
) {
141 Bool bottom_feeder_p
= (y
>= 0);
144 if (y
>= state
->grid_height
) return;
146 if (bottom_feeder_p
) {
148 to
= &state
->cells
[state
->grid_width
* y
+ x
];
152 for (y
= state
->grid_height
-1; y
> 0; y
--) {
154 from
= &state
->cells
[state
->grid_width
* (y
-1) + x
];
155 to
= &state
->cells
[state
->grid_width
* y
+ x
];
160 to
= &state
->cells
[x
];
168 else if (bottom_feeder_p
) to
->glow
= 1 + (random() % 2);
174 static void feed_matrix (m_state
*state
) {
180 * Update according to current feeders.
182 for (x
= 0; x
< state
->grid_width
; x
++) {
184 m_feeder
*f
= &state
->feeders
[x
];
186 if (f
->throttle
) { /* this is a delay tick, synced to frame. */
190 } else if (f
->remaining
> 0) { /* how many items are in the pipe */
192 int g
= (random() % state
->nglyphs
) + 1;
193 insert_glyph (state
, g
, x
, f
->y
);
195 if (f
->y
>= 0) f
->y
++; /* bottom_feeder_p */
197 } else { /* if pipe is empty, insert spaces */
199 insert_glyph (state
, 0, x
, f
->y
);
200 if (f
->y
>= 0) f
->y
++; /* bottom_feeder_p */
204 if ((random() % 10) == 0) { /* randomly change throttle speed */
206 f
->throttle
= ((random() % 5) + (random() % 5));
214 static int densitizer (m_state
*state
) {
216 /* Horrid kludge that converts percentages (density of screen coverage)
217 to the parameter that actually controls this. I got this mapping
218 empirically, on a 1024x768 screen. Sue me. */
219 if (state
->density
< 10) return 85;
220 else if (state
->density
< 15) return 60;
221 else if (state
->density
< 20) return 45;
222 else if (state
->density
< 25) return 25;
223 else if (state
->density
< 30) return 20;
224 else if (state
->density
< 35) return 15;
225 else if (state
->density
< 45) return 10;
226 else if (state
->density
< 50) return 8;
227 else if (state
->density
< 55) return 7;
228 else if (state
->density
< 65) return 5;
229 else if (state
->density
< 80) return 3;
230 else if (state
->density
< 90) return 2;
236 static void hack_matrix (m_state
*state
) {
240 /* Glow some characters. */
241 if (!state
->insert_bottom_p
) {
243 int i
= random() % (state
->grid_width
/ 2);
246 int x
= random() % state
->grid_width
;
247 int y
= random() % state
->grid_height
;
248 m_cell
*cell
= &state
->cells
[state
->grid_width
* y
+ x
];
249 if (cell
->glyph
&& cell
->glow
== 0) {
251 cell
->glow
= random() % 10;
252 cell
->changed
= True
;
258 /* Change some of the feeders. */
259 for (x
= 0; x
< state
->grid_width
; x
++) {
261 m_feeder
*f
= &state
->feeders
[x
];
262 Bool bottom_feeder_p
;
264 if (f
->remaining
> 0) /* never change if pipe isn't empty */
267 if ((random() % densitizer(state
)) != 0) /* then change N% of the time */
270 f
->remaining
= 3 + (random() % state
->grid_height
);
271 f
->throttle
= ((random() % 5) + (random() % 5));
273 if ((random() % 4) != 0)
276 if (state
->insert_top_p
&& state
->insert_bottom_p
)
277 bottom_feeder_p
= (random() & 1);
279 bottom_feeder_p
= state
->insert_bottom_p
;
282 f
->y
= random() % (state
->grid_height
/ 2);
289 void draw_matrix (m_state
*state
, int d
) {
295 feed_matrix( state
);
296 hack_matrix( state
);
298 for (y
= 0; y
< state
->grid_height
; y
++) {
299 for (x
= 0; x
< state
->grid_width
; x
++) {
301 m_cell
*cell
= &state
->cells
[state
->grid_width
* y
+ x
];
303 if ( cell
->glyph
) count
++;
305 if ( !cell
->changed
) continue;
307 if ( cell
->glyph
== 0 ) {
309 XFillRectangle( state
->dpy
, state
->window
, state
->erase_gc
,
310 x
* state
->char_width
, y
* state
->char_height
,
311 state
->char_width
, state
->char_height
);
314 XCopyArea( state
->dpy
, state
->images
, state
->window
, state
->draw_gc
,
315 (cell
->glow
? state
->char_width
: 0), (cell
->glyph
- 1) * state
->char_height
,
316 state
->char_width
, state
->char_height
, x
* state
->char_width
, y
* state
->char_height
);
320 cell
->changed
= False
;
322 if (cell
->glow
> 0) {
325 cell
->changed
= True
;
336 static int ndens
= 0;
337 static int tdens
= 0;
343 ((double) (state
->grid_width
* state
->grid_height
))));
346 printf ("density: %d%% (%d%%)\n", dens
, (tdens
/ ndens
));