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