began for maemo
[xscreensaver] / xscreensaver / hacks / rocks.c
1 /* xscreensaver, Copyright (c) 1992, 1995, 1996, 1997, 1998, 2006
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
44 struct state {
45   Display *dpy;
46   Window window;
47
48   double sins [SIN_RESOLUTION];
49   double coss [SIN_RESOLUTION];
50   double depths [(MAX_DEPTH + 1) * DEPTH_SCALE];
51
52   int width, height, midx, midy;
53   int dep_x, dep_y;
54   int ncolors;
55   XColor *colors;
56   float max_dep;
57   GC erase_gc;
58   GC *draw_gcs;
59   Bool rotate_p;
60   Bool move_p;
61   int speed;
62   Bool threed;
63   GC threed_left_gc, threed_right_gc;
64   double threed_delta;
65
66   struct rock *rocks;
67   int nrocks;
68   Pixmap pixmaps [MAX_SIZE];
69   int delay;
70
71   int move_current_dep[2];
72   int move_speed[2];
73   short move_direction[2];
74   int move_limit[2];
75
76   int current_delta;    /* observer Z rotation */
77   int  new_delta;
78   int  dchange_tick;
79 };
80
81
82
83 #define GETZDIFF(z) \
84         (st->threed_delta * 40.0 * \
85          (1.0 - ((MAX_DEPTH * DEPTH_SCALE / 2) / \
86                  ((z) + 20.0 * DEPTH_SCALE))))
87
88 struct rock {
89   int real_size;
90   int r;
91   int theta;
92   int depth;
93   int size, x, y;
94   int diff;
95   int color;
96 };
97
98 static void rock_compute (struct state *, struct rock *);
99 static void rock_draw (struct state *, struct rock *, Bool draw_p);
100
101 static void
102 rock_reset (struct state *st, struct rock *rock)
103 {
104   rock->real_size = MAX_SIZE;
105   rock->r = (SIN_RESOLUTION * 0.7) + (random () % (30 * SIN_RESOLUTION));
106   rock->theta = random () % SIN_RESOLUTION;
107   rock->depth = MAX_DEPTH * DEPTH_SCALE;
108   rock->color = random() % st->ncolors;
109   rock_compute (st, rock);
110   rock_draw (st, rock, True);
111 }
112
113 static void
114 rock_tick (struct state *st, struct rock *rock, int d)
115 {
116   if (rock->depth > 0)
117     {
118       rock_draw (st, rock, False);
119       rock->depth -= st->speed;
120       if (st->rotate_p)
121         {
122           rock->theta = (rock->theta + d) % SIN_RESOLUTION;
123         }
124       while (rock->theta < 0)
125         rock->theta += SIN_RESOLUTION;
126       if (rock->depth < (MIN_DEPTH * DEPTH_SCALE))
127         rock->depth = 0;
128       else
129         {
130           rock_compute (st, rock);
131           rock_draw (st, rock, True);
132         }
133     }
134   else if ((random () % 40) == 0)
135     rock_reset (st, rock);
136 }
137
138 static void
139 rock_compute (struct state *st, struct rock *rock)
140 {
141   double factor = st->depths [rock->depth];
142   double rsize = rock->real_size * factor;
143
144   rock->size = (int) (rsize + 0.5);
145   rock->diff = (int) GETZDIFF(rock->depth);
146   rock->x = st->midx + (st->coss [rock->theta] * rock->r * factor);
147   rock->y = st->midy + (st->sins [rock->theta] * rock->r * factor);
148
149   if (st->move_p)
150     {
151       double move_factor = (((double) MOVE_STYLE) -
152                             (((double) rock->depth) /
153                              (((double) (MAX_DEPTH + 1)) *
154                               ((double) DEPTH_SCALE))));
155       /* move_factor is 0 when the rock is close, 1 when far */
156       rock->x += (((double) st->dep_x) * move_factor);
157       rock->y += (((double) st->dep_y) * move_factor);
158     }
159 }
160
161 static void
162 rock_draw (struct state *st, struct rock *rock, Bool draw_p)
163 {
164   GC gc = (draw_p 
165            ? (st->threed ? st->erase_gc : st->draw_gcs[rock->color])
166            : st->erase_gc);
167
168   if (rock->x <= 0 || rock->y <= 0 || rock->x >= st->width || rock->y >= st->height)
169     {
170       /* this means that if a rock were to go off the screen at 12:00, but
171          would have been visible at 3:00, it won't come back once the observer
172          rotates around so that the rock would have been visible again.
173          Oh well.
174        */
175       if (!st->move_p)
176         rock->depth = 0;
177       return;
178     }
179   if (rock->size <= 1)
180     {
181       if (st->threed)
182         {
183           if (draw_p) gc = st->threed_left_gc;
184           XDrawPoint (st->dpy, st->window, gc, rock->x - rock->diff, rock->y);
185           if (draw_p) gc = st->threed_right_gc;
186           XDrawPoint (st->dpy, st->window, gc, rock->x + rock->diff, rock->y);
187         }
188       else
189         {
190           XDrawPoint (st->dpy, st->window, gc, rock->x, rock->y);
191         }
192     }
193   else if (rock->size <= MIN_SIZE || !draw_p)
194     {
195       if (st->threed)
196         {
197           if (draw_p) gc = st->threed_left_gc;
198           XFillRectangle(st->dpy, st->window, gc,
199                          rock->x - rock->size / 2 - rock->diff,
200                          rock->y - rock->size / 2,
201                          rock->size, rock->size);
202           if (draw_p) gc = st->threed_right_gc;
203           XFillRectangle(st->dpy, st->window, gc,
204                          rock->x - rock->size / 2 + rock->diff,
205                          rock->y - rock->size / 2,
206                          rock->size, rock->size);
207         }
208       else
209         {
210           XFillRectangle (st->dpy, st->window, gc,
211                           rock->x - rock->size/2, rock->y - rock->size/2,
212                           rock->size, rock->size);
213         }
214     }
215   else if (rock->size < MAX_SIZE)
216     {
217       if (st->threed)
218         {
219           gc = st->threed_left_gc;
220           XCopyPlane(st->dpy, st->pixmaps[rock->size], st->window, gc,
221                      0, 0, rock->size, rock->size,
222                      rock->x - rock->size / 2 - rock->diff,
223                      rock->y - rock->size / 2, 1L);
224           gc = st->threed_right_gc;
225           XCopyPlane(st->dpy, st->pixmaps[rock->size], st->window, gc,
226                      0, 0, rock->size, rock->size,
227                      rock->x - rock->size / 2 + rock->diff,
228                      rock->y - rock->size / 2, 1L);
229         }
230       else
231         {
232           XCopyPlane (st->dpy, st->pixmaps [rock->size], st->window, gc,
233                       0, 0, rock->size, rock->size,
234                       rock->x - rock->size/2, rock->y - rock->size/2,
235                       1L);
236         }
237     }
238 }
239
240
241 static void
242 init_pixmaps (struct state *st)
243 {
244   int i;
245   XGCValues gcv;
246   GC fg_gc = 0, bg_gc = 0;
247   st->pixmaps [0] = st->pixmaps [1] = 0;
248   for (i = MIN_DEPTH; i < MAX_SIZE; i++)
249     {
250       int w = (1+(i/32))<<5; /* server might be faster if word-aligned */
251       int h = i;
252       Pixmap p = XCreatePixmap (st->dpy, st->window, w, h, 1);
253       XPoint points [7];
254       st->pixmaps [i] = p;
255       if (! p)
256         {
257           fprintf (stderr, "%s: couldn't allocate pixmaps", progname);
258           exit (1);
259         }
260       if (! fg_gc)
261         {       /* must use drawable of pixmap, not window (fmh) */
262           gcv.foreground = 1;
263           fg_gc = XCreateGC (st->dpy, p, GCForeground, &gcv);
264           gcv.foreground = 0;
265           bg_gc = XCreateGC (st->dpy, p, GCForeground, &gcv);
266         }
267       XFillRectangle (st->dpy, p, bg_gc, 0, 0, w, h);
268       points [0].x = i * 0.15; points [0].y = i * 0.85;
269       points [1].x = i * 0.00; points [1].y = i * 0.20;
270       points [2].x = i * 0.30; points [2].y = i * 0.00;
271       points [3].x = i * 0.40; points [3].y = i * 0.10;
272       points [4].x = i * 0.90; points [4].y = i * 0.10;
273       points [5].x = i * 1.00; points [5].y = i * 0.55;
274       points [6].x = i * 0.45; points [6].y = i * 1.00;
275       XFillPolygon (st->dpy, p, fg_gc, points, 7, Nonconvex, CoordModeOrigin);
276     }
277   XFreeGC (st->dpy, fg_gc);
278   XFreeGC (st->dpy, bg_gc);
279 }
280
281
282 static int
283 compute_move(struct state *st, int axe)                 /* 0 for x, 1 for y */
284 {
285   int change = 0;
286
287   st->move_limit[0] = st->midx;
288   st->move_limit[1] = st->midy;
289
290   st->move_current_dep[axe] += st->move_speed[axe];     /* We adjust the displacement */
291
292   if (st->move_current_dep[axe] > (int) (st->move_limit[axe] * st->max_dep))
293     {
294       if (st->move_current_dep[axe] > st->move_limit[axe])
295         st->move_current_dep[axe] = st->move_limit[axe];
296       st->move_direction[axe] = -1;
297     }                   /* This is when we reach the upper screen limit */
298   if (st->move_current_dep[axe] < (int) (-st->move_limit[axe] * st->max_dep))
299     {
300       if (st->move_current_dep[axe] < -st->move_limit[axe])
301         st->move_current_dep[axe] = -st->move_limit[axe];
302       st->move_direction[axe] = 1;
303     }                   /* This is when we reach the lower screen limit */
304   if (st->move_direction[axe] == 1)     /* We adjust the speed */
305     st->move_speed[axe] += 1;
306   else if (st->move_direction[axe] == -1)
307     st->move_speed[axe] -= 1;
308
309   if (st->move_speed[axe] > MAX_DEP_SPEED)
310     st->move_speed[axe] = MAX_DEP_SPEED;
311   else if (st->move_speed[axe] < -MAX_DEP_SPEED)
312     st->move_speed[axe] = -MAX_DEP_SPEED;
313
314   if (st->move_p && !(random() % DIRECTION_CHANGE_RATE))
315     {
316       /* We change direction */
317       change = random() & 1;
318       if (change != 1)
319         {
320           if (st->move_direction[axe] == 0)
321             st->move_direction[axe] = change - 1;       /* 0 becomes either 1 or -1 */
322           else
323             st->move_direction[axe] = 0;                        /* -1 or 1 become 0 */
324         }
325     }
326   return (st->move_current_dep[axe]);
327 }
328
329 static void
330 tick_rocks (struct state *st, int d)
331 {
332   int i;
333
334   if (st->move_p)
335     {
336       st->dep_x = compute_move(st, 0);
337       st->dep_y = compute_move(st, 1);
338     }
339
340   for (i = 0; i < st->nrocks; i++)
341     rock_tick (st, &st->rocks [i], d);
342 }
343
344
345 static unsigned long
346 rocks_draw (Display *dpy, Window window, void *closure)
347 {
348   struct state *st = (struct state *) closure;
349   if (st->current_delta != st->new_delta)
350     {
351       if (st->dchange_tick++ == 5)
352         {
353           st->dchange_tick = 0;
354           if (st->current_delta < st->new_delta)
355             st->current_delta++;
356           else
357             st->current_delta--;
358         }
359     }
360   else
361     {
362       if (! (random() % 50))
363         {
364           st->new_delta = ((random() % 11) - 5);
365           if (! (random() % 10))
366             st->new_delta *= 5;
367         }
368     }
369   tick_rocks (st, st->current_delta);
370
371   return st->delay;
372 }
373
374 static void *
375 rocks_init (Display *d, Window w)
376 {
377   struct state *st = (struct state *) calloc (1, sizeof(*st));
378   int i;
379   XGCValues gcv;
380   Colormap cmap;
381   XWindowAttributes xgwa;
382   unsigned int bg;
383   st->dpy = d;
384   st->window = w;
385   XGetWindowAttributes (st->dpy, st->window, &xgwa);
386
387   st->width = xgwa.width;
388   st->height = xgwa.height;
389   st->midx = st->width/2;
390   st->midy = st->height/2;
391
392   cmap = xgwa.colormap;
393   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
394   if (st->delay < 0) st->delay = 0;
395   st->speed = get_integer_resource (st->dpy, "speed", "Integer");
396   if (st->speed < 1) st->speed = 1;
397   if (st->speed > 100) st->speed = 100;
398   st->rotate_p = get_boolean_resource (st->dpy, "rotate", "Boolean");
399   st->move_p = get_boolean_resource (st->dpy, "move", "Boolean");
400   if (mono_p)
401     st->ncolors = 2;
402   else
403     st->ncolors = get_integer_resource (st->dpy, "colors", "Colors");
404
405   if (st->ncolors < 2)
406     {
407       st->ncolors = 2;
408       mono_p = True;
409   }
410
411   st->colors = (XColor *) malloc(st->ncolors * sizeof(*st->colors));
412   st->draw_gcs = (GC *) malloc(st->ncolors * sizeof(*st->draw_gcs));
413
414   bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
415   st->colors[0].pixel = bg;
416   st->colors[0].flags = DoRed|DoGreen|DoBlue;
417   XQueryColor(st->dpy, cmap, &st->colors[0]);
418
419   st->ncolors--;
420   make_random_colormap(st->dpy, xgwa.visual, cmap, st->colors+1, &st->ncolors, True,
421                        True, 0, True);
422   st->ncolors++;
423
424   if (st->ncolors < 2)
425     {
426       st->ncolors = 2;
427       mono_p = True;
428   }
429
430   if (mono_p)
431     {
432       unsigned int fg = get_pixel_resource(st->dpy, cmap, "foreground", "Foreground");
433       st->colors[1].pixel = fg;
434       st->colors[1].flags = DoRed|DoGreen|DoBlue;
435       XQueryColor(st->dpy, cmap, &st->colors[1]);
436       gcv.foreground = fg;
437       gcv.background = bg;
438       st->draw_gcs[0] = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv);
439       st->draw_gcs[1] = st->draw_gcs[0];
440     }
441   else
442     for( i = 0; i < st->ncolors; i++ )
443       {
444         gcv.foreground = st->colors[i].pixel;
445         gcv.background = bg;
446         st->draw_gcs[i] = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv);
447       }
448
449   gcv.foreground = bg;
450   st->erase_gc = XCreateGC (st->dpy, st->window, GCForeground|GCBackground, &gcv);
451
452   st->max_dep = (st->move_p ? MAX_DEP : 0);
453
454   for (i = 0; i < SIN_RESOLUTION; i++)
455     {
456       st->sins [i] = sin ((((double) i) / (SIN_RESOLUTION / 2)) * M_PI);
457       st->coss [i] = cos ((((double) i) / (SIN_RESOLUTION / 2)) * M_PI);
458     }
459   /* we actually only need i/speed of these, but wtf */
460   for (i = 1; i < (sizeof (st->depths) / sizeof (st->depths[0])); i++)
461     st->depths [i] = atan (((double) 0.5) / (((double) i) / DEPTH_SCALE));
462   st->depths [0] = M_PI/2; /* avoid division by 0 */
463
464   st->threed = get_boolean_resource(st->dpy, "use3d", "Boolean");
465   if (st->threed)
466     {
467       gcv.background = bg;
468       gcv.foreground = get_pixel_resource (st->dpy, cmap, "left3d", "Foreground");
469       st->threed_left_gc = XCreateGC (st->dpy, st->window, GCForeground|GCBackground,&gcv);
470       gcv.foreground = get_pixel_resource (st->dpy, cmap, "right3d", "Foreground");
471       st->threed_right_gc = XCreateGC (st->dpy, st->window,GCForeground|GCBackground,&gcv);
472       st->threed_delta = get_float_resource(st->dpy, "delta3d", "Integer");
473     }
474
475   /* don't want any exposure events from XCopyPlane */
476   for( i = 0; i < st->ncolors; i++)
477     XSetGraphicsExposures (st->dpy, st->draw_gcs[i], False);
478   XSetGraphicsExposures (st->dpy, st->erase_gc, False);
479
480   st->nrocks = get_integer_resource (st->dpy, "count", "Count");
481   if (st->nrocks < 1) st->nrocks = 1;
482   st->rocks = (struct rock *) calloc (st->nrocks, sizeof (struct rock));
483   init_pixmaps (st);
484   XClearWindow (st->dpy, st->window);
485   return st;
486 }
487
488 static void
489 rocks_reshape (Display *dpy, Window window, void *closure, 
490                  unsigned int w, unsigned int h)
491 {
492   struct state *st = (struct state *) closure;
493   st->width = w;
494   st->height = h;
495   st->midx = st->width/2;
496   st->midy = st->height/2;
497 }
498
499 static Bool
500 rocks_event (Display *dpy, Window window, void *closure, XEvent *event)
501 {
502   return False;
503 }
504
505 static void
506 rocks_free (Display *dpy, Window window, void *closure)
507 {
508 }
509
510 \f
511
512 static const char *rocks_defaults [] = {
513   ".background: Black",
514   ".foreground: #E9967A",
515   "*colors:     5",
516   "*count:      100",
517   "*delay:      50000",
518   "*speed:      100",
519   "*rotate:     true",
520   "*move:       true",
521   "*use3d:      False",
522   "*left3d:     Blue",
523   "*right3d:    Red",
524   "*delta3d:    1.5",
525   0
526 };
527
528 static XrmOptionDescRec rocks_options [] = {
529   { "-count",           ".count",       XrmoptionSepArg, 0 },
530   { "-rotate",          ".rotate",      XrmoptionNoArg,  "true" },
531   { "-no-rotate",        ".rotate",      XrmoptionNoArg,  "false" },
532   { "-move",            ".move",        XrmoptionNoArg,  "true" },
533   { "-no-move",          ".move",        XrmoptionNoArg,  "false" },
534   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
535   { "-speed",           ".speed",       XrmoptionSepArg, 0 },
536   {"-3d",               ".use3d",       XrmoptionNoArg, "True"},
537   {"-no-3d",            ".use3d",       XrmoptionNoArg, "False"},
538   {"-left3d",           ".left3d",      XrmoptionSepArg, 0 },
539   {"-right3d",          ".right3d",     XrmoptionSepArg, 0 },
540   {"-delta3d",          ".delta3d",     XrmoptionSepArg, 0 },
541   { "-colors",          ".colors",      XrmoptionSepArg, 0 },
542   { 0, 0, 0, 0 }
543 };
544
545 XSCREENSAVER_MODULE ("Rocks", rocks)