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