1 /* xscreensaver, Copyright (c) 1992, 1995, 1996, 1997
2 * Jamie Zawinski <jwz@netscape.com>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
13 /* 18-Sep-97: Johannes Keukelaar <johannes@nada.kth.se>: Added some color.
14 * Using -mono gives the old behaviour. (Modified by jwz.)
16 /* Flying through an asteroid field. Based on TI Explorer Lisp code by
17 John Nguyen <johnn@hx.lcs.mit.edu>
22 #include "screenhack.h"
25 #define MIN_DEPTH 2 /* rocks disappear when they get this close */
26 #define MAX_DEPTH 60 /* this is where rocks appear */
27 #define MIN_SIZE 3 /* how small where pixmaps are not used */
28 #define MAX_SIZE 200 /* how big (in pixels) rocks are at depth 1 */
29 #define DEPTH_SCALE 100 /* how many ticks there are between depths */
30 #define SIN_RESOLUTION 1000
32 #define MAX_DEP 0.3 /* how far the displacement can be (percent) */
33 #define DIRECTION_CHANGE_RATE 60
34 #define MAX_DEP_SPEED 5 /* Maximum speed for movement */
35 #define MOVE_STYLE 0 /* Only 0 and 1. Distinguishes the fact that
36 these are the rocks that are moving (1)
37 or the rocks source (0). */
39 /* there's not much point in the above being user-customizable, but those
40 numbers might want to be tweaked for displays with an order of magnitude
41 higher resolution or compute power.
44 static double sins [SIN_RESOLUTION];
45 static double coss [SIN_RESOLUTION];
46 static double depths [(MAX_DEPTH + 1) * DEPTH_SCALE];
50 static int width, height, midx, midy;
51 static int dep_x, dep_y;
53 static XColor *colors;
61 static GC threed_left_gc, threed_right_gc;
62 static double threed_delta;
65 (threed_delta * 40.0 * \
66 (1.0 - ((MAX_DEPTH * DEPTH_SCALE / 2) / \
67 ((z) + 20.0 * DEPTH_SCALE))))
79 static struct rock *rocks;
81 static Pixmap pixmaps [MAX_SIZE];
84 static void rock_compute (struct rock *);
85 static void rock_draw (struct rock *, Bool draw_p);
88 rock_reset (struct rock *rock)
90 rock->real_size = MAX_SIZE;
91 rock->r = (SIN_RESOLUTION * 0.7) + (random () % (30 * SIN_RESOLUTION));
92 rock->theta = random () % SIN_RESOLUTION;
93 rock->depth = MAX_DEPTH * DEPTH_SCALE;
94 rock->color = random() % ncolors;
96 rock_draw (rock, True);
100 rock_tick (struct rock *rock, int d)
104 rock_draw (rock, False);
105 rock->depth -= speed;
108 rock->theta = (rock->theta + d) % SIN_RESOLUTION;
110 while (rock->theta < 0)
111 rock->theta += SIN_RESOLUTION;
112 if (rock->depth < (MIN_DEPTH * DEPTH_SCALE))
117 rock_draw (rock, True);
120 else if ((random () % 40) == 0)
125 rock_compute (struct rock *rock)
127 double factor = depths [rock->depth];
128 double rsize = rock->real_size * factor;
130 rock->size = (int) (rsize + 0.5);
131 rock->diff = (int) GETZDIFF(rock->depth);
132 rock->x = midx + (coss [rock->theta] * rock->r * factor);
133 rock->y = midy + (sins [rock->theta] * rock->r * factor);
137 double move_factor = (((double) MOVE_STYLE) -
138 (((double) rock->depth) /
139 (((double) (MAX_DEPTH + 1)) *
140 ((double) DEPTH_SCALE))));
141 /* move_factor is 0 when the rock is close, 1 when far */
142 rock->x += (((double) dep_x) * move_factor);
143 rock->y += (((double) dep_y) * move_factor);
148 rock_draw (rock, draw_p)
153 ? (threed ? erase_gc : draw_gcs[rock->color])
156 if (rock->x <= 0 || rock->y <= 0 || rock->x >= width || rock->y >= height)
158 /* this means that if a rock were to go off the screen at 12:00, but
159 would have been visible at 3:00, it won't come back once the observer
160 rotates around so that the rock would have been visible again.
171 if (draw_p) gc = threed_left_gc;
172 XDrawPoint (dpy, window, gc, rock->x - rock->diff, rock->y);
173 if (draw_p) gc = threed_right_gc;
174 XDrawPoint (dpy, window, gc, rock->x + rock->diff, rock->y);
178 XDrawPoint (dpy, window, gc, rock->x, rock->y);
181 else if (rock->size <= MIN_SIZE || !draw_p)
185 if (draw_p) gc = threed_left_gc;
186 XFillRectangle(dpy, window, gc,
187 rock->x - rock->size / 2 - rock->diff,
188 rock->y - rock->size / 2,
189 rock->size, rock->size);
190 if (draw_p) gc = threed_right_gc;
191 XFillRectangle(dpy, window, gc,
192 rock->x - rock->size / 2 + rock->diff,
193 rock->y - rock->size / 2,
194 rock->size, rock->size);
198 XFillRectangle (dpy, window, gc,
199 rock->x - rock->size/2, rock->y - rock->size/2,
200 rock->size, rock->size);
203 else if (rock->size < MAX_SIZE)
208 XCopyPlane(dpy, pixmaps[rock->size], window, gc,
209 0, 0, rock->size, rock->size,
210 rock->x - rock->size / 2 - rock->diff,
211 rock->y - rock->size / 2, 1L);
212 gc = threed_right_gc;
213 XCopyPlane(dpy, pixmaps[rock->size], window, gc,
214 0, 0, rock->size, rock->size,
215 rock->x - rock->size / 2 + rock->diff,
216 rock->y - rock->size / 2, 1L);
220 XCopyPlane (dpy, pixmaps [rock->size], window, gc,
221 0, 0, rock->size, rock->size,
222 rock->x - rock->size/2, rock->y - rock->size/2,
230 init_pixmaps (Display *dpy, Window window)
234 GC fg_gc = 0, bg_gc = 0;
235 pixmaps [0] = pixmaps [1] = 0;
236 for (i = MIN_DEPTH; i < MAX_SIZE; i++)
238 int w = (1+(i/32))<<5; /* server might be faster if word-aligned */
240 Pixmap p = XCreatePixmap (dpy, window, w, h, 1);
245 fprintf (stderr, "%s: couldn't allocate pixmaps", progname);
249 { /* must use drawable of pixmap, not window (fmh) */
251 fg_gc = XCreateGC (dpy, p, GCForeground, &gcv);
253 bg_gc = XCreateGC (dpy, p, GCForeground, &gcv);
255 XFillRectangle (dpy, p, bg_gc, 0, 0, w, h);
256 points [0].x = i * 0.15; points [0].y = i * 0.85;
257 points [1].x = i * 0.00; points [1].y = i * 0.20;
258 points [2].x = i * 0.30; points [2].y = i * 0.00;
259 points [3].x = i * 0.40; points [3].y = i * 0.10;
260 points [4].x = i * 0.90; points [4].y = i * 0.10;
261 points [5].x = i * 1.00; points [5].y = i * 0.55;
262 points [6].x = i * 0.45; points [6].y = i * 1.00;
263 XFillPolygon (dpy, p, fg_gc, points, 7, Nonconvex, CoordModeOrigin);
265 XFreeGC (dpy, fg_gc);
266 XFreeGC (dpy, bg_gc);
271 compute_move(int axe) /* 0 for x, 1 for y */
273 static int current_dep[2] = {0, 0};
274 static int speed[2] = {0, 0};
275 static short direction[2] = {0, 0};
276 static int limit[2] = {0, 0};
282 current_dep[axe] += speed[axe]; /* We adjust the displacement */
284 if (current_dep[axe] > (int) (limit[axe] * max_dep))
286 if (current_dep[axe] > limit[axe])
287 current_dep[axe] = limit[axe];
289 } /* This is when we reach the upper screen limit */
290 if (current_dep[axe] < (int) (-limit[axe] * max_dep))
292 if (current_dep[axe] < -limit[axe])
293 current_dep[axe] = -limit[axe];
295 } /* This is when we reach the lower screen limit */
296 if (direction[axe] == 1) /* We adjust the speed */
298 else if (direction[axe] == -1)
301 if (speed[axe] > MAX_DEP_SPEED)
302 speed[axe] = MAX_DEP_SPEED;
303 else if (speed[axe] < -MAX_DEP_SPEED)
304 speed[axe] = -MAX_DEP_SPEED;
306 if (move_p && !(random() % DIRECTION_CHANGE_RATE))
308 /* We change direction */
309 change = random() & 1;
312 if (direction[axe] == 0)
313 direction[axe] = change - 1; /* 0 becomes either 1 or -1 */
315 direction[axe] = 0; /* -1 or 1 become 0 */
318 return (current_dep[axe]);
328 dep_x = compute_move(0);
329 dep_y = compute_move(1);
332 for (i = 0; i < nrocks; i++)
333 rock_tick (&rocks [i], d);
340 static int current_delta = 0; /* observer Z rotation */
341 static int window_tick = 50;
342 static int new_delta = 0;
343 static int dchange_tick = 0;
345 if (window_tick++ == 50)
347 XWindowAttributes xgwa;
348 XGetWindowAttributes (dpy, window, &xgwa);
351 height = xgwa.height;
356 if (current_delta != new_delta)
358 if (dchange_tick++ == 5)
361 if (current_delta < new_delta)
369 if (! (random() % 50))
371 new_delta = ((random() % 11) - 5);
372 if (! (random() % 10))
376 tick_rocks (current_delta);
380 init_rocks (Display *d, Window w)
385 XWindowAttributes xgwa;
389 XGetWindowAttributes (dpy, window, &xgwa);
390 cmap = xgwa.colormap;
391 delay = get_integer_resource ("delay", "Integer");
392 if (delay < 0) delay = 0;
393 speed = get_integer_resource ("speed", "Integer");
394 if (speed < 1) speed = 1;
395 if (speed > 100) speed = 100;
396 rotate_p = get_boolean_resource ("rotate", "Boolean");
397 move_p = get_boolean_resource ("move", "Boolean");
401 ncolors = get_integer_resource ("colors", "Colors");
409 colors = (XColor *) malloc(ncolors * sizeof(*colors));
410 draw_gcs = (GC *) malloc(ncolors * sizeof(*draw_gcs));
412 bg = get_pixel_resource ("background", "Background", dpy, cmap);
413 colors[0].pixel = bg;
414 colors[0].flags = DoRed|DoGreen|DoBlue;
415 XQueryColor(dpy, cmap, &colors[0]);
418 make_random_colormap(dpy, xgwa.visual, cmap, colors+1, &ncolors, True,
430 unsigned int fg = get_pixel_resource("foreground", "Foreground",
432 colors[1].pixel = fg;
433 colors[1].flags = DoRed|DoGreen|DoBlue;
434 XQueryColor(dpy, cmap, &colors[1]);
437 draw_gcs[0] = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
438 draw_gcs[1] = draw_gcs[0];
441 for( i = 0; i < ncolors; i++ )
443 gcv.foreground = colors[i].pixel;
445 draw_gcs[i] = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
449 erase_gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
451 max_dep = (move_p ? MAX_DEP : 0);
453 for (i = 0; i < SIN_RESOLUTION; i++)
455 sins [i] = sin ((((double) i) / (SIN_RESOLUTION / 2)) * M_PI);
456 coss [i] = cos ((((double) i) / (SIN_RESOLUTION / 2)) * M_PI);
458 /* we actually only need i/speed of these, but wtf */
459 for (i = 1; i < (sizeof (depths) / sizeof (depths[0])); i++)
460 depths [i] = atan (((double) 0.5) / (((double) i) / DEPTH_SCALE));
461 depths [0] = M_PI/2; /* avoid division by 0 */
463 threed = get_boolean_resource("use3d", "Boolean");
467 gcv.foreground = get_pixel_resource ("left3d", "Foreground", dpy, cmap);
468 threed_left_gc = XCreateGC (dpy, window, GCForeground|GCBackground,&gcv);
469 gcv.foreground = get_pixel_resource ("right3d", "Foreground", dpy, cmap);
470 threed_right_gc = XCreateGC (dpy, window,GCForeground|GCBackground,&gcv);
471 threed_delta = get_float_resource("delta3d", "Integer");
474 /* don't want any exposure events from XCopyPlane */
475 for( i = 0; i < ncolors; i++)
476 XSetGraphicsExposures (dpy, draw_gcs[i], False);
477 XSetGraphicsExposures (dpy, erase_gc, False);
479 nrocks = get_integer_resource ("count", "Count");
480 if (nrocks < 1) nrocks = 1;
481 rocks = (struct rock *) calloc (nrocks, sizeof (struct rock));
482 init_pixmaps (dpy, window);
483 XClearWindow (dpy, window);
488 char *progclass = "Rocks";
490 char *defaults [] = {
491 "*background: Black",
492 "*foreground: #E9967A",
506 XrmOptionDescRec options [] = {
507 { "-count", ".count", XrmoptionSepArg, 0 },
508 { "-rotate", ".rotate", XrmoptionNoArg, "true" },
509 { "-norotate", ".rotate", XrmoptionNoArg, "false" },
510 { "-move", ".move", XrmoptionNoArg, "true" },
511 { "-nomove", ".move", XrmoptionNoArg, "false" },
512 { "-delay", ".delay", XrmoptionSepArg, 0 },
513 { "-speed", ".speed", XrmoptionSepArg, 0 },
514 {"-3d", ".use3d", XrmoptionNoArg, "True"},
515 {"-no-3d", ".use3d", XrmoptionNoArg, "False"},
516 {"-left3d", ".left3d", XrmoptionSepArg, 0 },
517 {"-right3d", ".right3d", XrmoptionSepArg, 0 },
518 {"-delta3d", ".delta3d", XrmoptionSepArg, 0 },
519 { "-colors", ".colors", XrmoptionSepArg, 0 },
524 screenhack (Display *dpy, Window window)
526 init_rocks (dpy, window);
531 if (delay) usleep (delay);