source: trunk/third/xscreensaver/hacks/rocks.c @ 12203

Revision 12203, 14.1 KB checked in by ghudson, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12202, which included commits to RCS files with non-trunk default branches.
Line 
1/* xscreensaver, Copyright (c) 1992, 1995, 1996, 1997, 1998
2 *  Jamie Zawinski <jwz@jwz.org>
3 *
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
10 * implied warranty.
11 */
12
13/* 18-Sep-97: Johannes Keukelaar <johannes@nada.kth.se>: Added some color.
14 * Using -mono gives the old behaviour.  (Modified by jwz.)
15 */
16/*   Flying through an asteroid field.  Based on TI Explorer Lisp code by
17   John Nguyen <johnn@hx.lcs.mit.edu>
18 */
19
20#include <stdio.h>
21#include <math.h>
22#include "screenhack.h"
23
24#define MIN_ROCKS 1
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
31
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). */
38
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.
42 */
43
44static double sins [SIN_RESOLUTION];
45static double coss [SIN_RESOLUTION];
46static double depths [(MAX_DEPTH + 1) * DEPTH_SCALE];
47
48static Display *dpy;
49static Window window;
50static int width, height, midx, midy;
51static int dep_x, dep_y;
52static int ncolors;
53static XColor *colors;
54static float max_dep;
55static GC erase_gc;
56static GC *draw_gcs;
57static Bool rotate_p;
58static Bool move_p;
59static int speed;
60static Bool threed;
61static GC threed_left_gc, threed_right_gc;
62static double threed_delta;
63
64#define GETZDIFF(z) \
65        (threed_delta * 40.0 * \
66         (1.0 - ((MAX_DEPTH * DEPTH_SCALE / 2) / \
67                 ((z) + 20.0 * DEPTH_SCALE))))
68
69struct rock {
70  int real_size;
71  int r;
72  int theta;
73  int depth;
74  int size, x, y;
75  int diff;
76  int color;
77};
78
79static struct rock *rocks;
80static int nrocks;
81static Pixmap pixmaps [MAX_SIZE];
82static int delay;
83
84static void rock_compute (struct rock *);
85static void rock_draw (struct rock *, Bool draw_p);
86
87static void
88rock_reset (struct rock *rock)
89{
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;
95  rock_compute (rock);
96  rock_draw (rock, True);
97}
98
99static void
100rock_tick (struct rock *rock, int d)
101{
102  if (rock->depth > 0)
103    {
104      rock_draw (rock, False);
105      rock->depth -= speed;
106      if (rotate_p)
107        {
108          rock->theta = (rock->theta + d) % SIN_RESOLUTION;
109        }
110      while (rock->theta < 0)
111        rock->theta += SIN_RESOLUTION;
112      if (rock->depth < (MIN_DEPTH * DEPTH_SCALE))
113        rock->depth = 0;
114      else
115        {
116          rock_compute (rock);
117          rock_draw (rock, True);
118        }
119    }
120  else if ((random () % 40) == 0)
121    rock_reset (rock);
122}
123
124static void
125rock_compute (struct rock *rock)
126{
127  double factor = depths [rock->depth];
128  double rsize = rock->real_size * factor;
129
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);
134
135  if (move_p)
136    {
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);
144    }
145}
146
147static void
148rock_draw (rock, draw_p)
149     struct rock *rock;
150     Bool draw_p;
151{
152  GC gc = (draw_p
153           ? (threed ? erase_gc : draw_gcs[rock->color])
154           : erase_gc);
155
156  if (rock->x <= 0 || rock->y <= 0 || rock->x >= width || rock->y >= height)
157    {
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.
161         Oh well.
162       */
163      if (!move_p)
164        rock->depth = 0;
165      return;
166    }
167  if (rock->size <= 1)
168    {
169      if (threed)
170        {
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);
175        }
176      else
177        {
178          XDrawPoint (dpy, window, gc, rock->x, rock->y);
179        }
180    }
181  else if (rock->size <= MIN_SIZE || !draw_p)
182    {
183      if (threed)
184        {
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);
195        }
196      else
197        {
198          XFillRectangle (dpy, window, gc,
199                          rock->x - rock->size/2, rock->y - rock->size/2,
200                          rock->size, rock->size);
201        }
202    }
203  else if (rock->size < MAX_SIZE)
204    {
205      if (threed)
206        {
207          gc = threed_left_gc;
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);
217        }
218      else
219        {
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,
223                      1L);
224        }
225    }
226}
227
228
229static void
230init_pixmaps (Display *dpy, Window window)
231{
232  int i;
233  XGCValues gcv;
234  GC fg_gc = 0, bg_gc = 0;
235  pixmaps [0] = pixmaps [1] = 0;
236  for (i = MIN_DEPTH; i < MAX_SIZE; i++)
237    {
238      int w = (1+(i/32))<<5; /* server might be faster if word-aligned */
239      int h = i;
240      Pixmap p = XCreatePixmap (dpy, window, w, h, 1);
241      XPoint points [7];
242      pixmaps [i] = p;
243      if (! p)
244        {
245          fprintf (stderr, "%s: couldn't allocate pixmaps", progname);
246          exit (1);
247        }
248      if (! fg_gc)
249        {       /* must use drawable of pixmap, not window (fmh) */
250          gcv.foreground = 1;
251          fg_gc = XCreateGC (dpy, p, GCForeground, &gcv);
252          gcv.foreground = 0;
253          bg_gc = XCreateGC (dpy, p, GCForeground, &gcv);
254        }
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);
264    }
265  XFreeGC (dpy, fg_gc);
266  XFreeGC (dpy, bg_gc);
267}
268
269
270static int
271compute_move(int axe)                           /* 0 for x, 1 for y */
272{
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};
277  int change = 0;
278
279  limit[0] = midx;
280  limit[1] = midy;
281
282  current_dep[axe] += speed[axe];       /* We adjust the displacement */
283
284  if (current_dep[axe] > (int) (limit[axe] * max_dep))
285    {
286      if (current_dep[axe] > limit[axe])
287        current_dep[axe] = limit[axe];
288      direction[axe] = -1;
289    }                   /* This is when we reach the upper screen limit */
290  if (current_dep[axe] < (int) (-limit[axe] * max_dep))
291    {
292      if (current_dep[axe] < -limit[axe])
293        current_dep[axe] = -limit[axe];
294      direction[axe] = 1;
295    }                   /* This is when we reach the lower screen limit */
296  if (direction[axe] == 1)      /* We adjust the speed */
297    speed[axe] += 1;
298  else if (direction[axe] == -1)
299    speed[axe] -= 1;
300
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;
305
306  if (move_p && !(random() % DIRECTION_CHANGE_RATE))
307    {
308      /* We change direction */
309      change = random() & 1;
310      if (change != 1)
311        {
312          if (direction[axe] == 0)
313            direction[axe] = change - 1;        /* 0 becomes either 1 or -1 */
314          else
315            direction[axe] = 0;                 /* -1 or 1 become 0 */
316        }
317    }
318  return (current_dep[axe]);
319}
320
321static void
322tick_rocks (int d)
323{
324  int i;
325
326  if (move_p)
327    {
328      dep_x = compute_move(0);
329      dep_y = compute_move(1);
330    }
331
332  for (i = 0; i < nrocks; i++)
333    rock_tick (&rocks [i], d);
334}
335
336
337static void
338rocks_once (void)
339{
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;
344
345  if (window_tick++ == 50)
346    {
347      XWindowAttributes xgwa;
348      XGetWindowAttributes (dpy, window, &xgwa);
349      window_tick = 0;
350      width = xgwa.width;
351      height = xgwa.height;
352      midx = width/2;
353      midy = height/2;
354    }
355
356  if (current_delta != new_delta)
357    {
358      if (dchange_tick++ == 5)
359        {
360          dchange_tick = 0;
361          if (current_delta < new_delta)
362            current_delta++;
363          else
364            current_delta--;
365        }
366    }
367  else
368    {
369      if (! (random() % 50))
370        {
371          new_delta = ((random() % 11) - 5);
372          if (! (random() % 10))
373            new_delta *= 5;
374        }
375    }
376  tick_rocks (current_delta);
377}
378
379static void
380init_rocks (Display *d, Window w)
381{
382  int i;
383  XGCValues gcv;
384  Colormap cmap;
385  XWindowAttributes xgwa;
386  unsigned int bg;
387  dpy = d;
388  window = w;
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");
398  if (mono_p)
399    ncolors = 2;
400  else
401    ncolors = get_integer_resource ("colors", "Colors");
402
403  if (ncolors < 2)
404    {
405      ncolors = 2;
406      mono_p = True;
407  }
408
409  colors = (XColor *) malloc(ncolors * sizeof(*colors));
410  draw_gcs = (GC *) malloc(ncolors * sizeof(*draw_gcs));
411
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]);
416
417  ncolors--;
418  make_random_colormap(dpy, xgwa.visual, cmap, colors+1, &ncolors, True,
419                       True, 0, True);
420  ncolors++;
421
422  if (ncolors < 2)
423    {
424      ncolors = 2;
425      mono_p = True;
426  }
427
428  if (mono_p)
429    {
430      unsigned int fg = get_pixel_resource("foreground", "Foreground",
431                                           dpy, cmap);
432      colors[1].pixel = fg;
433      colors[1].flags = DoRed|DoGreen|DoBlue;
434      XQueryColor(dpy, cmap, &colors[1]);
435      gcv.foreground = fg;
436      gcv.background = bg;
437      draw_gcs[0] = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
438      draw_gcs[1] = draw_gcs[0];
439    }
440  else
441    for( i = 0; i < ncolors; i++ )
442      {
443        gcv.foreground = colors[i].pixel;
444        gcv.background = bg;
445        draw_gcs[i] = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
446      }
447
448  gcv.foreground = bg;
449  erase_gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
450
451  max_dep = (move_p ? MAX_DEP : 0);
452
453  for (i = 0; i < SIN_RESOLUTION; i++)
454    {
455      sins [i] = sin ((((double) i) / (SIN_RESOLUTION / 2)) * M_PI);
456      coss [i] = cos ((((double) i) / (SIN_RESOLUTION / 2)) * M_PI);
457    }
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 */
462
463  threed = get_boolean_resource("use3d", "Boolean");
464  if (threed)
465    {
466      gcv.background = bg;
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");
472    }
473
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);
478
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);
484}
485
486
487
488char *progclass = "Rocks";
489
490char *defaults [] = {
491  ".background: Black",
492  ".foreground: #E9967A",
493  "*colors:     5",
494  "*count:      100",
495  "*delay:      50000",
496  "*speed:      100",
497  "*rotate:     true",
498  "*move:       true",
499  "*use3d:      False",
500  "*left3d:     Blue",
501  "*right3d:    Red",
502  "*delta3d:    1.5",
503  0
504};
505
506XrmOptionDescRec 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 },
520  { 0, 0, 0, 0 }
521};
522
523void
524screenhack (Display *dpy, Window window)
525{
526  init_rocks (dpy, window);
527  while (1)
528    {
529      rocks_once ();
530      XSync (dpy, False);
531      screenhack_handle_events (dpy);
532      if (delay) usleep (delay);
533    }
534}
Note: See TracBrowser for help on using the repository browser.