http://www.ibiblio.org/pub/historic-linux/ftp-archives/sunsite.unc.edu/Sep-29-1996...
[xscreensaver] / hacks / rocks.c
1 /* xscreensaver, Copyright (c) 1992, 1995 Jamie Zawinski <jwz@netscape.com>
2  *
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.
10  */
11
12 /* Flying through an asteroid field.  Based on TI Explorer Lisp code by 
13    John Nguyen <johnn@hx.lcs.mit.edu>
14  */
15
16 #include <stdio.h>
17 #include <math.h>
18 #include "screenhack.h"
19
20 #define MIN_DEPTH 2             /* rocks disappar when they get this close */
21 #define MAX_DEPTH 60            /* this is where rocks appear */
22 #define MAX_WIDTH 100           /* how big (in pixels) rocks are at depth 1 */
23 #define DEPTH_SCALE 100         /* how many ticks there are between depths */
24 #define SIN_RESOLUTION 1000
25
26 /* there's not much point in the above being user-customizable, but those
27    numbers might want to be tweaked for displays with an order of magnitude
28    higher resolution or compute power.
29  */
30
31 static double sins [SIN_RESOLUTION];
32 static double coss [SIN_RESOLUTION];
33 static double depths [(MAX_DEPTH + 1) * DEPTH_SCALE];
34
35 static Display *dpy;
36 static Window window;
37 static int width, height, midx, midy;
38 static GC draw_gc, erase_gc;
39 static Bool rotate_p ;
40
41 static int speed;
42
43 struct rock {
44   int real_size;
45   int r;
46   int theta;
47   int depth;
48   int size, x, y;
49 };
50
51 static struct rock *rocks;
52 static int nrocks;
53 static Pixmap pixmaps [MAX_WIDTH];
54 static int delay;
55
56 static void rock_reset(), rock_tick(), rock_compute(), rock_draw();
57 static void init_pixmaps(), init_rocks(), tick_rocks(), rocks_once();
58
59
60 static void
61 rock_reset (rock)
62      struct rock *rock;
63 {
64   rock->real_size = MAX_WIDTH;
65   rock->r = (SIN_RESOLUTION * 0.7) + (random () % (30 * SIN_RESOLUTION));
66   rock->theta = random () % SIN_RESOLUTION;
67   rock->depth = MAX_DEPTH * DEPTH_SCALE;
68   rock_compute (rock);
69   rock_draw (rock, True);
70 }
71
72 static void
73 rock_tick (rock, d)
74      struct rock *rock;
75      int d;
76 {
77   if (rock->depth > 0)
78     {
79       rock_draw (rock, False);
80       rock->depth -= speed;
81       if (rotate_p)
82         {
83           rock->theta = (rock->theta + d) % SIN_RESOLUTION;
84         }
85       while (rock->theta < 0)
86         rock->theta += SIN_RESOLUTION;
87       if (rock->depth < (MIN_DEPTH * DEPTH_SCALE))
88         rock->depth = 0;
89       else
90         {
91           rock_compute (rock);
92           rock_draw (rock, True);
93         }
94     }
95   else if ((random () % 40) == 0)
96     rock_reset (rock);
97 }
98
99 static void
100 rock_compute (rock)
101      struct rock *rock;
102 {
103   double factor = depths [rock->depth];
104   rock->size = (int) ((rock->real_size * factor) + 0.5);
105   rock->x = midx + (coss [rock->theta] * rock->r * factor);
106   rock->y = midy + (sins [rock->theta] * rock->r * factor);
107 }
108
109 static void
110 rock_draw (rock, draw_p)
111      struct rock *rock;
112      Bool draw_p;
113 {
114   GC gc = draw_p ? draw_gc : erase_gc;
115   if (rock->x <= 0 || rock->y <= 0 || rock->x >= width || rock->y >= height)
116     {
117       /* this means that if a rock were to go off the screen at 12:00, but
118          would have been visible at 3:00, it won't come back once the observer
119          rotates around so that the rock would have been visible again.
120          Oh well.
121        */
122       rock->depth = 0;
123       return;
124     }
125   if (rock->size <= 1)
126     XDrawPoint (dpy, window, gc, rock->x, rock->y);
127   else if (rock->size <= 3 || !draw_p)
128     XFillRectangle (dpy, window, gc,
129                     rock->x - rock->size/2, rock->y - rock->size/2,
130                     rock->size, rock->size);
131   else if (rock->size < MAX_WIDTH)
132     XCopyPlane (dpy, pixmaps [rock->size], window, gc,
133                 0, 0, rock->size, rock->size,
134                 rock->x - rock->size/2, rock->y - rock->size/2,
135                 1);
136   else
137     abort ();
138 }
139
140
141 static void
142 init_pixmaps (dpy, window)
143      Display *dpy;
144      Window window;
145 {
146   int i;
147   XGCValues gcv;
148   GC fg_gc = 0, bg_gc = 0;
149   pixmaps [0] = pixmaps [1] = 0;
150   for (i = MIN_DEPTH; i < MAX_WIDTH; i++)
151     {
152       int w = (1+(i/32))<<5; /* server might be faster if word-aligned */
153       int h = i;
154       Pixmap p = XCreatePixmap (dpy, window, w, h, 1);
155       XPoint points [7];
156       pixmaps [i] = p;
157       if (! p)
158         {
159           fprintf (stderr, "%s: couldn't allocate pixmaps", progname);
160           exit (1);
161         }
162       if (! fg_gc)
163         {       /* must use drawable of pixmap, not window (fmh) */
164           gcv.foreground = 1;
165           fg_gc = XCreateGC (dpy, p, GCForeground, &gcv);
166           gcv.foreground = 0;
167           bg_gc = XCreateGC (dpy, p, GCForeground, &gcv);
168         }
169       XFillRectangle (dpy, p, bg_gc, 0, 0, w, h);
170       points [0].x = i * 0.15; points [0].y = i * 0.85;
171       points [1].x = i * 0.00; points [1].y = i * 0.20;
172       points [2].x = i * 0.30; points [2].y = i * 0.00;
173       points [3].x = i * 0.40; points [3].y = i * 0.10;
174       points [4].x = i * 0.90; points [4].y = i * 0.10;
175       points [5].x = i * 1.00; points [5].y = i * 0.55;
176       points [6].x = i * 0.45; points [6].y = i * 1.00;
177       XFillPolygon (dpy, p, fg_gc, points, 7, Nonconvex, CoordModeOrigin);
178     }
179   XFreeGC (dpy, fg_gc);
180   XFreeGC (dpy, bg_gc);
181 }
182
183
184 static void
185 init_rocks (d, w)
186      Display *d;
187      Window w;
188 {
189   int i;
190   XGCValues gcv;
191   Colormap cmap;
192   XWindowAttributes xgwa;
193   unsigned int fg, bg;
194   dpy = d;
195   window = w;
196   XGetWindowAttributes (dpy, window, &xgwa);
197   cmap = xgwa.colormap;
198   delay = get_integer_resource ("delay", "Integer");
199   if (delay < 0) delay = 0;
200   speed = get_integer_resource ("speed", "Integer");
201   if (speed < 1) speed = 1;
202   if (speed > 100) speed = 100;
203   rotate_p = get_boolean_resource ("rotate", "Boolean");
204   fg = get_pixel_resource ("foreground", "Foreground", dpy, cmap);
205   bg = get_pixel_resource ("background", "Background", dpy, cmap);
206   gcv.foreground = fg;
207   gcv.background = bg;
208   draw_gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
209   gcv.foreground = bg;
210   gcv.background = fg;
211   erase_gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
212
213   for (i = 0; i < SIN_RESOLUTION; i++)
214     {
215       sins [i] = sin ((((double) i) / (SIN_RESOLUTION / 2)) * M_PI);
216       coss [i] = cos ((((double) i) / (SIN_RESOLUTION / 2)) * M_PI);
217     }
218   /* we actually only need i/speed of these, but wtf */
219   for (i = 1; i < (sizeof (depths) / sizeof (depths[0])); i++)
220     depths [i] = atan (((double) 0.5) / (((double) i) / DEPTH_SCALE));
221   depths [0] = M_PI/2; /* avoid division by 0 */
222
223   nrocks = get_integer_resource ("count", "Count");
224   if (nrocks < 1) nrocks = 1;
225   rocks = (struct rock *) calloc (nrocks, sizeof (struct rock));
226   init_pixmaps (dpy, window);
227   XClearWindow (dpy, window);
228 }
229
230
231 static void
232 tick_rocks (d)
233      int d;
234 {
235   int i;
236   for (i = 0; i < nrocks; i++)
237     rock_tick (&rocks [i], d);
238 }
239
240 static void
241 rocks_once ()
242 {
243   static int current_delta = 0; /* observer Z rotation */
244   static int window_tick = 50;
245
246   if (window_tick++ == 50)
247     {
248       XWindowAttributes xgwa;
249       XGetWindowAttributes (dpy, window, &xgwa);
250       window_tick = 0;
251       width = xgwa.width;
252       height = xgwa.height;
253       midx = width/2;
254       midy = height/2;
255     }
256
257   if ((random () % 50) == 0)
258     {
259       int d = current_delta;
260       int new_delta = ((random () % 11) - 5);
261       if ((random () % 10) == 0)
262         new_delta *= 5;
263
264       while (d == current_delta)
265         {
266           int i;
267           for (i = 0; i < 3; i++)
268             tick_rocks (d);
269           if (current_delta < new_delta) d++;
270           else d--;
271         }
272       current_delta = new_delta;
273     }
274   tick_rocks (current_delta);
275 }
276
277 \f
278 char *progclass = "Rocks";
279
280 char *defaults [] = {
281   "Rocks.background:    black",         /* to placate SGI */
282   "Rocks.foreground:    white",
283   "*count:      100",
284   "*delay:      50000",
285   "*speed:      100",
286   "*rotate:     true",
287   0
288 };
289
290 XrmOptionDescRec options [] = {
291   { "-count",           ".count",       XrmoptionSepArg, 0 },
292   { "-norotate",        ".rotate",      XrmoptionNoArg,  "false" },
293   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
294   { "-speed",           ".speed",       XrmoptionSepArg, 0 }
295 };
296 int options_size = (sizeof (options) / sizeof (options[0]));
297
298 void
299 screenhack (dpy, window)
300      Display *dpy;
301      Window window;
302 {
303   init_rocks (dpy, window);
304   while (1)
305     {
306       rocks_once ();
307       XSync (dpy, True);
308       if (delay) usleep (delay);
309     }
310 }