1 /* xscreensaver, Copyright (c) 1992-2013 Jamie Zawinski <jwz@jwz.org>
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 /* 18-Sep-97: Johannes Keukelaar <johannes@nada.kth.se>: Added some color.
13  * Using -mono gives the old behaviour.  (Modified by jwz.)
14  */
15 /*   Flying through an asteroid field.  Based on TI Explorer Lisp code by
16    John Nguyen <johnn@hx.lcs.mit.edu>
17  */
18 
19 #include <stdio.h>
20 #include <math.h>
21 #include "screenhack.h"
22 
23 #define MIN_ROCKS 1
24 #define MIN_DEPTH 2		/* rocks disappear when they get this close */
25 #define MAX_DEPTH 60		/* this is where rocks appear */
26 #define MIN_SIZE 3		/* how small where pixmaps are not used */
27 #define MAX_SIZE 200		/* how big (in pixels) rocks are at depth 1 */
28 #define DEPTH_SCALE 100		/* how many ticks there are between depths */
29 #define SIN_RESOLUTION 1000
30 
31 #define MAX_DEP 0.3		/* how far the displacement can be (percent) */
32 #define DIRECTION_CHANGE_RATE 60
33 #define MAX_DEP_SPEED 5		/* Maximum speed for movement */
34 #define MOVE_STYLE 0		/* Only 0 and 1. Distinguishes the fact that
35 				   these are the rocks that are moving (1)
36 				   or the rocks source (0). */
37 
38 /* there's not much point in the above being user-customizable, but those
39    numbers might want to be tweaked for displays with an order of magnitude
40    higher resolution or compute power.
41  */
42 
43 struct state {
44   Display *dpy;
45   Window window;
46 
47   double sins [SIN_RESOLUTION];
48   double coss [SIN_RESOLUTION];
49   double depths [(MAX_DEPTH + 1) * DEPTH_SCALE];
50 
51   int width, height, midx, midy;
52   int dep_x, dep_y;
53   int ncolors;
54   XColor *colors;
55   float max_dep;
56   GC erase_gc;
57   GC *draw_gcs;
58   Bool rotate_p;
59   Bool move_p;
60   int speed;
61   Bool threed;
62   GC threed_left_gc, threed_right_gc;
63   double threed_delta;
64 
65   struct rock *rocks;
66   int nrocks;
67   Pixmap pixmaps [MAX_SIZE];
68   int delay;
69 
70   int move_current_dep[2];
71   int move_speed[2];
72   short move_direction[2];
73   int move_limit[2];
74 
75   int current_delta;	/* observer Z rotation */
76   int  new_delta;
77   int  dchange_tick;
78 };
79 
80 
81 
82 #define GETZDIFF(z) \
83         (st->threed_delta * 40.0 * \
84 	 (1.0 - ((MAX_DEPTH * DEPTH_SCALE / 2) / \
85 		 ((z) + 20.0 * DEPTH_SCALE))))
86 
87 struct rock {
88   int real_size;
89   int r;
90   int theta;
91   int depth;
92   int size, x, y;
93   int diff;
94   int color;
95 };
96 
97 static void rock_compute (struct state *, struct rock *);
98 static void rock_draw (struct state *, struct rock *, Bool draw_p);
99 
100 static void
rock_reset(struct state * st,struct rock * rock)101 rock_reset (struct state *st, struct rock *rock)
102 {
103   rock->real_size = MAX_SIZE;
104   rock->r = (SIN_RESOLUTION * 0.7) + (random () % (30 * SIN_RESOLUTION));
105   rock->theta = random () % SIN_RESOLUTION;
106   rock->depth = MAX_DEPTH * DEPTH_SCALE;
107   rock->color = random() % st->ncolors;
108   rock_compute (st, rock);
109   rock_draw (st, rock, True);
110 }
111 
112 static void
rock_tick(struct state * st,struct rock * rock,int d)113 rock_tick (struct state *st, struct rock *rock, int d)
114 {
115   if (rock->depth > 0)
116     {
117       rock_draw (st, rock, False);
118       rock->depth -= st->speed;
119       if (st->rotate_p)
120         {
121 	  rock->theta = (rock->theta + d) % SIN_RESOLUTION;
122         }
123       while (rock->theta < 0)
124 	rock->theta += SIN_RESOLUTION;
125       if (rock->depth < (MIN_DEPTH * DEPTH_SCALE))
126 	rock->depth = 0;
127       else
128 	{
129 	  rock_compute (st, rock);
130 	  rock_draw (st, rock, True);
131 	}
132     }
133   else if ((random () % 40) == 0)
134     rock_reset (st, rock);
135 }
136 
137 static void
rock_compute(struct state * st,struct rock * rock)138 rock_compute (struct state *st, struct rock *rock)
139 {
140   double factor = st->depths [rock->depth];
141   double rsize = rock->real_size * factor;
142 
143   rock->size = (int) (rsize + 0.5);
144   rock->diff = (int) GETZDIFF(rock->depth);
145   rock->x = st->midx + (st->coss [rock->theta] * rock->r * factor);
146   rock->y = st->midy + (st->sins [rock->theta] * rock->r * factor);
147 
148   if (st->move_p)
149     {
150       double move_factor = (((double) MOVE_STYLE) -
151 			    (((double) rock->depth) /
152 			     (((double) (MAX_DEPTH + 1)) *
153 			      ((double) DEPTH_SCALE))));
154       /* move_factor is 0 when the rock is close, 1 when far */
155       rock->x += (((double) st->dep_x) * move_factor);
156       rock->y += (((double) st->dep_y) * move_factor);
157     }
158 }
159 
160 static void
rock_draw(struct state * st,struct rock * rock,Bool draw_p)161 rock_draw (struct state *st, struct rock *rock, Bool draw_p)
162 {
163   GC gc = (draw_p
164 	   ? (st->threed ? st->erase_gc : st->draw_gcs[rock->color])
165 	   : st->erase_gc);
166 
167   if (rock->x <= 0 || rock->y <= 0 || rock->x >= st->width || rock->y >= st->height)
168     {
169       /* this means that if a rock were to go off the screen at 12:00, but
170 	 would have been visible at 3:00, it won't come back once the observer
171 	 rotates around so that the rock would have been visible again.
172 	 Oh well.
173        */
174       if (!st->move_p)
175 	rock->depth = 0;
176       return;
177     }
178   if (rock->size <= 1)
179     {
180       if (st->threed)
181 	{
182 	  if (draw_p) gc = st->threed_left_gc;
183 	  XDrawPoint (st->dpy, st->window, gc, rock->x - rock->diff, rock->y);
184 	  if (draw_p) gc = st->threed_right_gc;
185 	  XDrawPoint (st->dpy, st->window, gc, rock->x + rock->diff, rock->y);
186 	}
187       else
188 	{
189 	  XDrawPoint (st->dpy, st->window, gc, rock->x, rock->y);
190 	}
191     }
192   else if (rock->size <= MIN_SIZE || !draw_p)
193     {
194       if (st->threed)
195 	{
196 	  if (draw_p) gc = st->threed_left_gc;
197 	  XFillRectangle(st->dpy, st->window, gc,
198 			 rock->x - rock->size / 2 - rock->diff,
199 			 rock->y - rock->size / 2,
200 			 rock->size, rock->size);
201 	  if (draw_p) gc = st->threed_right_gc;
202 	  XFillRectangle(st->dpy, st->window, gc,
203 			 rock->x - rock->size / 2 + rock->diff,
204 			 rock->y - rock->size / 2,
205 			 rock->size, rock->size);
206 	}
207       else
208 	{
209 	  XFillRectangle (st->dpy, st->window, gc,
210 			  rock->x - rock->size/2, rock->y - rock->size/2,
211 			  rock->size, rock->size);
212 	}
213     }
214   else if (rock->size < MAX_SIZE)
215     {
216       if (st->threed)
217 	{
218 	  gc = st->threed_left_gc;
219 	  XCopyPlane(st->dpy, st->pixmaps[rock->size], st->window, gc,
220 		     0, 0, rock->size, rock->size,
221 		     rock->x - rock->size / 2 - rock->diff,
222 		     rock->y - rock->size / 2, 1L);
223 	  gc = st->threed_right_gc;
224 	  XCopyPlane(st->dpy, st->pixmaps[rock->size], st->window, gc,
225 		     0, 0, rock->size, rock->size,
226 		     rock->x - rock->size / 2 + rock->diff,
227 		     rock->y - rock->size / 2, 1L);
228 	}
229       else
230 	{
231 	  XCopyPlane (st->dpy, st->pixmaps [rock->size], st->window, gc,
232 		      0, 0, rock->size, rock->size,
233 		      rock->x - rock->size/2, rock->y - rock->size/2,
234 		      1L);
235 	}
236     }
237 }
238 
239 
240 static void
init_pixmaps(struct state * st)241 init_pixmaps (struct state *st)
242 {
243   int i;
244   XGCValues gcv;
245   GC fg_gc = 0, bg_gc = 0;
246   st->pixmaps [0] = st->pixmaps [1] = 0;
247   for (i = MIN_DEPTH; i < MAX_SIZE; i++)
248     {
249       int w = (1+(i/32))<<5; /* server might be faster if word-aligned */
250       int h = i;
251       Pixmap p = XCreatePixmap (st->dpy, st->window, w, h, 1);
252       XPoint points [7];
253       st->pixmaps [i] = p;
254       if (! p)
255 	{
256 	  fprintf (stderr, "%s: couldn't allocate pixmaps", progname);
257 	  exit (1);
258 	}
259       if (! fg_gc)
260 	{	/* must use drawable of pixmap, not window (fmh) */
261 	  gcv.foreground = 1;
262 	  fg_gc = XCreateGC (st->dpy, p, GCForeground, &gcv);
263 	  gcv.foreground = 0;
264 	  bg_gc = XCreateGC (st->dpy, p, GCForeground, &gcv);
265 	}
266       XFillRectangle (st->dpy, p, bg_gc, 0, 0, w, h);
267       points [0].x = i * 0.15; points [0].y = i * 0.85;
268       points [1].x = i * 0.00; points [1].y = i * 0.20;
269       points [2].x = i * 0.30; points [2].y = i * 0.00;
270       points [3].x = i * 0.40; points [3].y = i * 0.10;
271       points [4].x = i * 0.90; points [4].y = i * 0.10;
272       points [5].x = i * 1.00; points [5].y = i * 0.55;
273       points [6].x = i * 0.45; points [6].y = i * 1.00;
274       XFillPolygon (st->dpy, p, fg_gc, points, 7, Nonconvex, CoordModeOrigin);
275     }
276   XFreeGC (st->dpy, fg_gc);
277   XFreeGC (st->dpy, bg_gc);
278 }
279 
280 
281 static int
compute_move(struct state * st,int axe)282 compute_move(struct state *st, int axe)			/* 0 for x, 1 for y */
283 {
284   int change = 0;
285 
286   st->move_limit[0] = st->midx;
287   st->move_limit[1] = st->midy;
288 
289   st->move_current_dep[axe] += st->move_speed[axe];	/* We adjust the displacement */
290 
291   if (st->move_current_dep[axe] > (int) (st->move_limit[axe] * st->max_dep))
292     {
293       if (st->move_current_dep[axe] > st->move_limit[axe])
294 	st->move_current_dep[axe] = st->move_limit[axe];
295       st->move_direction[axe] = -1;
296     }			/* This is when we reach the upper screen limit */
297   if (st->move_current_dep[axe] < (int) (-st->move_limit[axe] * st->max_dep))
298     {
299       if (st->move_current_dep[axe] < -st->move_limit[axe])
300 	st->move_current_dep[axe] = -st->move_limit[axe];
301       st->move_direction[axe] = 1;
302     }			/* This is when we reach the lower screen limit */
303   if (st->move_direction[axe] == 1)	/* We adjust the speed */
304     st->move_speed[axe] += 1;
305   else if (st->move_direction[axe] == -1)
306     st->move_speed[axe] -= 1;
307 
308   if (st->move_speed[axe] > MAX_DEP_SPEED)
309     st->move_speed[axe] = MAX_DEP_SPEED;
310   else if (st->move_speed[axe] < -MAX_DEP_SPEED)
311     st->move_speed[axe] = -MAX_DEP_SPEED;
312 
313   if (st->move_p && !(random() % DIRECTION_CHANGE_RATE))
314     {
315       /* We change direction */
316       change = random() & 1;
317       if (change != 1)
318 	{
319 	  if (st->move_direction[axe] == 0)
320 	    st->move_direction[axe] = change - 1;	/* 0 becomes either 1 or -1 */
321 	  else
322 	    st->move_direction[axe] = 0;			/* -1 or 1 become 0 */
323 	}
324     }
325   return (st->move_current_dep[axe]);
326 }
327 
328 static void
tick_rocks(struct state * st,int d)329 tick_rocks (struct state *st, int d)
330 {
331   int i;
332 
333   if (st->move_p)
334     {
335       st->dep_x = compute_move(st, 0);
336       st->dep_y = compute_move(st, 1);
337     }
338 
339   for (i = 0; i < st->nrocks; i++)
340     rock_tick (st, &st->rocks [i], d);
341 }
342 
343 
344 static unsigned long
rocks_draw(Display * dpy,Window window,void * closure)345 rocks_draw (Display *dpy, Window window, void *closure)
346 {
347   struct state *st = (struct state *) closure;
348   if (st->current_delta != st->new_delta)
349     {
350       if (st->dchange_tick++ == 5)
351 	{
352 	  st->dchange_tick = 0;
353 	  if (st->current_delta < st->new_delta)
354 	    st->current_delta++;
355 	  else
356 	    st->current_delta--;
357 	}
358     }
359   else
360     {
361       if (! (random() % 50))
362 	{
363 	  st->new_delta = ((random() % 11) - 5);
364 	  if (! (random() % 10))
365 	    st->new_delta *= 5;
366 	}
367     }
368   tick_rocks (st, st->current_delta);
369 
370   return st->delay;
371 }
372 
373 static void *
rocks_init(Display * d,Window w)374 rocks_init (Display *d, Window w)
375 {
376   struct state *st = (struct state *) calloc (1, sizeof(*st));
377   int i;
378   XGCValues gcv;
379   Colormap cmap;
380   XWindowAttributes xgwa;
381   unsigned int bg;
382   st->dpy = d;
383   st->window = w;
384   XGetWindowAttributes (st->dpy, st->window, &xgwa);
385 
386   st->width = xgwa.width;
387   st->height = xgwa.height;
388   st->midx = st->width/2;
389   st->midy = st->height/2;
390 
391   cmap = xgwa.colormap;
392   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
393   if (st->delay < 0) st->delay = 0;
394   st->speed = get_integer_resource (st->dpy, "speed", "Integer");
395   if (st->speed < 1) st->speed = 1;
396   if (st->speed > 100) st->speed = 100;
397   st->rotate_p = get_boolean_resource (st->dpy, "rotate", "Boolean");
398   st->move_p = get_boolean_resource (st->dpy, "move", "Boolean");
399   if (mono_p)
400     st->ncolors = 2;
401   else
402     st->ncolors = get_integer_resource (st->dpy, "colors", "Colors");
403 
404   if (st->ncolors < 2)
405     {
406       st->ncolors = 2;
407       mono_p = True;
408   }
409 
410   st->colors = (XColor *) malloc(st->ncolors * sizeof(*st->colors));
411   st->draw_gcs = (GC *) malloc(st->ncolors * sizeof(*st->draw_gcs));
412 
413   bg = get_pixel_resource (st->dpy, cmap, "background", "Background");
414   st->colors[0].pixel = bg;
415   st->colors[0].flags = DoRed|DoGreen|DoBlue;
416   XQueryColor(st->dpy, cmap, &st->colors[0]);
417 
418   st->ncolors--;
419   make_random_colormap(xgwa.screen, xgwa.visual, cmap,
420                        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
rocks_reshape(Display * dpy,Window window,void * closure,unsigned int w,unsigned int h)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
rocks_event(Display * dpy,Window window,void * closure,XEvent * event)500 rocks_event (Display *dpy, Window window, void *closure, XEvent *event)
501 {
502   return False;
503 }
504 
505 static void
rocks_free(Display * dpy,Window window,void * closure)506 rocks_free (Display *dpy, Window window, void *closure)
507 {
508   struct state *st = (struct state *) closure;
509   int i;
510   for (i = 0; i < MAX_SIZE; i++)
511     if (st->pixmaps[i]) XFreePixmap (dpy, st->pixmaps[i]);
512   for (i = 0; i < st->ncolors; i++)
513     if (st->draw_gcs[i]) XFreeGC (dpy, st->draw_gcs[i]);
514   XFreeGC (dpy, st->erase_gc);
515   free (st->colors);
516   free (st->rocks);
517   free (st->draw_gcs);
518   free (st);
519 }
520 
521 
522 
523 static const char *rocks_defaults [] = {
524   ".lowrez:     true",
525   ".background:	Black",
526   ".foreground:	#E9967A",
527   "*fpsSolid:	true",
528   "*colors:	5",
529   "*count:	100",
530   "*delay:	50000",
531   "*speed:	100",
532   "*rotate:     true",
533   "*move:       true",
534   "*use3d:      False",
535   "*left3d:	Blue",
536   "*right3d:	Red",
537   "*delta3d:	1.5",
538 #ifdef HAVE_MOBILE
539   "*ignoreRotation: True",
540 #endif
541   0
542 };
543 
544 static XrmOptionDescRec rocks_options [] = {
545   { "-count",		".count",	XrmoptionSepArg, 0 },
546   { "-rotate",          ".rotate",      XrmoptionNoArg,  "true" },
547   { "-no-rotate",        ".rotate",      XrmoptionNoArg,  "false" },
548   { "-move",            ".move",        XrmoptionNoArg,  "true" },
549   { "-no-move",          ".move",        XrmoptionNoArg,  "false" },
550   { "-delay",		".delay",	XrmoptionSepArg, 0 },
551   { "-speed",		".speed",	XrmoptionSepArg, 0 },
552   {"-3d",		".use3d",	XrmoptionNoArg, "True"},
553   {"-no-3d",		".use3d",	XrmoptionNoArg, "False"},
554   {"-left3d",		".left3d",	XrmoptionSepArg, 0 },
555   {"-right3d",		".right3d",	XrmoptionSepArg, 0 },
556   {"-delta3d",		".delta3d",	XrmoptionSepArg, 0 },
557   { "-colors",		".colors",	XrmoptionSepArg, 0 },
558   { 0, 0, 0, 0 }
559 };
560 
561 XSCREENSAVER_MODULE ("Rocks", rocks)
562